简介
基于安全的考虑,W3C
规范规定浏览器禁止访问不同域(origin)的资源,目前绝大部分浏览器遵循这一规范,从而衍生出了跨域资源共享 (CORS
)问题,相比于IFRAME
或JSONP
,CORS
更全面并且更安全,Spring Mvc
为我们提供了一套多粒度的CORS解决方案。
CORS介绍
关于CORS
的介绍,主要参考文章《Cross-Origin Resource Sharing (CORS)》
CORS
的工作原理是添加新的HTTP headers
来让服务器描述哪些源的请求可以访问该资源,对于可能对服务器造成不好影响的请求,规范规定浏览器需要先发送“预检”请求(也就是OPTION
请求),在预检请求通过后再发送实际的请求,服务器还可以通知客户端是否应该随请求发送“凭据”(例如 Cookie 和 HTTP 身份验证),更详细的介绍可以参考上面的文章,本文主要讨论Spring Mvc
对CORS
的支持。
需要注意的是:
- 简单请求
- 下列请求之一:
- HEAD
- GET
- POST
- 只包含下列头信息:
- Accept
- Accept-Language
- Content-Language
- Content-Type (只局限于application/x-www-form-urlencoded、multipart/form-data
、text/plain)
不需要发送”预检“请求
-
相关HTTP Header介绍
- Request Header
- Origin:请求的源信息(协议 + 域名 + 端口)
- Access-Control-Request-Method:预检请求类型
- Access-Control-Request-Headers:额外发送的Header信息
- Resonse Header
- Access-Control-Allow-Origin:服务器接受的源信息,“*”表示所有
- Access-Control-Allow-Methods:服务器支持的所有跨域请求类型,“*”表示所有
- Access-Control-Allow-Credentials:服务器是否接受Cookies和HTTP Authentication
- Access-Control-Expose-Headers:服务器暴露的额外的Header,“*”表示所有
- Access-Control-Max-Age:本次预检的有效期
- Request Header
-
Access-Control-Allow-Credentials设置为true时Access-Control-Allow-Origin不能设置为“*”
@CrossOrigin注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
@Deprecated
String[] DEFAULT_ORIGINS = {"*"};
@Deprecated
String[] DEFAULT_ALLOWED_HEADERS = {"*"};
@Deprecated
boolean DEFAULT_ALLOW_CREDENTIALS = false;
@Deprecated
long DEFAULT_MAX_AGE = 1800;
@AliasFor("origins")
String[] value() default {};
@AliasFor("value")
String[] origins() default {};
/**
* @since 5.3
*/
String[] originPatterns() default {};
String[] allowedHeaders() default {};
String[] exposedHeaders() default {};
RequestMethod[] methods() default {};
String allowCredentials() default "";
long maxAge() default -1;
}
复制代码
可以看见@CrossOrigin
注解可以标注在类或者方法上,其中几个常量如DEFAULT_ORIGINS
已经在Spring 5.0弃用,取而代之的是CorsConfiguration#applyPermitDefaultValues
方法。
@CrossOrigin
属性介绍
-
origins和value
支持的源,
origins
和value
都是相同的配置,互为别名,默认配置是“*”,表示服务器支持所有源的跨域请求,安全信息较低,最好根据实际情况设置对应的信息(协议 + 域名 + 端口)。 -
originPatterns
同样表示支持的源,
Spring 5.3
引入的属性,默认为空,与origins
二选一,支持通配符的形式配置origins
,比如https://*.domain1.com
,该字段为list,也就是可以配置多个。 -
allowedHeaders
允许跨域的请求头信息,默认为“*”表示允许所有的请求头,
cors
默认支持的请求头为:Cache-Control、Content-Language、Expires、Last-Modified、Pragma,如果你需要携带其他的请求头需要设置该属性。 -
exposedHeaders
服务器允许客户端访问的相应头,默认为空,表示只允许访问:Cache-Control、Content-Language、Expires、Last-Modified、Pragma,如果需要客户端访问其他的相应头需要设置该属性。
-
methods
服务器允许的Http Request类型,默认是允许
GET
、POST
、HEAD
,根据项目需要自行设置。 -
allowCredentials
浏览器是否需要把凭证(如:cookies、CSRF tokens)发送到服务器,默认是关闭的,因为该选项开启后会与配置的源建立高度信任的关系,并且还会暴露一些敏感信息,所以开始该选项时
origin
不允许设置为“*”。 -
maxAge
“预检”结果的缓存时间,单位是秒,默认1800s,在缓存时间内同一请求不需要“预检”请求。
@CrossOrigin用法
标注在类上,该类的所有方法均会生效
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
复制代码
同时也可以类和方法结合使用
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin("https://domain2.com")
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
复制代码
@CrossOrigin
注解比较适用于较细粒度的跨域控制,对于全局的跨域控制,Spring Mvc
提供了Global Configuration
配置。
Global Configuration
Spring Mvc
对于全局的CORS
比较简单,分为两个方案
实现WebMvcConfigurer
接口
创建WebConfig
类实现WebMvcConfigurer
接口,通过CorsRegistry
设置跨域信息
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://domain2.com")
.allowedMethods("PUT", "DELETE")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(true).maxAge(3600);
// Add more mappings...
}
}
复制代码
通过CorsFilter
通过CorsConfiguration
设置跨域信息,并将CorsConfiguration
通过CorsFilter
构造函数传递进去
@Bean
CorsFilter corsFilter(){
CorsConfiguration config = new CorsConfiguration();
// Possibly...
// config.applyPermitDefaultValues()
config.setAllowCredentials(true);
config.addAllowedOrigin("https://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
复制代码
总结
Spring Mvc
对于CORS
可以说是非常方便,本文主要是想让各位开发者对跨域有个整体的了解,各个参数代表的含义,而不是在所有项目中都一概而论的设置为“*”,要在自身项目的实际需求以及安全性上多做思考,防止生产事故。
有问题或发现错误欢迎评论留言或者邮箱yp.yang7@foxmail.com