当前位置: 首页 > news >正文

授权与认证之jwt(四)创建OAuth2 Filter类

 接上篇讲到授权与认证之jwt(三)刷新令牌该如何设计 这篇文章中要注意

注意事项:
因为在OAuth2 Filter类中要读写
ThreadLocal中的数据,所以OAuth:2 Filter类
必须要设置成多例的,否则ThreadLocal将无法使用。

一、在配置文件中,添加)WT需要用到的密钥、过期时间和缓存过期时间。

emos:
  jwt:
    #密钥
    secret: abc123456
    #令牌过期时间(天)
    expire: 5
    #令牌缓存时间(天)
    cache-expire: 10

二、创建OAuth2Filter类

@Component
@Scope("prototype")
public class OAuth2Filter extends AuthenticatingFilter {

    @Autowired
    private ThreadLocalToken threadLocalToken;

    @Value("${emos.jwt.cache-expire}")
    private int cacheExpire;

    @Autowired
    private JwtUtil jwtUtil;

    //用于往redis中存取数据
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 拦截请求后,用于把令牌字符串封装成令牌对象
     */
    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest req=(HttpServletRequest)request;
        String token = getRequestToken(req);
        if (StrUtil.isBlank(token)) {
            return null;
        }
        return new OAuth2Token(token);
    }

    /**
     *判断哪种请求可以被shiro处理
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue){
        HttpServletRequest req = (HttpServletRequest)request;
        //ajax提交application/json数据的时候,会先发出Option请求
        //这里要放行Option请求,不需要shiro处理
        if (req.getMethod().equals(RequestMethod.OPTIONS.name())) {
            return true;
        }
        //除了Options请求之外,所以请求都要被shiro处理
        return false;
    }

    /**
     * 该方法用于处理所有应该被shiro处理的请求
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletRequest req = (HttpServletRequest)servletRequest;
        HttpServletResponse resp = (HttpServletResponse)servletResponse;

        //允许跨域请求
        resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
        resp.setHeader("Access-Control-Allow-Credentials", "true");
        resp.setContentType("text/html;charset=utf-8");

        threadLocalToken.clear();
        String token = getRequestToken(req);
        if (StrUtil.isBlank(token)) {
            resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            resp.getWriter().print("无效令牌");
            return false;
        }
        //验证token是否内容有效,验证是否过期
        try {
            jwtUtil.verifyToken(token);
        }catch (TokenExpiredException e){
            //查redis有没有该令牌
            if (redisTemplate.hasKey(token)) {
                //有数据,需要删掉缓存的老令牌,重新生成新令牌
                redisTemplate.delete(token);
                int userId=jwtUtil.getUserId(token);
                //创建新token
                token=jwtUtil.createToken(userId);
                //把token存到redis中
                redisTemplate.opsForValue().set(token,userId+"",cacheExpire, TimeUnit.DAYS);
                //把token存到threadLocal中
                threadLocalToken.setToken(token);
            }else {
                //客户端和redis中令牌都过期,需要用户重新登录
                resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                resp.getWriter().print("令牌已过期");
                return false;
            }
        }catch (JWTDecodeException e){
            resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            resp.getWriter().print("无效令牌");
            return false;
        }

        //此时令牌没问题
        boolean b = executeLogin(req, resp);
        return b;
    }

    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        HttpServletRequest req = (HttpServletRequest)servletRequest;
        HttpServletResponse resp = (HttpServletResponse)servletResponse;

        //允许跨域请求
        resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
        resp.setHeader("Access-Control-Allow-Credentials", "true");
        resp.setContentType("text/html;charset=utf-8");

        resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        try {
            resp.getWriter().print(e.getMessage());
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
        return false;
    }

    @Override
    public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        super.doFilterInternal(request, response, chain);
    }

    /**
     * 获取token方法
     */
    private String getRequestToken(HttpServletRequest req){
        String token = req.getHeader("token");

        if (StrUtil.isBlank(token)) {
            //从请求体中获取数据
            token=req.getParameter("token");
        }
        return token;
    }
}

相关文章:

  • 3471. 找出最大的几近缺失整数
  • 基于图神经网络的会话推荐经典论文
  • 十四届蓝桥杯JAVA-b组-合并石子
  • _ 为什么在python中可以当变量名
  • Redis面试常见问题——使用场景问题
  • int new_pos = (pos + delta + 9) % 9 化曲为直算法
  • 蓝桥杯自我复习打卡
  • TDengine 中对表的管理操作
  • 配置Nginx日志url encode问题
  • 本地部署大语言模型-DeepSeek
  • Java基础关键_016_System 类
  • RabbitMQ面试题及原理
  • AI理解物理世界的新突破:V-JEPA带来直观物理的觉醒!
  • java后端开发day24--阶段项目(一)
  • 算法-二叉树篇26-将有序数组转换为二叉搜索树
  • 基于兆芯ZX-C4500全国产电力通讯管理机解决方案,电力四级
  • pandas 数据的拼接
  • SpringBoot @Value 注解使用
  • 如何使用ArcGIS Pro制作横向图例:详细步骤与实践指南
  • 【vue-echarts】——03.配置项---tooltip
  • 国家统计局:下阶段要继续发挥宏观政策作用,促进价格合理回升
  • 中国证监会副主席李明:目前A股估值水平仍处于相对低位
  • 广药集团原董事长李楚源被“双开”:去年8月被查,曾多次发表争议言论
  • 既是工具又是食物,可食用机器人开启舌尖上的新科技
  • 上海静安将发放七轮文旅消费券,住宿券最高满800元减250元
  • 市场监管总局等五部门约谈外卖平台企业