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到服務端。但願對你有所幫助,歡迎留言交流。