CSRF简介——摘抄自《Spring实战(第4版)》css
咱们能够回忆一下,当一个POST请求提交到“/spittles”上时,SpittleController将会为用户建立一个新的Spittle对象。可是,若是这个POST请求来源于其余站点的话,会怎么样呢?若是在其余站点提交以下表单,这个POST请求会形成什么样的结果呢?假设你禁不住得到一辆新汽车的诱惑,点击了按钮——那么你将会提交表单到以下地址http://www.spittr.com/spittles。若是你已经登陆到了spittr.com,那么这就会广播一条消息,让每一个人都知道你作了一件蠢事。这是跨站请求伪造(cross-site request forgery,CSRF)的一个简单样例。简单来说,若是一个站点欺骗用户提交请求到其余服务器的话,就会发生CSRF攻击,这可能会带来消极的后果。尽管提交“I’m stupid!”这样的信息到微博站点算不上什么CSRF攻击的最糟糕场景,可是你能够很容易想到更为严重的攻击情景,它可能会对你的银行帐号执行难以预期的操做。 web
从Spring Security 3.2开始,默认就会启用CSRF防御。实际上,除非你采起行为处理CSRF防御或者将这个功能禁用,不然的话,在应用中提交表单时,你可能会遇到问题。spring
Spring Security经过一个同步token的方式来实现CSRF防御的功能。它将会拦截状态变化的请求(例如,非GET、HEAD、OPTIONS和TRACE的请求)并检查CSRF token。若是请求中不包含CSRF token的话,或者token不能与服务器端的token相匹配,请求将会失败,并抛出CsrfException异常。这意味着在你的应用中,全部的表单必须在一个“_csrf”域中提交token,并且这个token必需要与服务器端计算并存储的token一致,这样的话当表单提交的时候,才能进行匹配。服务器
好消息是,Spring Security已经简化了将token放到请求的属性中这一任务。app
1、Freemarker模板集成Spring Security-CSRF防止功能框架
1.Spring Security配置防止CSRF为开启状态,默认状况下该功能是开启的,调用disable()方法能够禁止。ide
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * HTTP请求处理 */ @Override protected void configure(HttpSecurity http) throws Exception { String doUrl = "/**/*.do"; http .formLogin().loginPage("/user/login.do") .defaultSuccessUrl("/free/list.do")//启用FORM登陆 .and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout","GET")) .and().authorizeRequests().antMatchers("/user/login.do").permitAll()//登陆页容许全部人访问 .and().portMapper().http(8898).mapsTo(8443) //添加端口映射,作测试用 .and().authorizeRequests().antMatchers(doUrl).authenticated() .and().requiresChannel().antMatchers("/free/**",doUrl).requiresSecure() .and().requiresChannel().antMatchers(doUrl).requiresInsecure() .and().httpBasic(); //.and().csrf().disable(); //禁用CSRF }
2.Freemarker模板绑定_csrf 值post
<#include "/templates/_base.ftl"/> <@layout;section> <#if section="title"> 用户登陆 <#elseif section="css"> <#elseif section="content"> <div class="width:400px;height:300px;"> <h3>用户登陆</h3> <form name="f" action="/stest/user/login.do" method="POST"> <input name="${_csrf.parameterName}" type="hidden" value="${_csrf.token}"> <table> <tbody> <tr><td>User:</td><td><input type="text" name="username" value=""></td></tr> <tr><td>Password:</td><td><input type="password" name="password"></td></tr> <tr><td colspan="2"><input name="submit" type="submit" value="Login"></td></tr> </tbody> </table> <p>${msg!}</p> </form> </div> <#elseif section="scripts"> </#if> </@layout>
疑问解析:属性_csrf是怎么产生的呢? 根据调试发现,这个属性是在org.springframework.security.web.csrf.CsrfFilter过滤器中赋值到request的属性集合中的。所以能够在视图绑定过程当中,直接获取。源码截图以下测试
3.查看登陆页面输出的csrf-Token值,已经成功赋值。若是咱们随便修改一下这个token值,而后post请求到服务端,会发现报403错误,请求被阻止。说明配置已经成功生效,防止了请求的伪造。
ui
2、当开启CSRF后,原来以Get方式,调用/logout,退出登陆状态的功能失效了,跳转后报404错误。
一、查看源码,发现框架方法里作了备注。大概意思就是开启CSRF后,logout方法须要以post方式提交。或者调用logoutRequestMatcher方法,显示设置/logout请求为GET方法
二、用logoutRequestMatcher设置logout为get请求来解决404报错问题。POST方式比较简单这里就不在赘述。固然,实际项目中,最好选择以post方式退出系统。
http .formLogin().loginPage("/user/login.do") .defaultSuccessUrl("/free/list.do")//启用FORM登陆 .and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout","GET"))
至此CSRF防止方案示例就讲完了。在项目中,对于post请求都须要注意提交_csrf到服务端。但愿对你有所帮助,欢迎留言交流。