Spring支持的特性之一(跨域请求处理)
Spring官方文档: https://spring.io/blog/2015/06/08/cors-support-in-spring-framework
本节主题:注解@CrossOrigin
专业解释:
出于安全考虑,浏览器会限制脚本中发起的跨站请求。比如,使用 XMLHttpRequest 对象发起 HTTP 请求就必须遵守同源策略(same-origin policy)。 具体而言,Web 应用程序能且只能使用 XMLHttpRequest 对象向其加载的源域名发起 HTTP 请求,而不能向任何其它域名发起请求
我的解释:
出于安全原因,浏览器禁止Ajax调用驻留在当前原点之外的资源。例如,当你在一个标签中检查你的银行账户时,你可以在另一个选项卡上的网站。来自这个网站的脚本不能够对你的银行API做出Ajax请求(从你的帐户中取出钱!)即使使用您的凭据。
但有时候我们又需要这么做,最典型的就是前后端分离后,前端调用后端就是跨域请求,为了能够避免浏览器同源策略的限制。我们采用以下方式实现:
跨源资源共享(CORS)是由大多数浏览器实现的W3C规范,允许您灵活地指定什么样的跨域请求被授权,而不是使用一些不太安全和不太强大的策略,如iframe或jsonp等。
跨域请求的趋势:在当今的 Web 开发中,使用跨站 HTTP 请求加载各类资源(包括CSS、图片、JavaScript 脚本以及其它类资源),已经成为了一种普遍且流行的方式。
Spring Framework 4.2 GA才为CORS提供了支持,使您比通常的基于过滤器的解决方案更容易和更强大地配置它。所以springMVC的版本要在4.2或以上版本才支持@CrossOrigin
备注:
在WEB项目中,如果我们想支持CORS,一般都要通过过滤器进行实现,可以定义一些基本的规则,但是不方便提供更细粒度的配置,如果你想参考过滤器实现,你可以阅读下面这篇文章:
http://my.oschina.net/huangyong/blog/521891
4.5 解决跨域问题 部分有介绍
全局跨域请求配置:(多个类中都使用相同的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 MyCorsConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
// addMapping 参数为路径模式 其他参数与注解方式中属性相同
//其中* 表示匹配到下一层;** 表示后面不管有多少层,都能匹配。
registry.addMapping("*")// addMapping("/api/**")
.allowedOrigins("")
.allowedHeaders("")
.allowCredentials(true);
}
}
如果使用Spring Boot,建议将WebMvcConfigurer bean声明如下形式更优雅些:减少不必要的依赖关系
@Configuration
public class MyConfiguration {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
};
}
}
测试跨域请求 加深理解:
如果注解配置和配置类配置 都存在时: 会对两者的属性进行合并,合并规则是,
对于数组属性会对两者属性进行合并,allowCredentials属性以注解属性为准,不存在时取配置类的。
maxAge属性优先注解属性为准,不存在时取配置类的。
我们还可以研究一下 @CrossOrigin放在类上和方法上都存在时 采用的合并策略和上面的配置类类似:
如果我们直接写 @CrossOrigin 不配置属性,里面有那些属性和默认值
先通过源码看看该注解支持的属性
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
String[] DEFAULT_ORIGINS = { "*" };
String[] DEFAULT_ALLOWED_HEADERS = { "*" };
boolean DEFAULT_ALLOW_CREDENTIALS = true;
long DEFAULT_MAX_AGE = 1800;
/**
* 同origins属性一样
*/
@AliasFor("origins")
String[] value() default {};
/**
* 所有支持域的集合,例如"http://domain1.com"。
* <p>这些值都显示在请求头中的Access-Control-Allow-Origin
* "*"代表所有域的请求都支持
* <p>如果没有定义,所有请求的域都支持
* @see #value
*/
@AliasFor("value")
String[] origins() default {};
/**
* 允许请求头中的header,默认都支持
*/
String[] allowedHeaders() default {};
/**
* 响应头中允许访问的header,默认为空
*/
String[] exposedHeaders() default {};
/**
* 请求支持的方法,例如"{RequestMethod.GET, RequestMethod.POST}"}。
* 默认支持RequestMapping中设置的方法
*/
RequestMethod[] methods() default {};
/**
* 是否允许cookie随请求发送,使用时必须指定具体的域
*/
String allowCredentials() default "";
/**
* 预请求的结果的保存有效期,默认30分钟
*/
long maxAge() default -1;
}
源码部分截图:
如果你对这些属性还是不太理解 推荐阅读:
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
我们基于百度首页做测试:
准备后端代码 并启动服务:
@RestController
public class TestController {
// 只允许origin是 https://www.baidu.com 的跨域请求
@CrossOrigin(origins = "https://www.baidu.com")
@GetMapping("/testCrossOrigin")
public String testCrossOrigin(){
return "CORS TEST !";
}
}
打开浏览器测试:
https://www.baidu.com/ 点击后F12 打开console控制台:输入
var xhr = new XMLHttpRequest(); xhr.open("GET","http:localhost:8080/testCrossOrigin",true); xhr.send();
就是在百度首页下 发起Ajax请求:
后端只有 http://localhost:8080/testCrossOrigin请求配置跨域注解,其他跨域请求都会报错,
下图展示了正确配置 http://localhost:8080/testCrossOrigin的请求结果正常:
这里我们对正常的跨域请求 主要关注请求头和响应头的 Origin 、Access-Control-Allow-Origin 属性特点:属性值相同。
我们不基于百度首页进行测试 发现报错:说明我们的配置只能针对百度首页有效。
总结 :
客户端发起跨域请求CORS 时在请求头设置Origin属性,springmvc接收请求后会根据跨域配置注解的属性判断
是否允许访问以及访问路径模式是否正确,通过后就会在响应头内设置Access-Control-Allow-Origin 的值取值orgin的值。标识可以正常访问
拓展:实战问题
spring注解@CrossOrigin不起作用的原因
1、是springMVC的版本要在4.2或以上版本才支持@CrossOrigin
2、非@CrossOrigin没有解决跨域请求问题,而是不正确的请求导致无法得到预期的响应,导致浏览器端提示跨域问题。
3、在Controller注解上方添加@CrossOrigin注解后,仍然出现跨域问题,解决方案之一就是:
在@RequestMapping注解中没有指定Get、Post方式,具体指定后,问题解决。
项目中使用Spring Security如何配置CORS
请确保在Spring安全级别启用CORS,并允许Spring Security利用Spring MVC级别定义的配置
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()...
}
}
基于过滤器的CORS支持:SpringBoot常用
Spring框架还提供了CorsFilter。在这种情况下,不用使用@CrossOrigin或
WebMvcConfigurer#addCorsMappings(CorsRegistry)
例如,可以在Spring Boot应用程序中声明如下的过滤器:
@Configuration
public class MyConfiguration {
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
}
源码层面:
CORS请求(包括预选的带有选项方法)被自动发送到注册的各种HandlerMapping(控制器方法) 。它们处理CORS准备请求并拦截CORS预检请求和实际请求,这得益于CorsProcessor实现。搭建有空可以研究一下。本篇主题是原理与使用 就不扯到底层源码了。
测试题:都配置了maxAge (这个特殊)那么 哪一个生效?
测试验证在类上的注解生效。maxAge =100
来源:oschina
链接:https://my.oschina.net/u/4321806/blog/4500760