springboot在filter中如何用threadlocal存放用户身份信息

 更新时间:2024年07月11日 11:32:03   作者:march of Time  
这篇文章主要介绍了springboot中在filter中如何用threadlocal存放用户身份信息,本文章主要描述通过springboot的filter类,在过滤器中设置jwt信息进行身份信息保存的方法,需要的朋友可以参考下

本文章主要描述通过springboot的filter类,在过滤器中设置jwt信息进行身份信息保存的方法
流程:请求->过滤器->解析请求的body信息->放入threadlocal中

定义filter:一个使用 Servlet 规范的过滤器(Filter),它通过 @WebFilter 注解注册为拦截所有匹配 /api 路径的 HTTP 请求。

@WebFilter(“/api”) 注解指定了过滤器将应用于所有访问 /api路径的请求。
@Component 注解:

@Component 是 Spring 框架的注解,表明 JwtFilter 是一个 Spring 组件,可以被 Spring 容器管理,并支持依赖注入。
doFilter方法:
doFilter 方法定义了过滤器如何拦截和处理进入 Servlet 或 Servlet 容器的请求和响应。
方法签名:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException;

这个方法接受三个参数:ServletRequest request、ServletResponse response 和 FilterChain chain。
它可能抛出 IOException 或 ServletException。
请求和响应:

doFilter 方法的前两个参数代表当前的请求和响应对象,你可以在这个方法中读取请求数据、修改请求和响应。
通常,在 doFilter 方法的最后,你需要调用 chain.doFilter(request, response) 来继续执行过滤器链中的下一个过滤器或目标资源。

如果要重新修改请求内容,可以用HttpServletRequestWrapper,HttpServletRequestWrapper 是一个包装器类,它扩展了 HttpServletRequest 接口,允许你修改或扩展请求的处理。使用 HttpServletRequestUriWrapper(这可能是一个自定义的包装器类,继承自 HttpServletRequestWrapper)的目的通常包括:

修改请求 URI:

你可能想要修改请求的 URI,但不想改变原始的 HttpServletRequest 对象。通过使用 HttpServletRequestUriWrapper,你可以包装原始请求并提供一个修改后的 URI。
保持原始请求不变:

使用包装器可以保持原始请求对象不变,同时允许你在过滤链中的某个点修改请求的某些方面。
过滤和预处理:

在调用 filterChain.doFilter 之前,你可以在 doFilter 方法中添加任何预处理逻辑,例如修改请求参数、更改请求路径、添加或修改请求头等。

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
@WebFilter("/api")
@Component
@Slf4j
public class JwtFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) {
        // noting to do
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        var httpRequest = (HttpServletRequest) servletRequest;
        var requestBodyPayload = StreamUtils.copyToString(servletRequest.getInputStream(), StandardCharsets.UTF_8);
        // 解析Body参数,并存入threadLocal管理
        var jwtInfo = JwtUtil.getJwtInfoFromReq(requestBodyPayload);
        JwtUtil.setJwtInfo(jwtInfo);
        // 读取过body,需要重新设置body
        var wrapper = new HttpServletRequestUriWrapper(httpRequest, httpRequest.getRequestURI(), requestBodyPayload);
        // 将请求传递到下一个过滤器(或者最终到达控制器方法)
        filterChain.doFilter(wrapper, servletResponse);
    }
    @Override
    public void destroy() {
        JwtUtil.removeJwtInfo();
        MDC.clear();
    }
}

jwt信息:

@Slf4j
@Component
public class JwtUtil {
    /** 线程jwt信息维护 */
    private static final ThreadLocal<JwtInfo> REQUEST_BASE_INFO_THREAD_LOCAL = new ThreadLocal<>();
    /** 解析jwt信息 */
    public static JwtInfo getJwtInfoFromReq(String requestBodyPayload) {
        var jwtInfo = new JwtInfo();
        try {
            var requestBody = JsonUtil.getJsonNode(requestBodyPayload);
            log.info("[JwtUtil] RequestBody -> {}", requestBody);
            // 解析requestBody,转为jwtInfo对象
            jwtInfo.setRequestId(requestBody.get("RequestId") != null ? requestBody.get("RequestId").asText() : "");
            jwtInfo.setRegion(requestBody.get("Region") != null ? requestBody.get("Region").asText() : "");
            log.info("[JwtUtil] JwtInfo -> {}", jwtInfo);
        } catch (Exception e) {
            log.error("[JwtUtil] Parse RequestBodyInfo Error, Error Message -> {}", e.getMessage(), e);
        }
        return jwtInfo;
    }
    /** 获取jwt信息 */
    public static JwtInfo getJwtInfo() {
        var jwtInfo = REQUEST_BASE_INFO_THREAD_LOCAL.get();
        if (Objects.isNull(jwtInfo)) {
            final var requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (Objects.nonNull(requestAttributes)) {
                var requestBodyPayload = "";
                try {
                    requestBodyPayload = StreamUtils.copyToString(requestAttributes.getRequest().getInputStream(),
                            StandardCharsets.UTF_8);
                } catch (Exception e) {
                    log.error("[JwtUtil] Parse RequestBodyInfo Error, Error Message -> {}", e.getMessage());
                }
                jwtInfo = getJwtInfoFromReq(requestBodyPayload);
                setJwtInfo(jwtInfo);
            }
        }
        return jwtInfo;
    }
    /** 将jwt信息存入threadLocal中 */
    public static void setJwtInfo(JwtInfo jwtInfo) {
        REQUEST_BASE_INFO_THREAD_LOCAL.set(jwtInfo);
        // 将traceId写入日志变量
        MDC.put("traceId", jwtInfo.getRequestId());
    }
    public static void setJwtInfo(String appId, String ownerUin) {
        var jwtInfo = new JwtUtil.JwtInfo();
        jwtInfo.setRequestId(UUID.randomUUID().toString());
        setJwtInfo(jwtInfo);
    }
    /** 从threadLocal中删除jwt信息 */
    public static void removeJwtInfo() {
        REQUEST_BASE_INFO_THREAD_LOCAL.remove();
    }
    @Data
    public static class JwtInfo {
        @JsonPropertyDescription("请求requestId")
        private String requestId;
        @JsonPropertyDescription("请求的Region")
        private String region;
    }
}

获得jwt中的内容,去发送其他http请求:

   public static JsonNode sendHttpRequest(String method, String action, String url, Map<String, Object> body)
            throws IOException, InterruptedException {
        // 设置通用参数
        var jwtInfo = JwtUtil.getJwtInfo();
        if (jwtInfo != null) {
            body.put("RequestId", jwtInfo.getRequestId());
            body.put("AppId", Integer.valueOf(jwtInfo.getAppId()));
            body.put("Uin", jwtInfo.getUin());
            body.put("Region", jwtInfo.getRegion());
        }
        // 设置action
        body.put("Action", action);
        // 发送http请求,拿到请求结果
        HttpConnectUtil.ResponseInfo responseInfo = switch (method) {
            case "GET" -> HttpConnectUtil.sendGetByJson(url, JsonUtil.toJson(body));
            case "POST" -> HttpConnectUtil.sendPost(url, JsonUtil.toJson(body), new HashMap<>(2));
            default -> new HttpConnectUtil.ResponseInfo();
        };
        // 检查Api3格式返回结果,并解析
        var jsonResponse = JsonUtil.getJsonNode(responseInfo.getContent()).get("Response");
        var jsonError = jsonResponse.get("Error");
        if (jsonError != null) {
            var errorCode = jsonError.get("Code").asText();
            var errorMessage = jsonError.get("Message").asText();
            throw new ApiException(ErrorCode.INTERNAL_ERROR,
                    String.format("错误码:[%s],错误信息:[%s]", errorCode, errorMessage));
        }
        return jsonResponse;
    }

到此这篇关于springboot中在filter中用threadlocal存放用户身份信息的文章就介绍到这了,更多相关springboot threadlocal存放用户身份信息内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Mybatis增强版MyBatis-Flex的具体使用

    Mybatis增强版MyBatis-Flex的具体使用

    Mybatis-Flex一个用于增强MyBatis的框架,本文主要介绍了Mybatis增强版MyBatis-Flex的具体使用,具有一定的参考价值,感兴趣的可以了解一下
    2024-06-06
  • SpringMVC中的DispatcherServlet初始化流程详解

    SpringMVC中的DispatcherServlet初始化流程详解

    这篇文章主要介绍了SpringMVC中的DispatcherServlet初始化流程详解,DispatcherServlet这个前端控制器是一个Servlet,所以生命周期和普通的Servlet是差不多的,在一个Servlet初始化的时候都会调用该Servlet的init()方法,需要的朋友可以参考下
    2023-12-12
  • Java查搜索文件内容实现方式

    Java查搜索文件内容实现方式

    用户为解决无法搜索文件内容的问题,编写了一个支持单文件、文件夹及多层递归查找的工具,具备字符串匹配及忽略大小写功能,并计划扩展日期、创建人等组合搜索条件,最终打包成exe便于快速查找所有文件内容
    2025-09-09
  • SpringBoot 自定义注解之脱敏注解详解

    SpringBoot 自定义注解之脱敏注解详解

    这篇文章主要介绍了SpringBoot 自定义注解之脱敏注解详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • java wait()/notify() 实现生产者消费者模式详解

    java wait()/notify() 实现生产者消费者模式详解

    这篇文章主要介绍了java wait()/notify() 实现生产者消费者模式详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Java关于含有继承类的成员初始化过程讲解

    Java关于含有继承类的成员初始化过程讲解

    今天小编就为大家分享一篇Java关于含有继承类的成员初始化过程讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-04-04
  • IDEA 设置显示内存的使用情况和内存回收的方法

    IDEA 设置显示内存的使用情况和内存回收的方法

    这篇文章主要介绍了IDEA 设置显示内存的使用情况和内存回收的方法,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • Spring AI对接大模型开发易错点总结与实战解决办法

    Spring AI对接大模型开发易错点总结与实战解决办法

    本文介绍了SpringAI接入大模型时常见的问题及解决方案,主要从地址配置、密钥鉴权、模型匹配、版本依赖四大维度入手,详细分析了各种问题的成因,并提供了实用的配置与代码解决方案,帮助开发者快速避坑,提高开发效率,需要的朋友可以参考下
    2026-05-05
  • Java代码混淆工具ProGuard使用指南(附有1.8以上和以下使用工具)

    Java代码混淆工具ProGuard使用指南(附有1.8以上和以下使用工具)

    ProGuard是一个开源的Java class文件缩小器、优化器、混淆器和预验证器,它通过删除未使用的类、字段、方法和属性,优化字节码指令,并重命名类、字段和方法,使反编译后的代码难以理解,从而提高应用的安全性,本文给大家详细介绍了ProGuard使用指南,需要的朋友可以参考下
    2025-05-05
  • SSH 框架简介

    SSH 框架简介

    SSH是 struts+spring+hibernate的一个集成框架,是目前较流行的一种web应用程序开源框架。本文给大家详细看一下组成SSH的这三个框架
    2017-09-09

最新评论