token自刷新功能实现记录

登录控制 : Token自动刷新功能,达到续期目的

spring boot 项目引入JWT 校验 : 基于JWT 使用Spring boot项目权限校验
前言:
  • 为达成公司内部需求 : APP 登录. PC端登录 可以保留用户登录状态, 用户持续使用中,应在用户无感知的情况下自动续期token.
设计流程实现:

流程1

摘要说明:
token时长例如 = 30min
refreshToken时长务必大于token时长,这里我取值  =  60min

这里当token失效时: 后台会在返回数据时给headers中放入过期提醒状态.

前端需要使用响应前置拦截,并判断headers中的参数是否需要更新token,若需要更新token则需要用refreshToken换取新的token和新的refreshToken.

在这里插入图片描述

最终达成目标流程:

在这里插入图片描述

代码实现:
  • 这里使用 spring boot 2.1.13 版本.

  • 省略前端登录,存储token,根据后端状态刷新token功能, 只实现后台拦截器.

/**
 * @author zly
 * Spring MVC 请求拦截配置
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    /**
     * 认证组件注册到IOC
     *
     * @return AuthenticationInterceptor
     */
    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor();
    }

    /**
     * 过滤器放行文档文件
     *
     * @param registry 资源处理器注册对象
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
       /* 静态资源文件放行处理 */
    }

    /**
     * 统一异常处理
     *
     * @param exceptionResolvers 异常信息
     */
    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
       /*统一异常处理 */
       /* 在这里处理当token失效所抛出的异常 , 修改其响应的httpStatus */ 
    }

    /**
     * 跨域放行
     *
     * @param registry CorsRegistry对象
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                //是否发送Cookie信息
                .allowCredentials(true)
                //放行哪些原始域(请求方式)
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                //放行哪些原始域(头部信息)
                .allowedHeaders("*");
    }


    /**
     * 添加拦截器
     *
     * @param registry 拦截器注册对象
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor())
                //所有访问路径都要进行判断
                .addPathPatterns("/**");
    }

    /**
     * 返回响应结果
     *
     * @param response 封装响应对象
     * @param result   返回结果
     */
    private void responseResult(HttpServletResponse response, Result result, HttpStatus status) {
        /* 统一响应的封装 */
    }


}
/**
 * 拦截器规则实例
 *
 * @Author: zly
 * @Date: 2020/3/27 15:23
 */
public class AuthenticationInterceptor implements HandlerInterceptor {

    @Resource
    private UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {
        // 如果不是映射到方法直接通过,防止静态资源被拦截
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        //检查是否有CheckLogin注释,有则跳过认证    ** 自定义注解
        if (method.isAnnotationPresent(CheckLogin.class)) {
            CheckLogin checkLogin = method.getAnnotation(CheckLogin.class);
            if (checkLogin.required()) {
                return true;
            }
        }
        //检查有没有需要用户权限的注解    ** 自定义注解
        if (method.isAnnotationPresent(CheckToken.class)) {
            // 从 http 请求头中取出 token
            String token = httpServletRequest.getHeader("token");
            CheckToken checkToken = method.getAnnotation(CheckToken.class);
            if (checkToken.required()) {
                // 执行认证
                if (StringUtils.isBlank(token)) {
                    throw new AuthException("签名校验失败,请重新登录");
                }
                User user = JwtUtil.decode(token, User.class);
                if (null == user) {
                    //取出刷新token
                    String refreshToken = httpServletRequest.getHeader("refreshToken");
                    if (StringUtils.isBlank(refreshToken)) {
                        throw new AuthException("签名已过期,请重新登录");
                    }
                    User refreshUser = JwtUtil.decode(refreshToken, User.class);
                    if (null == refreshUser) {
                        throw new AuthException("签名已过期,请重新登录");
                    }
                    /* 如果是获取刷新token的URI则跳过 */
                    if (!httpServletRequest.getRequestURI().contains(REFRESH_TOKEN)) {
                        httpServletResponse.setHeader("status", AUTHORIZATION_EXPIRES);
                    }
                    user = refreshUser;
                }
                User currUser = userService.findById(user.getId());
                if (currUser == null) {
                    throw new AuthException("不合法的签名,请重新登录");
                }
                if (!user.getPassword().equals(currUser.getPassword())) {
                    throw new AuthException("密码已修改,请重新登录!");
                }
                //  权限判定
                RequiredPermission requiredPermission = handlerMethod.getMethod().getAnnotation(RequiredPermission.class);
                // 如果方法上的注解为空 则获取类的注解
                if (requiredPermission == null) {
                    requiredPermission = handlerMethod.getMethod().getDeclaringClass().getAnnotation(RequiredPermission.class);
                }
                // 如果标记了注解,则判断权限  ** 自定义权限注解
                if (requiredPermission != null && StringUtils.isNotBlank(requiredPermission.value())) {
                    // redis或数据库 中获取该用户的权限信息 并判断是否有权限
                    Set<String> permissionSet = userService.getPermissionSet(user.getId());
                    if (CollectionUtils.isEmpty(permissionSet) || !permissionSet.contains(requiredPermission.value())) {
                        throw new ServiceException("权限不足!");
                    }
                }
            }
        }
        return true;
    }
}
总结
掌握spring boot => 实现WebMvcConfigurer的接口来达成路径的拦截与对跨域的处理, 包括对在请求拦截中添加的操作: 一般鉴定权限的方法可以嵌入其中.
掌握spring boot => 统一异常处理, 返回数据的结构统一化, 减少业务代码中重复出现的try-catch,造成的代码冗余.
掌握对于使用JWT TOKEN的进行登录认证基础使用.

以上就是token自刷新的核心推导思路和核心实现代码. 若有不足,烦请指出…

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值