使用CORS解决跨域问题

        后端接口和前端分离的时候,很多情况下会遇到跨域问题。这是浏览器的同源策略导致的,同源策略是为了Web安全提出的,说的是两个不同源的网址默认是不能请求对方的接口的。不同源包含:协议(http|https)、ip/域名、端口之一不同就是不同源。不同源的网页请求接口都要遵循浏览器的同源策略。

       解决跨域问题有两种方案,都需要服务端支持才可以。

       一种是JSONP,基本意思是通过在网页上动态添加一个script标签,在这个标签中去请求接口,带上一个函数名,在网页存在,这个是不用遵循同源策略的,这个接口返回中用函数名包裹,页面就可以回调此函数得到真正的数据。JSONP只支持GET方法。

      另外一种是CORS(Cross Origin Resource Share),跨域资源共享。这是跨域问题的终极解决方案。支持所有的方法。CORS对于简单请求和非简单请求采取不同的处理方案。简单请求是满足以下条件的请求,不满足的都是非简单请求。

(1) 请求方法是以下三种方法之一:

HEAD
GET
POST
(2)HTTP的头信息不超出以下几种字段:

Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。

下面是一个例子,浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin字段。
 

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面的头信息中,Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段,就知道出错了,从而抛出一个错误,被XMLHttpRequestonerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

对于非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

下面针对Nginx和Java代码作出服务端的CORS配置:

Nginx:

if ( $request_method = 'OPTIONS' ) {
	add_header Access-Control-Allow-Origin 'http://somedomain:port';
add_header Access-Control-Allow-Origin '$http_origin';
	add_header Access-Control-Allow-Methods 'POST,GET,PUT,OPTIONS,DELETE';
	add_header Access-Control-Max-Age 3600;
	add_header Access-Control-Allow-Headers 'Origin,X-Requested-With,Content-Type,Accept,Authorization,sourcetype,token';
	add_header Access-Control-Allow-Credentials true;
	add_header Content-Length 0;
	add_header Content-Type text/plain;
	return 200;
}
add_header Access-Control-Allow-Origin 'http://somedomain:port';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, PUT, POST, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type,*';

add_header Access-Control-Allow-Origin '$http_origin';//这样就能全部了,*是不行的

静态资源设置允许跨域,否则canvas使用不行
img.crossOrigin = 'anonymous'; //可选值:anonymous,*
服务端nginx
server {
        listen       80;
        add_header 'Access-Control-Allow-Origin' '*';
        location /Roboto/ {
            root   /home/images;
            autoindex on;
        }
}

在SpringBoot中可以通过设置Filter或者WebMvcConfigurerAdapter#addCorsMappings(CorsRegistry registry)做配置

先定义CORS每个配置项

@ConfigurationProperties(prefix = "cors")
public class CorsProperties {
    private String[] allowedOrigins;
    private String[] allowedMethods;
    private String[] allowedHeaders;
    private String[] exposedHeaders;
    private long maxAge;
    private boolean allowCredentials;


    public String[] getAllowedOrigins() {
        return allowedOrigins;
    }

    public void setAllowedOrigins(String[] allowedOrigins) {
        this.allowedOrigins = allowedOrigins;
    }

    public String[] getAllowedMethods() {
        return allowedMethods;
    }

    public void setAllowedMethods(String[] allowedMethods) {
        this.allowedMethods = allowedMethods;
    }

    public String[] getAllowedHeaders() {
        return allowedHeaders;
    }

    public void setAllowedHeaders(String[] allowedHeaders) {
        this.allowedHeaders = allowedHeaders;
    }

    public String[] getExposedHeaders() {
        return exposedHeaders;
    }

    public void setExposedHeaders(String[] exposedHeaders) {
        this.exposedHeaders = exposedHeaders;
    }

    public long getMaxAge() {
        return maxAge;
    }

    public void setMaxAge(long maxAge) {
        this.maxAge = maxAge;
    }

    public boolean isAllowCredentials() {
        return allowCredentials;
    }

    public void setAllowCredentials(boolean allowCredentials) {
        this.allowCredentials = allowCredentials;
    }
}

配置文件配置:


#cors.allowedOrigins=http://113.204.233.35:21128
cors.allowedOrigins=*
cors.allowedMethods=POST,GET,PUT,DELETE,OPTIONS,HEAD
#cors.allowedHeaders=Origin,X-Requested-With,Content-Type,Accept,Authorization,token,userId,userCode,userName,user
cors.allowedHeaders=*
cors.maxAge=3600
cors.allowCredentials=true

通过CorsFilter配置:

@Configuration
public class CorsConfig {
    @Autowired
    private CorsProperties corsProperties;
 
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig());
        return new CorsFilter(source);
    }

    private CorsConfiguration buildConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        for (String origin : corsProperties.getAllowedOrigins()) {
            corsConfiguration.addAllowedOrigin(origin);
        }
        for (String method : corsProperties.getAllowedMethods()) {
            corsConfiguration.addAllowedMethod(method);
        }
        for (String header : corsProperties.getAllowedHeaders()) {
            corsConfiguration.addAllowedHeader(header);
        }
        if(ArrayUtil.isNotEmpty(corsProperties.getExposedHeaders())){
            for (String header : corsProperties.getExposedHeaders()) {
                corsConfiguration.addExposedHeader(header);
            }
        }
        corsConfiguration.setMaxAge(corsProperties.getMaxAge());
        corsConfiguration.setAllowCredentials(corsProperties.isAllowCredentials());
        return corsConfiguration;
    }
}

参考文件:

http://www.ruanyifeng.com/blog/2016/04/cors.html

https://blog.csdn.net/w1316022737/article/details/100010505

https://blog.csdn.net/weixin_36276193/article/details/83870241?depth_1.utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

https://blog.csdn.net/bingospunky/article/details/80136164?depth_1.utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

https://blog.csdn.net/zhengshengnan123/article/details/90400255?depth_1.utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

https://blog.csdn.net/u012988901/article/details/97395556?depth_1.utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

发布了245 篇原创文章 · 获赞 204 · 访问量 111万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章