Token验证登录用户

1. 什么是token

简单来说token是用户登录后生成的凭证,此后用户发出的所有请求的请求头中都要携带此凭证,否则会被拦截器拦截仅能访问放行的部分功能。加入token提高了系统安全性,同时将token信息保存在redis解决了传统储存方式产生的session共享问题

2. 实现

2.1 拦截器部分

首先在mcvconfg中配置拦截器,注意拦截器顺序,刷新token拦截器与验证token拦截器要相邻(否则在验证token时获取id失败)

@Configuration
public class MvcConfig extends WebMvcConfigurationSupport {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
    	//刷新token(该类由spring管控,可以成功注入redisTemplate)
        registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).order(0);
        //验证是否登录
        registry.addInterceptor(new LoginCheckFilter()).excludePathPatterns(
        //放行路径
                "/user/sendCode",
                "/user/login",
                "/user",
                "/user/detail",
                "/essay/recommendEssays",
                "/essay/hotEssays",
                "/essay/find",
                "/question/recommendQuestions",
                "/question/hotQuestion",
                "/question/find",
                "/category"
        ).order(1);
    }

刷新token拦截器

@Slf4j
public class RefreshTokenInterceptor implements HandlerInterceptor {

    private StringRedisTemplate stringRedisTemplate;

    public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

		//跨域处理,cros跨域请求会发起两次请求,放行opinions方式请求容许跨域
        if (request.getMethod().equals("OPTIONS")){
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }

        //获取请求头中token
        String token = request.getHeader("Authorization");
        String key = LOGIN_TOKEN_KEY + token;
        if (StrUtil.isBlank(token)) {
            return true;
        }
        //基于token获取redis中用户
        String id = stringRedisTemplate.opsForValue().get(key);

        //判断用户是否存在
        if (StringUtils.isEmpty(id)) {
            return true;
        }
        //存在,保存用户信息到ThreadLocal(线程池中用于获取用户id)
        BaseContext.setMember(Long.valueOf(id));
        //刷新token有效期
        stringRedisTemplate.expire(key, LOGIN_TOKEN_TIME, TimeUnit.HOURS);
        //放行
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //移除用户
        BaseContext.removeUser();
    }

}

验证登陆拦截器

@Slf4j
public class LoginCheckFilter implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        if (request.getMethod().equals("OPTIONS")){
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }

        if (BaseContext.getUserId() == null){
            response.setStatus(401);
            log.info("请求被拦截!");
            return false;
        }
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        BaseContext.removeUser();
    }
}

2.2 登录生成token

 @Override
    public R<LoginDTO> login(Map member) {
        //获取邮箱地址
        String email = member.get("email").toString();
        //获取密码
        String password = member.get("password").toString();
        //获取验证码
        String code = member.get("code").toString();

        //从redis中获取验证码
        String codeInRedis = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + email);
        if (StringUtils.isEmpty(codeInRedis) || !codeInRedis.equals(code)) {
            return R.error("验证码错误!");
        }

        //判断邮箱和密码是否符合规则
        if (RegexUtils.isNotEmail(email) || RegexUtils.isNotPassword(password)) {
            return R.error("邮箱或密码格式错误");
        }

        //在数据库中查询用户是否存在
        LambdaQueryWrapper<Member> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Member::getEmail, email);
        Member one = this.getOne(queryWrapper);
        
        if (one == null) {
            return R.error("该用户未注册!");
        }
        if (!one.getPassword().equals(password)) {
            return R.error("密码错误!");
        }

        //1.随机生成token,作为登陆令牌
        String token = UUID.randomUUID().toString(true);

        String keyToken = LOGIN_TOKEN_KEY + token;
        //2.将token存入redis
        stringRedisTemplate.opsForValue().set(keyToken, one.getId().toString());
        //3.设置token刷新时间
        stringRedisTemplate.expire(keyToken, LOGIN_TOKEN_TIME, TimeUnit.HOURS);

        //用户登录成功删除redis缓存的验证码
        stringRedisTemplate.delete(LOGIN_CODE_KEY + email);

        LoginDTO loginDTO = new LoginDTO();
        loginDTO.setFlag(one.getFlag());
        loginDTO.setToken(token);

        return R.success(loginDTO);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值