使用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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章