1. 程式人生 > >spring security下開啟csrf,同時支援session.invalidate()呼叫

spring security下開啟csrf,同時支援session.invalidate()呼叫

最近在做的一個java web專案,要求登入介面資訊提交時使用https,登入成功後頁面使用http,同時全站使用csrf防禦。

然後https和http的訪問會分別建立兩個session,csrf的token存在於https建立的session中,當驗證使用者身份通過後,跳轉到http進入時,新建的session中沒有csrfToken,因此被403拒絕訪問。

解決方法為:在身份驗證成功後,跳轉頁面的action中加入

Cookie nck = new Cookie("JSESSIONID",request.getSession().getId());
			nck.setPath("/");
			nck.setSecure(true);
			response.addCookie(nck);
這樣就可以保證跳轉前後的session保持一致了。

但新的需求又來了:在某些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);
		}
	}
修改完成後,再跑程式,就沒問題了。