原理:首先调用登录,会传递一个token给前端,之后的接口都需要用到这个token,需要前端将token放入请求头里面,在调接口的时候传过来,后端获取到了这个token后,将获取的token与数据库中的比对,相同则说明已登录,可以调用接口。

实现解析:

1、首先需要登录,需要生成一个token,并且token要存入数据库,token怎么存?

考虑到这个token基本不会变并且使用频繁,因此存储到redis中是一个不错的选择,后面请求头中token都与redis中的进行比对,想要在redis中找到这个token,因此需要不同的key来标识不同的token,例如token1:"12345",token2:"23456",因此需要定义一个容易找的标识,我使用的是cache_具体的token值这种格式。

2、第二个问题,在每个接口之前都写上一个获取token的方法判断吗?

这种方式肯定是不可取的,接口如果多起来了,会很麻烦,因此我用到了spring的AOP面向切面编程,就是在controller中的每个接口之前建了一个切面,在这个切面统一执行一个方法,去判断token,这样可以统一的管理,不需要去重复写代码判断,在这里我们不要忘记去过滤登录的接口,那个接口不需要token。

3、如果用户长时间未操作,为了安全考虑,token需要自动销毁,如何做呢,定时任务吗?

类似与定时任务,redis可以给里面的数据设置一个存活时间,到了时间会自动删除,但是为了用户的体验,因此需要在每次调接口的时候,刷新一遍存活时间,这样可以优化用户的体验。

主要实现代码如下:

//建立切点
@Pointcut("execution(* com.example.blog.controller.*.*(..))")
    public void controllerMethod(){}

    @Before("controllerMethod()")
    public void LogRequestInfo(JoinPoint joinPoint){
        RequestAttributes requestAttribute = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes)requestAttribute).getRequest();
        // 打印请求内容
        log.info("----------------------------------------------------------接口调用发起----------------------------------------------------------");
        log.info("接收到请求,请求方式={},请求地址={},请求IP={},请求方法名称={},请求参数={}",request.getMethod(),request.getRequestURL().toString(),
                ToolsUtil.getServerIp(),joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
        //过滤登录接口
        if(!joinPoint.getSignature().getName().equals("userLogin")){
            //接口之前,权限校验
            String key = "cache_token_"+ RequestUtil.getUserId();
            if(ParamUtil.empty(redisUtil.get(key))){
                log.info("接口调用失败:返回结果=" + ResponseCode.NoAuthor.toString());
                log.info("----------------------------------------------------------接口调用结束----------------------------------------------------------");
                throw new CommonException(ResponseCode.NoAuthor.getMessage(),ResponseCode.NoAuthor.getMessage_en(),ResponseCode.NoAuthor.getErrorCode());
            }
            if(!Objects.equals(RequestUtil.getToken(), redisUtil.get(key))){
                log.info("接口调用失败:返回结果=" + ResponseCode.NOPOWER.toString());
                log.info("----------------------------------------------------------接口调用结束----------------------------------------------------------");
                throw new CommonException(ResponseCode.NOPOWER.getMessage(),ResponseCode.NOPOWER.getMessage_en(),ResponseCode.NOPOWER.getErrorCode());
            }
            //刷新token存活时间
            redisUtil.expire(key,4*60*60);
        }
    }

Logo

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

更多推荐