什么是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);
- });