springboot在filter中如何用threadlocal存放用戶身份信息
本文章主要描述通過springboot的filter類,在過濾器中設(shè)置jwt信息進(jìn)行身份信息保存的方法
流程:請求->過濾器->解析請求的body信息->放入threadlocal中
定義filter:一個使用 Servlet 規(guī)范的過濾器(Filter),它通過 @WebFilter 注解注冊為攔截所有匹配 /api 路徑的 HTTP 請求。
@WebFilter(“/api”) 注解指定了過濾器將應(yīng)用于所有訪問 /api路徑的請求。
@Component 注解:
@Component 是 Spring 框架的注解,表明 JwtFilter 是一個 Spring 組件,可以被 Spring 容器管理,并支持依賴注入。
doFilter方法:
doFilter 方法定義了過濾器如何攔截和處理進(jìn)入 Servlet 或 Servlet 容器的請求和響應(yīng)。
方法簽名:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;
這個方法接受三個參數(shù):ServletRequest request、ServletResponse response 和 FilterChain chain。
它可能拋出 IOException 或 ServletException。
請求和響應(yīng):
doFilter 方法的前兩個參數(shù)代表當(dāng)前的請求和響應(yīng)對象,你可以在這個方法中讀取請求數(shù)據(jù)、修改請求和響應(yīng)。
通常,在 doFilter 方法的最后,你需要調(diào)用 chain.doFilter(request, response) 來繼續(xù)執(zhí)行過濾器鏈中的下一個過濾器或目標(biāo)資源。
如果要重新修改請求內(nèi)容,可以用HttpServletRequestWrapper,HttpServletRequestWrapper 是一個包裝器類,它擴(kuò)展了 HttpServletRequest 接口,允許你修改或擴(kuò)展請求的處理。使用 HttpServletRequestUriWrapper(這可能是一個自定義的包裝器類,繼承自 HttpServletRequestWrapper)的目的通常包括:
修改請求 URI:
你可能想要修改請求的 URI,但不想改變原始的 HttpServletRequest 對象。通過使用 HttpServletRequestUriWrapper,你可以包裝原始請求并提供一個修改后的 URI。
保持原始請求不變:
使用包裝器可以保持原始請求對象不變,同時允許你在過濾鏈中的某個點(diǎn)修改請求的某些方面。
過濾和預(yù)處理:
在調(diào)用 filterChain.doFilter 之前,你可以在 doFilter 方法中添加任何預(yù)處理邏輯,例如修改請求參數(shù)、更改請求路徑、添加或修改請求頭等。
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參數(shù),并存入threadLocal管理 var jwtInfo = JwtUtil.getJwtInfoFromReq(requestBodyPayload); JwtUtil.setJwtInfo(jwtInfo); // 讀取過body,需要重新設(shè)置body var wrapper = new HttpServletRequestUriWrapper(httpRequest, httpRequest.getRequestURI(), requestBodyPayload); // 將請求傳遞到下一個過濾器(或者最終到達(dá)控制器方法) filterChain.doFilter(wrapper, servletResponse); } @Override public void destroy() { JwtUtil.removeJwtInfo(); MDC.clear(); } }
jwt信息:
@Slf4j @Component public class JwtUtil { /** 線程jwt信息維護(hù) */ 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,轉(zhuǎn)為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中的內(nèi)容,去發(fā)送其他http請求:
public static JsonNode sendHttpRequest(String method, String action, String url, Map<String, Object> body) throws IOException, InterruptedException { // 設(shè)置通用參數(shù) 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()); } // 設(shè)置action body.put("Action", action); // 發(fā)送http請求,拿到請求結(jié)果 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格式返回結(jié)果,并解析 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; }
到此這篇關(guān)于springboot中在filter中用threadlocal存放用戶身份信息的文章就介紹到這了,更多相關(guān)springboot threadlocal存放用戶身份信息內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot中的ThreadLocal保存請求用戶信息的實(shí)例demo
- springboot登錄攔截器+ThreadLocal實(shí)現(xiàn)用戶信息存儲的實(shí)例代碼
- SpringBoot ThreadLocal 簡單介紹及使用詳解
- SpringBoot+ThreadLocal+AbstractRoutingDataSource實(shí)現(xiàn)動態(tài)切換數(shù)據(jù)源
- Springboot公共字段填充及ThreadLocal模塊改進(jìn)方案
- SpringBoot ThreadLocal實(shí)現(xiàn)公共字段自動填充案例講解
- SpringBoot通過ThreadLocal實(shí)現(xiàn)登錄攔截詳解流程
- springboot 使用ThreadLocal的實(shí)例代碼
- SpringBoot中使用?ThreadLocal?進(jìn)行多線程上下文管理及注意事項(xiàng)小結(jié)
相關(guān)文章
Java中的lambda和stream實(shí)現(xiàn)排序
這篇文章主要介紹了Java中的lambda和stream實(shí)現(xiàn)排序,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-09-09springBoot整合shiro如何解決讀取不到@value值問題
這篇文章主要介紹了springBoot整合shiro如何解決讀取不到@value值問題,具有很好的參考價值,希望對大家有所幫助,2023-08-08SpringBoot入門實(shí)現(xiàn)第一個SpringBoot項(xiàng)目
今天我們一起來完成一個簡單的SpringBoot(Hello World)。就把他作為你的第一個SpringBoot項(xiàng)目。具有一定的參考價值,感興趣的可以了解一下2021-09-09詳解java代碼中init method和destroy method的三種使用方式
這篇文章主要介紹了詳解java代碼中init method和destroy method的三種使用方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03完美解決在Servlet中出現(xiàn)一個輸出中文亂碼的問題
下面小編就為大家?guī)硪黄昝澜鉀Q在Servlet中出現(xiàn)一個輸出中文亂碼的問題。小編覺得挺不錯的現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01springboot實(shí)現(xiàn)maven多模塊和打包部署
本文主要介紹了springboot實(shí)現(xiàn)maven多模塊和打包部署,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04java使用WatchService監(jiān)控文件夾示例
本篇文章主要介紹了java使用WatchService監(jiān)控文件夾示例的資料,這里整理了詳細(xì)的代碼,有需要的小伙伴可以參考下。2017-02-02