• Spring Security笔记:解决CsrfFilter与Rest服务Post方式的矛盾


    基于Spring Security+Spring MVC的web应用,为了防止跨站提交攻击,通常会配置csrf,即:

    1     <http ...>
    2         ...
    3         <csrf />        
    4     </http>

    如果应用中有Post方式访问的Rest服务(参考下面的代码),会很不幸的发现,所有POST方式请求的服务会调用失败。

    1     @RequestMapping(value = "/user/create", method = RequestMethod.POST)
    2     @ResponseBody
    3     public UserInfo createUser(@RequestBody(required = true) UserInfo user,
    4             HttpServletRequest request, HttpServletResponse response)
    5             throws Exception {
    6         ...
    7     }

    原因在于:启用csrf后,所有http请求都被会CsrfFilter拦截,而CsrfFilter中有一个私有类DefaultRequiresCsrfMatcher

     1     private static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
     2         private Pattern allowedMethods = Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");
     3 
     4         /* (non-Javadoc)
     5          * @see org.springframework.security.web.util.matcher.RequestMatcher#matches(javax.servlet.http.HttpServletRequest)
     6          */
     7         public boolean matches(HttpServletRequest request) {
     8             return !allowedMethods.matcher(request.getMethod()).matches();
     9         }
    10     }

    从这段源码可以发现,POST方法被排除在外了,也就是说只有GET|HEAD|TRACE|OPTIONS这4类方法会被放行,其它Method的http请求,都要验证_csrf的token是否正确,而通常post方式调用rest服务时,又没有_csrf的token,所以校验失败。

    解决方法:自己弄一个Matcher

     1 package com.cnblogs.yjmyzz.utils;
     2 
     3 import java.util.List;
     4 import java.util.regex.Pattern;
     5 
     6 import javax.servlet.http.HttpServletRequest;
     7 
     8 import org.springframework.security.web.util.matcher.RequestMatcher;
     9 
    10 public class CsrfSecurityRequestMatcher implements RequestMatcher {
    11     private Pattern allowedMethods = Pattern
    12             .compile("^(GET|HEAD|TRACE|OPTIONS)$");
    13 
    14     public boolean matches(HttpServletRequest request) {
    15 
    16         if (execludeUrls != null && execludeUrls.size() > 0) {
    17             String servletPath = request.getServletPath();
    18             for (String url : execludeUrls) {
    19                 if (servletPath.contains(url)) {
    20                     return false;
    21                 }
    22             }
    23         }
    24         return !allowedMethods.matcher(request.getMethod()).matches();
    25     }
    26 
    27     /**
    28      * 需要排除的url列表
    29      */
    30     private List<String> execludeUrls;
    31 
    32     public List<String> getExecludeUrls() {
    33         return execludeUrls;
    34     }
    35 
    36     public void setExecludeUrls(List<String> execludeUrls) {
    37         this.execludeUrls = execludeUrls;
    38     }
    39 }

    这里添加了一个属性execludeUrls,允许人为排除哪些url。

    然后在配置文件里,这样修改:

     1     <http entry-point-ref="loginEntryPoint" use-expressions="true">
     2         ...
     3         <intercept-url pattern="/rest/**" access="permitAll" />
     4         ...
     5         <csrf request-matcher-ref="csrfSecurityRequestMatcher"/>        
     6     </http>
     7     
     8     <beans:bean id="csrfSecurityRequestMatcher" class="com.cnblogs.yjmyzz.utils.CsrfSecurityRequestMatcher">
     9         <beans:property name="execludeUrls">
    10             <beans:list>
    11                 <beans:value>/rest/</beans:value>
    12             </beans:list>
    13         </beans:property>
    14     </beans:bean>

    这里约定所有/rest/开头的都是Rest服务地址,上面的配置就把/rest/排除在csrf验证的范围之外了.

  • 相关阅读:
    MySQL group_concat() 函数用法
    Git error The file will have its original line endings in your working directory
    SQL exists 基本用法
    (11) 严格模式(use strict)
    (10)变量提升
    (9)调试
    (8)正则表达式
    (7)类型转换
    (6)typeof, null, 和 undefined
    (5)运算符
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/customize-CsrfFilter-to-ignore-certain-post-http-request.html
Copyright © 2020-2023  润新知