Spring Security :HTTP Status 403-Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.
原因:
1.Spring Security 4.0之后,引入了CSRF,默認是開啟。CSRF默認支持的方法: GET|HEAD|TRACE|OPTIONS,不支持POST。
Spring Security 3默認關閉csrf,Spring Security 4默認啟動了csrf。
2.什么是csrf:
這是一個web應用安全的問題,CSRF(Cross-site request forgery跨站請求偽造,也被稱為“One Click Attack” 或者Session Riding,攻擊方通過偽造用戶請求訪問受信任站點。
我們知道,客戶端與服務端在基於http協議在交互的數據的時候,由於http協議本身是無狀態協議,后來引進了cookie的 方式進行記錄服務端和客戶端的之間交互的狀態和標記。cookie里面一般會放置服務端生成的session id(會話ID)用來識別客戶端訪問服務端過 程中的客戶端的身份標記。
在跨域 (科普一下:同一個ip、同一個網絡協議、同一個端口,三者都滿足就是同一個域,否則就有跨域問題) 的情況下, session id可能會被惡意第三方劫持,此時劫持這個session id的第三方會根據這個session id向服務器發起請求,此時服務器收到這個請求會 認為這是合法的請求,並返回根據請求完成相應的服務端更新。
如果這個http請求是get方式發起的請求,意味着它只是訪問服務器 的資源,僅僅只是查詢,沒有更新服務器的資源,所以對於這類請求,spring security的防御策略是允許的;
如果這個請求是通過post請求發起的, 那么spring security是默認攔截這類請求的,因為這類請求是帶有更新服務器資源的危險操作,如果惡意第三方可以通過劫持session id來更新 服務器資源,那會造成服務器數據被非法的篡改,所以這類請求是會被Spring security攔截的,在默認的情況下,spring security是啟用csrf 攔截功能的,這會造成,在跨域的情況下,post方式提交的請求都會被攔截無法被處理(包括合理的post請求),前端發起的post請求后端無法正常 處理,雖然保證了跨域的安全性,但影響了正常的使用,如果關閉csrf防護功能,雖然可以正常處理post請求,但是無法防范通過劫持session id的非法的post請求,所以spring security為了正確的區別合法的post請求,采用了token的機制。
在跨域的場景下,客戶端訪問服務端會首先發起get請求,這個get請求在到達服務端的時候,服務端的Spring security會有一個過濾 器 CsrfFilter去檢查這個請求,如果這個request請求的http header里面的X-CSRF-COOKIE的token值為空的時候,服務端就好自動生成一個 token值放進這個X-CSRF-COOKIE值里面,客戶端在get請求的header里面獲取到這個值,如果客戶端有表單提交的post請求,則要求客戶端要 攜帶這個token值給服務端,在post請求的header里面設置_csrf屬性的token值,提交的方式可以是ajax也可以是放在form里面設置hidden 屬性的標簽里面提交給服務端,服務端就會根據post請求里面攜帶的token值進行校驗,如果跟服務端發送給合法客戶端的token值是一樣的,那么 這個post請求就可以受理和處理,如果不一樣或者為空,就會被攔截。由於惡意第三方可以劫持session id,而很難獲取token值,所以起到了 安全的防護作用。
解決方案:
如果不采用csrf,可禁用security的csrf
Java注解方式配置:加上 .csrf().disable()
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers("/").access("hasRole('READER')") .antMatchers("/*").permitAll() .and() .formLogin() .loginPage("/login") .failureUrl("/login?error=true"); http.csrf().disable(); } }