spring security下開啟csrf,同時支援session.invalidate()呼叫
阿新 • • 發佈:2019-01-29
最近在做的一個java web專案,要求登入介面資訊提交時使用https,登入成功後頁面使用http,同時全站使用csrf防禦。
然後https和http的訪問會分別建立兩個session,csrf的token存在於https建立的session中,當驗證使用者身份通過後,跳轉到http進入時,新建的session中沒有csrfToken,因此被403拒絕訪問。
解決方法為:在身份驗證成功後,跳轉頁面的action中加入
這樣就可以保證跳轉前後的session保持一致了。Cookie nck = new Cookie("JSESSIONID",request.getSession().getId()); nck.setPath("/"); nck.setSecure(true); response.addCookie(nck);
但新的需求又來了:在某些action處理過程中,需要更新session id,可以參考《spring security 整合csrf與會話更新》,也可以修改spring security原始碼。
spring security的CsrfFilter.java檔案中,在filterChain執行完回撥後,再新增一次token到session中即可:
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { CsrfToken csrfToken = tokenRepository.loadToken(request); final boolean missingToken = csrfToken == null; if (missingToken) { CsrfToken generatedToken = tokenRepository.generateToken(request); csrfToken = new SaveOnAccessCsrfToken(tokenRepository, request, response, generatedToken); } request.setAttribute(CsrfToken.class.getName(), csrfToken); request.setAttribute(csrfToken.getParameterName(), csrfToken); if (!requireCsrfProtectionMatcher.matches(request)) { filterChain.doFilter(request, response); //****以下是新增的程式碼**** HttpSession session = request.getSession(false); if(session!=null){ tokenRepository.saveToken(csrfToken, request, response); } //****以上是新增的程式碼**** return; } System.out.println("method: "+request.getMethod()+" match."); String actualToken = request.getHeader(csrfToken.getHeaderName()); if (actualToken == null) { System.out.println("req.getHeader.token==null"); actualToken = request.getParameter(csrfToken.getParameterName()); } if (!csrfToken.getToken().equals(actualToken)) { System.out.println("csrfToken:"+csrfToken.getToken()); System.out.println("actualToken:"+actualToken); if (logger.isDebugEnabled()) { logger.debug("Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request)); } if (missingToken) { accessDeniedHandler.handle(request, response, new MissingCsrfTokenException(actualToken)); } else { accessDeniedHandler.handle(request, response, new InvalidCsrfTokenException(csrfToken, actualToken)); } return; } System.out.println("csrf filter next doFilter"); filterChain.doFilter(request, response);
****以下是新增的程式碼****
HttpSession session = request.getSession(false);
if(session!=null){
tokenRepository.saveToken(csrfToken, request, response);
}
//****以上是新增的程式碼****
return;
}
這樣修改應該可行了,在執行中某些頁面跳轉時遇到session空指標問題,很納悶,原來是jsp載入時,要獲取session中的csrfToken,但是session為空,沒搞懂原因,只能在HttpSessionCsrfTokenRepository.java的saveToken函式中新增session為空的判斷:
修改完成後,再跑程式,就沒問題了。public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) { if (token == null) { HttpSession session = request.getSession(false); if (session != null) { session.removeAttribute(sessionAttributeName); } } else { HttpSession session = request.getSession(); if(session!=null)//在此處新增判斷 session.setAttribute(sessionAttributeName, token); } }