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

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 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
相关文章
|
1月前
|
存储 安全 前端开发
基于springboot技术的美食烹饪互动平台的设计与实现
基于springboot技术的美食烹饪互动平台的设计与实现
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
springboot基于人工智能和自然语言理解技术的医院智能导医系统源码
智能导诊系统可为患者提供线上挂号智能辅助服务,患者根据提示手动输入自己的基本症状,通过智能对话方式,该系统会依据大数据一步步帮助患者“诊断”,并最终推荐就医的科室和相关专家。患者可自主选择,实现“一键挂号”。这一模式将精确的导诊服务前置,从源头上让医疗服务更高效。
372 2
|
2月前
|
缓存 安全 Java
Shiro框架以及Spring Boot整合Shiro
Shiro框架以及Spring Boot整合Shiro
Shiro框架以及Spring Boot整合Shiro
|
20天前
|
安全 数据安全/隐私保护
Springboot+Spring security +jwt认证+动态授权
Springboot+Spring security +jwt认证+动态授权
|
10天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
11天前
|
存储 数据可视化 安全
Java全套智慧校园系统源码springboot+elmentui +Quartz可视化校园管理平台系统源码 建设智慧校园的5大关键技术
智慧校园指的是以物联网为基础的智慧化的校园工作、学习和生活一体化环境,这个一体化环境以各种应用服务系统为载体,将教学、科研、管理和校园生活进行充分融合。无处不在的网络学习、融合创新的网络科研、透明高效的校务治理、丰富多彩的校园文化、方便周到的校园生活。简而言之,“要做一个安全、稳定、环保、节能的校园。
37 6
|
1月前
|
Kubernetes Cloud Native Devops
云原生技术落地实现之二KubeSphere DevOps 系统在 Kubernetes 集群上实现springboot项目的自动部署和管理 CI/CD (2/2)
云原生技术落地实现之二KubeSphere DevOps 系统在 Kubernetes 集群上实现springboot项目的自动部署和管理 CI/CD (2/2)
52 1
|
1月前
|
前端开发 Java Spring
SpringBoot通过拦截器和JWT令牌实现登录验证
该文介绍了JWT工具类、匿名访问注解、JWT验证拦截器的实现以及拦截器注册。使用`java-jwt`库生成和验证JWT,JwtUtil类包含generateToken和verifyToken方法。自定义注解`@AllowAnon`允许接口匿名访问。JwtInterceptor在Spring MVC中拦截请求,检查JWT令牌有效性。InterceptorConfig配置拦截器,注册并设定拦截与排除规则。UserController示例展示了注册、登录(允许匿名)和需要验证的用户详情接口。
175 1
|
1月前
|
存储 JSON Java
spring boot3登录开发-1(整合jwt)
spring boot3登录开发-1(整合jwt)
59 1
|
1月前
|
JavaScript 前端开发 Java
基于SpringBoot和VUE技术的智慧生活商城系统设计与实现
基于SpringBoot和VUE技术的智慧生活商城系统设计与实现