好的,各位掘金的靓仔靓女们,大家好!我是你们的老朋友,代码界的段子手,Bug界的终结者——程序猿小码。今天,咱们来聊聊 Spring Security 里让人又爱又恨的 CORS 配置。
话说这 CORS,就像一对异地恋的情侣,明明相爱,却总被各种阻碍。浏览器就是那个死板的家长,动不动就来一句“跨域请求?不行,不安全!”,搞得我们开发者焦头烂额。但是,为了真爱,为了让我们的前后端小情人能够顺利“私会”,我们必须搞定它!
第一幕:CORS 登场——何方妖孽?
首先,我们得认识一下 CORS 这位“家长”。CORS,全称 Cross-Origin Resource Sharing,跨域资源共享。简单来说,就是当你的前端(比如运行在 http://localhost:8080
)想去请求后端(比如运行在 http://localhost:9000
)的资源时,如果这两个地址的协议、域名、端口号任何一个不同,浏览器就会认为这是跨域请求。
为啥浏览器要管这事儿呢?这是为了安全!想象一下,如果没有 CORS 限制,一个恶意网站可以随意读取你其他网站的数据,那你的银行账户、个人信息岂不是要裸奔了?想想都可怕 😱!
第二幕:浏览器的心思—— preflight 请求是个啥?
浏览器在发起跨域请求时,并不是直接就冲上去,而是先试探一下,发一个叫 preflight request 的东西。这就像情侣约会前,男生先发个短信问问:“宝贝,今天有空吗?想去看电影吗?”
这个 preflight 请求是一个 OPTIONS
请求,它会带着一些头信息,告诉服务器:“嘿,我要跨域请求啦,我用的方法是 POST,我带的头是 Content-Type,你允许吗?”
服务器收到这个 preflight 请求后,如果允许这次跨域请求,就会返回一些 CORS 相关的响应头,告诉浏览器:“嗯,允许你跨域,你可以用 POST 方法,你可以带 Content-Type 头。”
如果服务器不允许这次跨域请求,就会返回一个错误,浏览器看到错误后,就会阻止这次跨域请求。
第三幕:Spring Security 的 CORS 策略——各有千秋
Spring Security 提供了多种方式来配置 CORS,就像给情侣提供了多种约会方案,总有一款适合你。
-
方案一:全局配置——一劳永逸的爱情
这种方式就像父母直接认可了这段恋情,以后随便你们怎么玩,我都支持!在 Spring Boot 中,我们可以通过配置
application.properties
或application.yml
来实现全局 CORS 配置。spring: mvc: cors: allowed-origins: "http://localhost:8080,http://127.0.0.1:8080" # 允许的来源 allowed-methods: "GET,POST,PUT,DELETE,OPTIONS" # 允许的请求方法 allowed-headers: "*" # 允许的请求头 exposed-headers: "Authorization" # 允许暴露的响应头 allow-credentials: true # 允许携带 Cookie max-age: 3600 # preflight 请求的缓存时间,单位秒
表格:全局 CORS 配置详解
配置项 含义 举例 allowed-origins
允许跨域请求的来源。可以是一个具体的域名,也可以是 *
表示允许所有来源(不推荐在生产环境中使用*
)。多个来源可以用逗号分隔。"http://localhost:8080,http://127.0.0.1:8080"
allowed-methods
允许跨域请求的方法。常用的方法有 GET
、POST
、PUT
、DELETE
、OPTIONS
。"GET,POST,PUT,DELETE,OPTIONS"
allowed-headers
允许跨域请求携带的请求头。可以是一个具体的请求头名称,也可以是 *
表示允许所有请求头。"*"
或"Content-Type,Authorization"
exposed-headers
允许浏览器访问的响应头。默认情况下,浏览器只能访问一些标准的响应头,如果服务器返回了一些自定义的响应头,并且希望浏览器能够访问这些响应头,就需要在这里配置。 "Authorization"
allow-credentials
是否允许携带 Cookie。如果设置为 true
,则表示允许跨域请求携带 Cookie。注意:如果设置为true
,则allowed-origins
不能设置为*
,必须指定具体的域名。true
或false
max-age
preflight 请求的缓存时间,单位秒。浏览器会缓存 preflight 请求的结果,在缓存时间内,相同的跨域请求不会再发送 preflight 请求。 3600
优点: 配置简单,一次配置,全局生效。
缺点: 所有接口都应用相同的 CORS 策略,不够灵活。
-
方案二:
@CrossOrigin
注解——自由恋爱的宣言这种方式就像情侣自己决定是否要在一起,比较自由。我们可以在 Controller 的方法或者类上使用
@CrossOrigin
注解来配置 CORS。import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController @CrossOrigin(origins = "http://localhost:8080", allowedHeaders = "*") public class MyController { @GetMapping("/hello") public String hello() { return "Hello, CORS!"; } }
优点: 可以针对不同的接口配置不同的 CORS 策略,更加灵活。
缺点: 需要在每个需要配置 CORS 的接口上添加注解,比较繁琐。
-
方案三:
WebMvcConfigurer
——定制化的爱情合约这种方式就像情侣签订了一份详细的爱情合约,约定了各种细节,非常灵活。我们可以通过实现
WebMvcConfigurer
接口来定制 CORS 配置。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 CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") // 匹配的路径 .allowedOrigins("http://localhost:8080") // 允许的来源 .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的请求方法 .allowedHeaders("*") // 允许的请求头 .exposedHeaders("Authorization") // 允许暴露的响应头 .allowCredentials(true) // 允许携带 Cookie .maxAge(3600); // preflight 请求的缓存时间 } }
优点: 可以集中管理 CORS 配置,更加清晰,也更加灵活。
缺点: 需要编写额外的配置类,稍微复杂一些。
-
方案四:
CorsFilter
——拦截式的爱情守护这种方式就像一个忠实的保镖,拦截所有的请求,然后根据配置的 CORS 策略来处理。我们可以使用 Spring 提供的
CorsFilter
来实现 CORS 配置。import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import java.util.Arrays; @Configuration public class CorsFilterConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:8080")); corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); corsConfiguration.setAllowedHeaders(Arrays.asList("*")); corsConfiguration.setAllowCredentials(true); corsConfiguration.setMaxAge(3600L); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", corsConfiguration); // 匹配所有路径 return new CorsFilter(source); } }
优点: 可以灵活地配置 CORS 策略,并且可以与其他 Filter 配合使用。
缺点: 需要编写额外的配置类,稍微复杂一些。
第四幕:Cookie 的秘密——信任的基石
CORS 中,Cookie 是一个非常重要的概念。如果你的跨域请求需要携带 Cookie,那么你需要做以下几件事:
-
在服务器端,设置
allow-credentials
为true
。 -
在客户端(比如 JavaScript),设置
withCredentials
为true
。fetch('http://localhost:9000/api/hello', { method: 'GET', credentials: 'include' // 或者 'same-origin' }) .then(response => response.text()) .then(data => console.log(data));
注意: 如果你设置了
allow-credentials
为true
,那么allowed-origins
不能设置为*
,必须指定具体的域名。这是因为安全原因,如果允许所有来源携带 Cookie,那么就可能存在安全风险。
第五幕:调试 CORS——拨开迷雾见真相
CORS 问题有时候很隐蔽,让人摸不着头脑。这时候,我们需要一些调试技巧来拨开迷雾见真相。
- 使用浏览器的开发者工具: 浏览器的开发者工具可以查看请求的头信息和响应头信息,以及 CORS 相关的错误信息。
- 使用 Postman 或其他 API 客户端: Postman 可以模拟各种类型的 HTTP 请求,并且可以方便地查看请求头和响应头。
- 查看服务器端的日志: 服务器端的日志可以记录 CORS 相关的请求和响应信息,帮助你定位问题。
第六幕:最佳实践——幸福的秘诀
最后,我们来总结一下 Spring Security CORS 配置的最佳实践,让我们的前后端小情人能够幸福地在一起。
- 尽量使用
WebMvcConfigurer
或CorsFilter
来配置 CORS,这样可以集中管理 CORS 配置,更加清晰。 - *不要在生产环境中使用 `allowed-origins: ""`,要指定具体的域名。**
- 如果需要携带 Cookie,一定要设置
allow-credentials
为true
,并且指定具体的allowed-origins
。 - 使用浏览器的开发者工具和服务器端的日志来调试 CORS 问题。
总结:
CORS 配置虽然有点复杂,但是只要我们理解了它的原理,掌握了各种配置方式,就可以轻松搞定它。记住,CORS 就像一个负责任的家长,它虽然会阻碍我们的跨域请求,但也是为了保护我们的安全。
希望今天的分享能够帮助到大家,让大家在 Spring Security 的 CORS 配置中少走弯路,早日实现前后端和谐共处的美好愿景!
好了,今天的分享就到这里,感谢大家的收听!如果大家觉得有用,记得点赞、评论、转发哦!我们下期再见! 👋