Spring MVC(7)CORS

一 简介

同源策略(same origin policy)是浏览器安全的基石。在同源策略的限制下,非同源的网站之间不能发送 ajax请求的。
为了解决这个问题,w3c 提出了跨源资源共享,即CORS([Cross-Origin Resource Sharing](https://www.w3.org/TR/cors/))。

CORS 做到了两点:

  1. 不破坏即有规则
  2. 服务器实现了 CORS 接口,就可以跨源通信

基于这两点,CORS 将请求分为两类:简单请求和非简单请求。

CORS 出现前的情况

跨源时能够通过script或者image标签触发 GET 请求或通过表单发送一条 POST 请求,但这两种请求 HTTP 头信息中都不能包含任何自定义字段

二 简单请求

请求方法是HEADGETPOST 且 HTTP 头信息不超过以下几个字段:Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type(只限于 application/x-www-form-urlencoded、multipart/form-data、text/plain)

例如:

GET /test HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, sdch, br
Origin: http://www.examples.com
Host: www.examples.com

CORS处理策列

后台处理

请求时,在头信息中添加一个 Origin 字段,服务器收到请求后,根据该字段判断是否允许该请求。

  1. 如果允许,则在 HTTP 头信息中添加Access-Control-Allow-Origin字段,并返回正确的结果
  2. 如果不允许,则不在头信息中添加 Access-Control-Allow-Origin 字段

浏览器处理

浏览器先于用户得到返回结果,根据有无 Access-Control-Allow-Origin字段来决定是否拦截该返回结果。

CORS 的出现,没有对”旧的“服务造成任何影响。

描述 CORS 返回结果

Access-Control-Allow-Credentials: 可选,用户是否可以发送、处理 cookie
Access-Control-Expose-Headers:可选,可以让用户拿到的字段。有几个字段无论设置与否都可以拿到的,包括:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。

三 非简单请求

预检请求

对于非简单请求的跨源请求,浏览器会在真实请求发出前,增加一次OPTION请求,称为预检请求(preflight request)。预检请求将真实请求的息,包括请求方法、自定义头字段、源信息添加到 HTTP 头信息字段中,询问服务器是否允许这样的操作。
比如对于 DELETE 请求:

OPTIONS /test HTTP/1.1
Origin: http://www.examples.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: X-Custom-Header
Host: www.examples.com

与 CORS 相关的字段有:
Access-Control-Request-Method: 真实请求使用的 HTTP 方法
Access-Control-Request-Headers: 真实请求中包含的自定义头字段

处理策略

后台处理

服务器收到请求时,需要分别对 Origin、Access-Control-Request-Method、Access-Control-Request-Headers 进行验证,验证通过后,会在返回 Http 头信息中添加

Access-Control-Allow-Origin: http://www.examples.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000

他们的含义分别是:
Access-Control-Allow-Methods: 真实请求允许的方法
Access-Control-Allow-Headers: 服务器允许使用的字段
Access-Control-Allow-Credentials: 是否允许用户发送、处理 cookie
Access-Control-Max-Age: 预检请求的有效期,单位为秒。有效期内,不会重复发送预检请求

四 配置 CORS

一个应用可能会有多个 CORS 配置,并且可以设置每个 CORS 配置针对一个接口一系列接口或者对所有接口生效。
举例来说,我们需要:

  1. 让 /test 接口支持跨源访问,而 /test/1 或 /api 等其它接口不支持跨源访问
  2. 让 /test/* 这一类接口支持跨源访问,而 /api 等其它接口不支持跨源访问
  3. 站点所有的接口都支持跨源访问

针对某个接口

可以在方法上添加 CrossOrigin 注解:


	
	@CrossOrigin(origins = {"http://localhost:8083", "null"})
	@RequestMapping("/t1")
	public String test() {
		return "{\"project\":\"just a test\"}";
	}

针对一系列接口

可以在上添加 CrossOrigin 注解:

@RestController
@CrossOrigin(origins = {"http://localhost:8083", "null"})
@RequestMapping("/test")
public class TestController {

添加全局配置

添加一个配置类

package com.zyc.cros;

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

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:9000", "null")
                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
                .maxAge(3600)
                .allowCredentials(true);
    }
}

上面已经过时,最新版本的如下

package com.zyc.cros;

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

@Configuration
public class WebMvcConfg2 extends WebMvcConfigurationSupport {

	@Override
	protected void addCorsMappings(CorsRegistry registry) {
      registry.addMapping("/**")
      	.allowedOrigins("http://localhost:9000", "null")
      	.allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
      	.maxAge(3600)
      	.allowCredentials(true);
	}
	

}

添加 Filter 的方式

	@Bean
	public FilterRegistrationBean<CorsFilter> corsFilter() {
	    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
	    CorsConfiguration config = new CorsConfiguration();
	    config.setAllowCredentials(true);	
	    config.addAllowedOrigin("http://localhost:9000");
	    config.addAllowedOrigin("null");
	    config.addAllowedHeader("*");
	    config.addAllowedMethod("*");
//	    source.registerCorsConfiguration("/**", config); // CORS 配置对所有接口都有效
	    source.registerCorsConfiguration("/test/**", config);
	    FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
	    bean.setOrder(0);
	    return bean;
	}

建议第一种,第二种设置路径为’/test/**'后,前端不会报403错误。而是空的response。

五 原理

无论是通过哪种方式配置 CORS,其实都是在构造 CorsConfiguration。
一个 CORS 配置用一个 CorsConfiguration 类来表示,它的定义如下:

public class CorsConfiguration {
    private List<String> allowedOrigins;
    private List<String> allowedMethods;
    private List<String> allowedHeaders;
    private List<String> exposedHeaders;
    private Boolean allowCredentials;
    private Long maxAge;
}

Spring MVC 中对 CORS 规则的校验,都是通过委托给DefaultCorsProcessor 实现的。

六 携带cookie

axios默认是发送请求的时候不会带上cookie的,如果需要,则要设置withCredentials: true。
这时后台的Access-Control-Allow-Origin不能是*,必须指定明确的Origin值

那如果服务器被多个Origin不同的前端跨域访问呢?
可以动态的对response设置Access-Control-Allow-Origin
实现过滤器

public class LoginAuthFilter implements Filter {
    private static Logger logger = LoggerFactory.getLogger(LoginAuthFilter.class);
 
 
    @Override
    public void destroy() {
 
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse rep = (HttpServletResponse) response;
        // 设置允许多个域名请求
//        String[] allowDomains = {"http://www.toheart.xin","http://192.168.10.213:8080","http://localhost:8080"};
//        Set allowOrigins = new HashSet(Arrays.asList(allowDomains));
        String originHeads = req.getHeader("Origin");//这样处理,本质就是和*一样。允许所有的Origin访问,但是返回Origin是具体的路径
//        if(allowOrigins.contains(originHeads)){
//            //设置允许跨域的配置
//            // 这里填写你允许进行跨域的主机ip(正式上线时可以动态配置具体允许的域名和IP)
//            rep.setHeader("Access-Control-Allow-Origin", originHeads);
//        }
        rep.setHeader("Access-Control-Allow-Origin", originHeads);
 
 
        // 设置服务器允许浏览器发送请求都携带cookie
        rep.setHeader("Access-Control-Allow-Credentials","true");
        // 允许的访问方法
        rep.setHeader("Access-Control-Allow-Methods","POST, GET, PUT, OPTIONS, DELETE, PATCH");
        // Access-Control-Max-Age 用于 CORS 相关配置的缓存
        rep.setHeader("Access-Control-Max-Age", "3600");
        rep.setHeader("Access-Control-Allow-Headers","token,Origin, X-Requested-With, Content-Type, Accept,mid,X-Token");
        response.setCharacterEncoding("UTF-8");
//        response.setContentType("application/json; charset=utf-8");
        chain.doFilter(request, response);
    }
 
    @Override
    public void init(FilterConfig arg0) throws ServletException {
 
    }
 
 
}

添加fileter

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new DateConverter());
    }
}

参考

  1. Spring MVC 与 CORS
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值