CORS與CSRF在Spring Security中的使用


背景

在項目使用了Spring Security之后,很多接口無法訪問了,從瀏覽器的網絡調試窗看到的是CORS的報錯和403的報錯

image

分析

我們先來看一下CORS是什么,和它很相似的CSRF是什么,在SpringSecurity中如何配置以及起的什么作用

CORS(Cross Origin Resource Sharing)

CORS跨域資源分享,是一種機制,通過在HTTP響應頭中加入特定字段限制不同域的資源請求

跨源HTTP請求的一個例子:運行在 https://domain-a.com 的 JavaScript 代碼使用 XMLHttpRequest 來發起一個到 https://domain-b.com/data.json 的請求

出於安全性,瀏覽器限制腳本內發起的跨源HTTP請求,而CORS就是用來允許跨源請求

用法

用法: 在服務端的響應頭(Header)中增加以下字段

Header 含義 例子
Access-Control-Allow-Origin 指定了允許訪問該資源的外域 URI https://mozilla.org或者*
Access-Control-Expose-Headers 讓服務器把允許瀏覽器訪問的頭放入白名單 X-My-Custom-Header, X-Another-Custom-Header
Access-Control-Max-Age 指定了preflight請求的結果能夠被緩存多久
Access-Control-Allow-Credentials 當瀏覽器的 credentials 設置為 true 時是否允許瀏覽器讀取 response 的內容 true
Access-Control-Allow-Methods 允許哪些方法 GET、POST
Access-Control-Allow-Methods 允許哪些Header

代碼非常簡單,有現成的兩種方式

SpringMVC

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CrossConfig implements WebMvcConfigurer{

	@Override
	public void addCorsMappings( CorsRegistry registry ){

		registry.addMapping( "/**" )
				.allowedOrigins( "*" )
				.allowedMethods( "GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS" )
				.allowCredentials( true ).maxAge( 3600 ).allowedHeaders( "*" );
	}
}

Spring Security

在Spring Security配置類中加入

	@Bean
	public CorsConfigurationSource corsConfigurationSource(){

		CorsConfiguration configuration = new CorsConfiguration();
		configuration.setAllowedOrigins( Collections.singletonList( "*" ) );
		configuration.setAllowedMethods( Arrays.asList( "GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS" ) );
		configuration.setAllowCredentials( true );
		configuration.setAllowedHeaders( Collections.singletonList( "*" ) );
		configuration.setMaxAge( 3600L );
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration( "/**", configuration );
		return source;
	}

注意

  • 自己寫過濾器也行,總之把這些Header加入響應就行
  • 上面代碼的策略非常寬松,可以適當限制域名保證安全
Spring Security和SpringMVC的CORS沖突

Spring Security是用訪問認證是過濾器來實現的

SpringMVC的CORS是用攔截器來實現的,參考SpringBoot中addCorsMappings配置跨域與攔截器互斥問題的原因研究
,其中寫入響應頭的類是org.springframework.web.cors.DefaultCorsProcessor

當存在Spring Security時,會存在加不上響應頭的現象,因為在過濾器階段可能因為認證不通過被拒絕了,所以當存在Spring Security的時候使用Spring Security的CORS用法就行

CSRF(Cross Site Request Forgery)

CSRF跨站請求偽造,是一種web攻擊手段,通過向服務器發送偽造請求,進行惡意行為的攻擊手段

舉個例子,你登錄了A網站,瀏覽器會記錄A網站的登錄Cookie,下次訪問就不會重新登錄了,而此時你訪問了B網站,網站的網頁攜帶一個惡意JS腳本,其中的內容是獲取或更改你A網站的信息,此時瀏覽器會自動的把Cookie帶上,A網站會認為這個請求是你本人的操作,CSRF就是用來防范這種攻擊的

如何防范

Spring Security的方式是在表單上面生成一個csrf_token, 提交表單的時候去驗證,因為第三方網站是沒有這個token的,所以提交不成功

無狀態應用

如這篇文章A Guide to CSRF Protection in Spring Security所說:

If our stateless API uses token-based authentication, like JWT, we don't need CSRF protection and we must disable it as we saw earlier.

使用JWT無狀態應用是不需要csrf保護的,因為JWT不使用cookie, 具體參考如何通過JWT防御CSRF

Spring Security關閉csrf

其中的csrf().disable()

@Override
	protected void configure( HttpSecurity http ) throws Exception{

		http
				.cors()
				.and()
				.csrf()
				.disable()
				.sessionManagement()
				.and()
				.authorizeRequests()
				.anyRequest()
				.permitAll()
				.and()
				.exceptionHandling()
				.accessDeniedHandler( new SimpleAccessDeniedHandler() );
	}

Set-Cookie問題

解決了cors的問題之后,再訪問Spring Security需要認證的URL時,還是出現403錯誤

image

看下save的請求頭,沒有帶上cookie
image

userinfo接口是登錄接口,響應頭包含了Set-Cookie,看起來沒有生效, 而且能確認是前端的問題
image

解決方法

通過修改前端axios配置, 增加withCredentials: true

const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API2, 
  withCredentials: true,
  transformResponse: [function (data) {
    try {
      return JSONBig.parse(data)
    } catch (err) {
      return data
    }
  }],
  timeout: 20000 // request timeout
})

參考

SpringMVC官方文檔

Spring Security 中的 CSRF和CORS

MDN HTTP CORS

如何通過JWT防御CSRF

SpringBoot中addCorsMappings配置跨域與攔截器互斥問題的原因研究


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM