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验证的范围之外了.

时间: 2024-10-02 19:21:53

Spring Security笔记:解决CsrfFilter与Rest服务Post方式的矛盾的相关文章

Spring Security笔记:HTTP Basic 认证

在第一节 Spring Security笔记:Hello World 的基础上,只要把Spring-Security.xml里改一个位置 1 <http auto-config="true"> 2 <intercept-url pattern="/admin" access="ROLE_USER" /> 3 <http-basic /> 4 </http> 注意第三行,加上<http-basi

Spring Security笔记:自定义Login/Logout Filter、AuthenticationProvider、AuthenticationToken

在前面的学习中,配置文件中的<http>...</http>都是采用的auto-config="true"这种自动配置模式,根据Spring Security文档的说明: ------------------ auto-config Automatically registers a login form, BASIC authentication, logout services. If set to "true", all of thes

Spring Security笔记:使用数据库进行用户认证(form login using database)

在前一节,学习了如何自定义登录页,但是用户名.密码仍然是配置在xml中的,这样显然太非主流,本节将学习如何把用户名/密码/角色存储在db中,通过db来实现用户认证 一.项目结构 与前面的示例相比,因为要连接db,所以多出了一个spring-database.xml用来定义数据库连接,此外,为了演示登录用户权限不足的场景,加了一个页面403.jsp,用来统一显示权限不足的提示信息 二.数据库表结构(oracle环境) 1 create table T_USERS 2 ( 3 d_username

Spring Security笔记:Remember Me(下次自动登录)

前一节学习了如何限制登录尝试次数,今天在这个基础上再增加一点新功能:Remember Me. 很多网站,比如博客园,在登录页面就有这个选项,勾选"下次自动登录"后,在一定时间段内,只要不清空浏览器Cookie,就可以自动登录. 一.spring-security.xml 最简单的配置 1 <http auto-config="true" use-expressions="true"> 2 ... 3 <remember-me /

Spring Security笔记:Hello World

本文演示了Spring Security的最最基本用法,二个页面(或理解成二个url),一个需要登录认证后才能访问(比如:../admin/),一个可匿名访问(比如:../welcome) 注:以下内容参考了 http://www.mkyong.com/spring-security/spring-security-hello-world-example/   一.利用STS(Spring Tools Suite)创建一个Spring MVC Project 如果不想使用STS,在普通Eclip

Spring Security笔记:自定义登录页

以下内容参考了 http://www.mkyong.com/spring-security/spring-security-form-login-example/ 接上回,在前面的Hello World示例中,Spring Security为我们自动生成了默认登录页,对于大多数项目而言,如此简单的登录页并不能满足实际需求,接下来,我们看看如何自定义登录页 一.项目结构 与前一个示例相比较,只是多了一个css样式以及登录页login.jsp,这二个文件具体的内容如下: 1 @CHARSET "UT

Spring Security笔记:使用BCrypt算法加密存储登录密码

在前一节使用数据库进行用户认证(form login using database)里,我们学习了如何把"登录帐号.密码"存储在db中,但是密码都是明文存储的,显然不太讲究.这一节将学习如何使用spring security3新加入的bcrypt算法,将登录加密存储到db中,并正常通过验证. 一.Bcrypt算法 1 int t = 0; 2 String password = "123456"; 3 System.out.println(password + &q

Spring Security 入门详解(转)

1.Spring Security介绍 Spring Security是基于spring的应用程序提供声明式安全保护的安全性框架,它提供了完整的安全性解决方案,能够在web请求级别和方法调用级别 处理身份证验证和授权.它充分使用了依赖注入和面向切面的技术. Spring security主要是从两个方面解决安全性问题: web请求级别:使用servlet过滤器保护web请求并限制URL级别的访问 方法调用级别:使用Spring AOP保护方法调用,确保具有适当权限的用户采用访问安全保护的方法.

java中spring security的remember me异常解决办法

 想想也不可能,网络中不会时不时出现cookie theft攻击吧.看了官方文档,也没给出解释,后来在oschina看到一篇分析的文章,才明白其中的缘由,文章<是谁动了我的cookie?Spring Security自动登录功能开发经历总结>. 从这篇文章的分析结合spring secutiy的源码来看,在每次持久化登录校验token完毕之后,会更新token的值并写入cookie.而token的校验是通过加密处理写入cookie中的字符串与持久化存储的token做对比看是否一致来判断登录信息