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

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 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
相关文章
|
18天前
|
JSON 安全 Java
使用Spring Boot和JWT实现用户认证
使用Spring Boot和JWT实现用户认证
|
25天前
|
存储 算法 安全
|
26天前
|
安全 Java Apache
如何安装与使用Spring Boot 2.2.x、Spring Framework 5.2.x与Apache Shiro 1.7进行高效开发
【7月更文第1天】在现代Java Web开发领域,Spring Boot以其简化配置、快速开发的特点备受青睐。结合Spring Framework的成熟与Apache Shiro的强大权限控制能力,我们可以轻松构建安全且高效的Web应用。本篇文章将指导你如何安装并使用Spring Boot 2.2.x、Spring Framework 5.2.x以及Apache Shiro 1.7来构建一个具备基础权限管理功能的项目。
48 0
|
29天前
|
JSON 安全 Java
Spring Boot中使用JWT进行安全认证
Spring Boot中使用JWT进行安全认证
|
1月前
|
JavaScript 应用服务中间件 nginx
nginx配置解决vue刷新404、swagger 页面访问(springboot+vue项目)
nginx配置解决vue刷新404、swagger 页面访问(springboot+vue项目)
31 0
|
1月前
|
NoSQL 安全 Linux
springboot+shiro+redis前后端分离实现认证(一)
springboot+shiro+redis前后端分离实现认证(一)
28 0
|
1月前
|
JavaScript 前端开发 NoSQL
杨校老师项目之基于SpringBoot+Shiro+Vue的企业人事管理系统
杨校老师项目之基于SpringBoot+Shiro+Vue的企业人事管理系统
30 0
|
1月前
|
安全 Java 数据安全/隐私保护
上手spring boot项目(二)之spring boot整合shiro安全框架
上手spring boot项目(二)之spring boot整合shiro安全框架
|
1月前
|
开发框架 安全 Java
信息打点-语言框架&开发组件&FastJson&Shiro&Log4j&SpringBoot等
信息打点-语言框架&开发组件&FastJson&Shiro&Log4j&SpringBoot等
|
2月前
|
安全 数据安全/隐私保护
Springboot+Spring security +jwt认证+动态授权
Springboot+Spring security +jwt认证+动态授权
148 0

热门文章

最新文章