1.原理:

        用户登录时,数据库验证账号密码是否正确,若正确则生成token,将token放入redis中存储,并将token传给前端。拦截器拦截每个请求,在请求进入接口前验证head中的token是否已经在redis中,若存在则说明用户已经登录,可以进入其他页面;若不存在说明用户未登录成功,需要去登陆页面重新登录。

2.实现

Step1:

导入redis依赖

        <dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-redis</artifactId>
			<version>${spring-boot.version}</version>
		</dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
			<version>3.3.0</version>
        </dependency>

        <dependency>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
			<version>5.3.3.RELEASE</version>
        </dependency>

Step2:

在application.yml中进行相关配置(注意缩进)

spring:
  redis:
    host: 192.168.xxx.xxx
    port: 6379
    password:
    jedis:
      pool:
        max-active: 8
   database: 0

Step3:

UserController(其实具体逻辑应该放到Service里面,但是我懒QAQ)

/**
     * 用户登录
     * @param request
     * @return
     */
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public Result login(@Valid @RequestBody LoginRequest request){
        Result result = new Result();
        try {
            //密码加密
            request.setPassword(MD5Util.MD5(request.getPassword()));
            //查询用户是否存在
            User user = userService.queryByPhoneAndPasswordAndType(request);
            if(user == null){
                return new Result(false, MessageEnum.Login_User_FAIL);
            }
            //登录成功,将生成的token存入redis中
            String token = UUID.randomUUID().toString().replaceAll("-", "") + "";
            //System.out.println(user);
            ValueOperations valueOperations = redisTemplate.opsForValue();
            valueOperations.set(token, "default", 30, TimeUnit.MINUTES);
            result.setFlag(true);
            result.setMessage(MessageEnum.Login_User_SUCCESS);
            result.setData(token);
        }catch (Exception e){
            logger.error("用户登录发生异常,param:{}, exception:{}", JSON.toJSONString(request),e);
            return new Result(false, MessageEnum.Login_User_Exception);
        }
        logger.info("用户登录成功, user register param:{}", JSON.toJSONString(request));
        return result;
    }

Step4:

拦截器拦截请求:利用拦截器将所有访问资源的请求进行拦截,校验head中的token是否合法,不合法直接返回登陆界面,合法则可以正常访问资源。

编写拦截器:

@Component
public class LoginInterceptor implements HandlerInterceptor{
    @Autowired
    private ObjectMapper objectMapper;

//    @Autowired
//    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 从请求头里面获取token,因为每次都会在请求头里面携带token
        System.out.println(request.getRequestURI());
        String token = request.getHeader("TOKEN");
        if (StringUtils.isNotBlank(token)) {
            //根据head中的token,查询redis中是否有数据
            ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
            String tokenType = opsForValue.get(token);
            // 如果能够获取到数据,说明token未过期
            if (StringUtils.isNotBlank(tokenType)) {
                    return true;
            }
        }
        //从请求头中获取不到token说明未登录,阻止该请求访问资源
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json;charset=utf-8");
        try {
            PrintWriter pw = response.getWriter();
            pw.println(objectMapper.writeValueAsString(new Result(false, MessageEnum.Login_User_FAIL)));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
}

配置拦截器:
 


@Configuration
public class LoginConfig implements WebMvcConfigurer {

    @Resource
    private LoginInterceptor loginInterceptor;

    //不拦截的请求
    String[] excludePatterns = {
            "/user/login"};

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        System.out.println("got there");
        // 添加拦截器
        registry.addInterceptor(loginInterceptor)
                // 要拦截的请求(所有请求)
                .addPathPatterns("/**")
                // 排除的拦截请求
                .excludePathPatterns(excludePatterns);
    }
}

3.一些需要注意的坑

(1)在使用RedisTemplate 对value进行set、get时,RedisTemplate 和StringRedisTemplate 不能混用,使用时必须统一。具体原因详见:关于RedisTemplate和StringRedisTemplate不能共用的问题剖析

(2)需要对 redisTemplate 进行序列化处理,才能在redis中get到key对应的value。详见:redis 中我存入了数据,为什么获取不到

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐