springboot在filter中如何用threadlocal存放用户身份信息
作者:march of Time
本文章主要描述通过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存放用户身份信息内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!