Spring Securityメモ:CsrfFilterとRestサービスPost方式の矛盾を解決します.

11416 ワード

Spring Security+Spring MVCによるwebアプリケーションは、クロスステーションからの攻撃を防ぐために、通常はcsrfが配置されます.
1     <http ...>
2         ...
3         <csrf />        
4     http>
Post方式でアクセスしたResetサービス(以下のコードを参照)が適用されている場合、すべての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によって阻止されます.Csrf Filterには私有類のDefaultRequires CsrfMatchがあります.
 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_124; TRACE_OPTTIONSの4つの方法だけが実行されます.他のMethodのhttp要求は全部確認してください.csrfのtokenが正しいかどうかは、通常のpost方式でrestサービスを起動する時、またない.csrfのtokenは検証に失敗しました.
解決方法:自分でマッチを一つ作ります.
 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 execludeUrls;
31 
32     public List getExecludeUrls() {
33         return execludeUrls;
34     }
35 
36     public void setExecludeUrls(List execludeUrls) {
37         this.execludeUrls = execludeUrls;
38     }
39 }
ここにはどんなurlを排除するために人為的に可能な属性execludeUrlsが追加されています.
設定ファイルでこのように修正します.
 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サービスアドレスで、上の構成はcsrf検証の範囲から除外されました.