什麼是csrf?
spring-security中csrf防禦原理
- // 先從tokenRepository中加載token
- CsrfToken csrfToken = tokenRepository.loadToken(request);
- final boolean missingToken = csrfToken == null;
- // 如果爲空,則tokenRepository生成新的token,並保存到tokenRepository中
- if(missingToken) {
- CsrfToken generatedToken = tokenRepository.generateToken(request);
- // 默認的SaveOnAccessCsrfToken方法,記錄tokenRepository,
- // tokenRepository,response,獲取token時先將token同步保存到tokenRepository中
- csrfToken = new SaveOnAccessCsrfToken(tokenRepository, request, response, generatedToken);
- }
- // 將token寫入request的attribute中,方便頁面上使用
- request.setAttribute(CsrfToken.class.getName(), csrfToken);
- request.setAttribute(csrfToken.getParameterName(), csrfToken);
- // 如果不需要csrf驗證的請求,則直接下傳請求(requireCsrfProtectionMatcher是默認的對象,對符合^(GET|HEAD|TRACE|OPTIONS)$的請求不驗證)
- if(!requireCsrfProtectionMatcher.matches(request)) {
- filterChain.doFilter(request, response);
- return;
- }
- // 從用戶請求中獲取token信息
- String actualToken = request.getHeader(csrfToken.getHeaderName());
- if(actualToken == null) {
- actualToken = request.getParameter(csrfToken.getParameterName());
- }
- // 驗證,如果相同,則下傳請求,如果不同,則拋出異常
- if(!csrfToken.getToken().equals(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;
- }
- filterChain.doFilter(request, response);
使用樣例
- <bean id="csrfFilter" class="org.springframework.security.web.csrf.CsrfFilter">
- <constructor-arg>
- <span style="font-family: Arial, Helvetica, sans-serif;"><!--</span><pre name="code" class="html">HttpSessionCsrfTokenRepository是把token放到session中來存取
- <bean class="org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository"/>
- </constructor-arg>
- </bean>
- <!--
- 如果用的是spring mvc 的form標籤,則配置此項時自動將crsf的token放入到一個hidden的input中,而不需要開發人員顯式的寫入form
- -->
- <bean id="requestDataValueProcessor" class="org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor"/>
- <meta name="_csrf" content="${_csrf.token}"/>
- <meta name="_csrf_header" content="${_csrf.headerName}"/>
- var token = $("meta[name='_csrf']").attr("content");
- var header = $("meta[name='_csrf_header']").attr("content");
- $(document).ajaxSend(function(e, xhr, options) {
- xhr.setRequestHeader(header, token);
- });