個人問題記錄
Spring Security在集成Spring Boot的微服務框架后,實現(xiàn)了權(quán)限控制撮弧。但是在使用 postman 進行調(diào)用的時候出現(xiàn)這個問題.
科普一下,CSRF(Cross-site request forgery跨站請求偽造菲嘴,也被稱為“One Click Attack” 或者Session Riding迄委,攻擊方通過偽造用戶請求訪問受信任站點』鳎可以這么理解CSRF攻擊:攻擊者盜用了你的身份十拣,以你的名義向第三方網(wǎng)站發(fā)送惡意請求
原因
Spring Security 4.0之后,默認是開啟CSRF志鹃。不得不說夭问,CSRF和RESTful技術(shù)有沖突。CSRF默認支持的方法: GET|HEAD|TRACE|OPTIONS曹铃,不支持POST缰趋。
我們知道,客戶端與服務端在基于http協(xié)議在交互的數(shù)據(jù)的時候,由于http協(xié)議本身是無狀態(tài)協(xié)議秘血,后來引進了cookie的 方式進行記錄服務端和客戶端的之間交互的狀態(tài)和標記味抖。cookie里面一般會放置服務端生成的session id(會話ID)用來識別客戶端訪問服務端過 程中的客戶端的身份標記。
比較關(guān)鍵的一點是
如果這個http請求是get方式發(fā)起的請求灰粮,意味著它只是訪問服務器 的資源仔涩,僅僅只是查詢,沒有更新服務器的資源谋竖,所以對于這類請求红柱,spring security的防御策略是允許的,
如果這個請求是通過post請求發(fā)起的蓖乘, 那么spring security是默認攔截這類請求的锤悄,因為這類請求是帶有更新服務器資源的危險操作,如果惡意第三方可以通過劫持session id來更新 服務器資源嘉抒,那會造成服務器數(shù)據(jù)被非法的篡改零聚,所以這類請求是會被Spring security攔截的,在默認的情況下些侍,spring security是啟用csrf 攔截功能的隶症,這會造成,在跨域的情況下岗宣,post方式提交的請求都會被攔截無法被處理(包括合理的post請求)蚂会,前端發(fā)起的post請求后端無法正常 處理,雖然保證了跨域的安全性耗式,但影響了正常的使用胁住,如果關(guān)閉csrf防護功能,雖然可以正常處理post請求刊咳,但是無法防范通過劫持session id的非法的post請求彪见,所以spring security為了正確的區(qū)別合法的post請求,采用了token的機制娱挨。
在跨域的場景下余指,客戶端訪問服務端會首先發(fā)起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 屬性的標簽里面提交給服務端,服務端就會根據(jù)post請求里面攜帶的token值進行校驗,如果跟服務端發(fā)送給合法客戶端的token值是一樣的庇配,那么 這個post請求就可以受理和處理斩跌,如果不一樣或者為空,就會被攔截捞慌。由于惡意第三方可以劫持session id耀鸦,而很難獲取token值,所以起到了 安全的防護作用啸澡。
- 解決
原因找到了:spring Security 3默認關(guān)閉csrf袖订,Spring Security 4默認啟動了csrf。
解決方案:
如果不需要采用csrf嗅虏,可禁用security的csrf
Java注解方式配置:
加上 .csrf().disable()即可洛姑。
修改前WebSecurityConfig.java
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.and()
.formLogin()
.loginPage("/login").permitAll()
.and()
.logout().logoutUrl("/logout")
.logoutSuccessUrl("/hello")
.permitAll();
http.addFilterBefore(customizeFilterSecurityInterceptor, FilterSecurityInterceptor.class)
.csrf().disable(); //增加
}