基于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验证的范围之外了.
已有 0 人发表留言,猛击->> 这里<<-参与讨论
ITeye推荐