Springboot關於在攔截器中列印相關日誌和鑑權的若干點
阿新 • • 發佈:2022-05-18
需求:在filter中進行請求日誌的列印(有時候是因為資料的序列化出錯),做簡單鑑權
java中springboot,request body InputStream流的讀取只能讀取一次,讀完就沒,如果在filter中直接讀取了,到controller中資料就拿不到了。所以springboot提供了 ContentCachingRequestWrapper 這個類來進行包裹。
先看這個類的名稱可以踩個大概就是做request的快取用的。
// 程式碼如下,基本原理是將原始的request初始化wrappedRequest,然後用這個變數貫穿整個請求過程, // 值得注意的是wrappedRequest這個工作原理是read()函式被呼叫的過程中快取,也就是說這個原理是springboot/mvc在controller進行body資料讀取的時候,讀出來了再把資料copy一份到cache中,所以wrappedRequest就能反覆讀取資料。 // 在下列程式碼中 執行的順序 是 doFilter() 呼叫 read()函式,wrappedRequest中的資料就被copy完畢,所以recordReq() 和 recordResp() 函式引數就能訪問到請求資料,如果不使用wrappedRequest,那麼資料讀取完後request中資料就空了。 // 這邊值得注意的是在許可權驗證失敗時候手動呼叫了wrappedRequest.getReader().read(),意在將資料刷到wrappedRequest中。 @Component @WebFilter(filterName = "authenticationFilter", urlPatterns = "/*") @Order(-9999) @Slf4j public class AuthenticationFilter extends GenericFilterBean { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper((HttpServletRequest) servletRequest); ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper((HttpServletResponse) servletResponse); // 授權認證 Optional<String> origin = authentication(wrappedRequest); long start = TimeUtils.getLongTimeMillis(); if (origin.isEmpty()) { // 手動讀取,將結果重新整理到wrappedRequest中 wrappedRequest.getReader().read(); // 為攜帶Token wrappedResponse.setStatus(HttpServletResponse.SC_OK); String str = GsonUtils.toJson(BaseResponse.fail(ResultCode.AUTHORIZE_FAILED)); //設定 HttpServletResponse使用utf-8編碼 wrappedResponse.setCharacterEncoding("utf-8"); //設定響應頭的編碼 wrappedResponse.setHeader("Content-Type", "application/json;charset=utf-8"); wrappedResponse.getWriter().write(str); } else { chain.doFilter(wrappedRequest, wrappedResponse); } long end = TimeUtils.getLongTimeMillis(); // 記錄請求資訊 recordReq(wrappedRequest, origin); // 記錄response資訊 recordResp(wrappedRequest, wrappedResponse, end - start); // 這一步很重要,把快取的響應內容,輸出到客戶端 wrappedResponse.copyBodyToResponse(); } }