Springboot整合之Shiro和JWT技术实现无感刷新9

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
注册配置 MSE Nacos/ZooKeeper,118元/月
云原生网关 MSE Higress,422元/月
简介: Springboot整合之Shiro和JWT技术实现无感刷新9

4.5 创建OAuth2Filter类

注意事项:

    因为在 OAuth2Filter 类中要读写 ThreadLocal 中的数据,所以 OAuth2Filter 类必 须要设置成多例的,否则 ThreadLocal 将无法使用。

```package com.example.emos.wx.config.shiro;

import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import org.apache.http.HttpStatus;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

@Component
@Scope("prototype")
public class OAuth2Filter extends AuthenticatingFilter {
@Autowired
private ThreadLocalToken threadLocalToken;

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

@Autowired
private JwtUtil jwtUtil;

@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) {
    // Ajax提交application/json数据的时候,会先发出Options请求
    HttpServletRequest req= (HttpServletRequest) request;
    if(req.getMethod().equals(RequestMethod.OPTIONS.name())){
        return true;
    }
    return false;
}

/**
*     该方法用于处理所有应该被Shiro处理的请求
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
    HttpServletRequest req= (HttpServletRequest) request;
    HttpServletResponse resp= (HttpServletResponse) response;
    resp.setContentType("text/html");
    resp.setCharacterEncoding("UTF-8");
    resp.setHeader("Access-Control-Allow-Credentials", "true");
    resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));

    threadLocalToken.clear();
    //获取请求token,如果token不存在,直接返回401
    String token=getRequestToken(req);
    if(StrUtil.isBlank(token)){
        resp.setStatus(HttpStatus.SC_UNAUTHORIZED);
        resp.getWriter().print("无效的令牌");
        return false;
    }
    try{
        jwtUtil.verifierToken(token);
    }catch (TokenExpiredException e){
        //客户端令牌过期,查询Redis中是否存在令牌,如果存在令牌就重新生成一个令牌给客户端
        if(redisTemplate.hasKey(token)){
            redisTemplate.delete(token);
            int userId=jwtUtil.getUserId(token);
            token=jwtUtil.createToken(userId);
            redisTemplate.opsForValue().set(token,userId+"",cacheExpire, TimeUnit.DAYS);
            //把新令牌绑定到线程
            threadLocalToken.setToken(token);
        }
        else{
            //如果Redis不存在令牌,让用户重新登录
            resp.setStatus(HttpStatus.SC_UNAUTHORIZED);
            resp.getWriter().print("令牌已过期");
            return false;
        }
    }catch (Exception e){
        resp.setStatus(HttpStatus.SC_UNAUTHORIZED);
        resp.getWriter().print("无效的令牌");
        return false;
    }
    boolean bool=executeLogin(request,response);
    return bool;
}

@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
    HttpServletRequest req= (HttpServletRequest) request;
    HttpServletResponse resp= (HttpServletResponse) response;
    resp.setContentType("text/html");
    resp.setCharacterEncoding("UTF-8");
    resp.setHeader("Access-Control-Allow-Credentials", "true");
    resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
    resp.setStatus(HttpStatus.SC_UNAUTHORIZED);
    try{
        resp.getWriter().print(e.getMessage());
    }catch (Exception exception){

    }

    return false;
}

@Override
public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
    HttpServletRequest req= (HttpServletRequest) request;
    HttpServletResponse resp= (HttpServletResponse) response;
    resp.setContentType("text/html");
    resp.setCharacterEncoding("UTF-8");
    resp.setHeader("Access-Control-Allow-Credentials", "true");
    resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
    super.doFilterInternal(request, response, chain);

}
/**
* 获取请求头里面的token
*/
private String getRequestToken(HttpServletRequest request){
    String token=request.getHeader("token");
    if(StrUtil.isBlank(token)){
        token=request.getParameter("token");
    }
    return token;
}

}
```

4.6 创建ShiroConfig类

    我们要创建的 ShiroConfig 类,是用来把 OAuth2Filter 和 OAuth2Realm 配置到Shiro框架,这 样我们辛苦搭建的Shiro+JWT才算生效。

4.7 利用AOP,把更新的令牌返回给客户端

    我们在写 OAuth2Filter 的时候,把更新后的令牌写到 ThreadLocalToken 里面的 ThreadLocal 。那么这个小节,我们要创建 AOP切面类 ,拦截所有Web方法的返回值,在返回 的 R对象 中添加更新后的令牌。

————————————————

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
24天前
|
JSON 安全 算法
|
1月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
162 2
|
3天前
|
JSON 安全 Go
Go语言中使用JWT鉴权、Token刷新完整示例,拿去直接用!
本文介绍了如何在 Go 语言中使用 Gin 框架实现 JWT 用户认证和安全保护。JWT(JSON Web Token)是一种轻量、高效的认证与授权解决方案,特别适合微服务架构。文章详细讲解了 JWT 的基本概念、结构以及如何在 Gin 中生成、解析和刷新 JWT。通过示例代码,展示了如何在实际项目中应用 JWT,确保用户身份验证和数据安全。完整代码可在 GitHub 仓库中查看。
14 1
|
24天前
|
存储 安全 Java
|
1月前
|
JSON NoSQL Java
springBoot:jwt&redis&文件操作&常见请求错误代码&参数注解 (九)
该文档涵盖JWT(JSON Web Token)的组成、依赖、工具类创建及拦截器配置,并介绍了Redis的依赖配置与文件操作相关功能,包括文件上传、下载、删除及批量删除的方法。同时,文档还列举了常见的HTTP请求错误代码及其含义,并详细解释了@RequestParam与@PathVariable等参数注解的区别与用法。
|
14天前
|
JavaScript NoSQL Java
CC-ADMIN后台简介一个基于 Spring Boot 2.1.3 、SpringBootMybatis plus、JWT、Shiro、Redis、Vue quasar 的前后端分离的后台管理系统
CC-ADMIN后台简介一个基于 Spring Boot 2.1.3 、SpringBootMybatis plus、JWT、Shiro、Redis、Vue quasar 的前后端分离的后台管理系统
29 0
|
1月前
|
机器学习/深度学习 移动开发 自然语言处理
基于人工智能技术的智能导诊系统源码,SpringBoot作为后端服务的框架,提供快速开发,自动配置和生产级特性
当身体不适却不知该挂哪个科室时,智能导诊系统应运而生。患者只需选择不适部位和症状,系统即可迅速推荐正确科室,避免排错队浪费时间。该系统基于SpringBoot、Redis、MyBatis Plus等技术架构,支持多渠道接入,具备自然语言理解和多输入方式,确保高效精准的导诊体验。无论是线上医疗平台还是大型医院,智能导诊系统均能有效优化就诊流程。
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
143 1
|
17天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
95 62
|
15天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
34 2