目錄
現(xiàn)象
原因
瀏覽器同源策略
導(dǎo)致結(jié)果:
解決方案
跨源資源共享(CORS)
各個(gè)端解決方法:
后端:
方式1:重載WebMvcConfigurer方法
方式2:配置監(jiān)聽(tīng)CorsFilter
方式3:相關(guān)類(lèi)上加注解?@CrossOrigin
注意事項(xiàng):
Nginx解決:
情況1:
前端解決:
現(xiàn)象
本人身份:后端
今天部署線上環(huán)境前端代碼時(shí),發(fā)生了如下報(bào)錯(cuò):
Access to XMLHttpRequest at 'http://192.168.1.11:8081/api/v1/sys/auth/login' from origin 'http://192.168.1.8:8101' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
經(jīng)典的跨域問(wèn)題,=探究一下這個(gè)現(xiàn)象的本質(zhì)以及從3個(gè)端(后端、代理、前端)分別該怎么處理這個(gè)問(wèn)題
?這個(gè)問(wèn)題其實(shí)在開(kāi)發(fā)環(huán)境是不會(huì)出現(xiàn)的,當(dāng)把前端代碼打包好后部署到線上環(huán)境,用Nginx跑起來(lái)后,發(fā)生了這個(gè)問(wèn)題,發(fā)生這個(gè)問(wèn)題的根本原因是由于瀏覽器的同源策略
原因
瀏覽器同源策略
同源策略(Same Origin Policy)是一種安全策略,它是瀏覽器最核心也是最基本的安全功能。同源策略會(huì)阻止一個(gè)域的javascrip腳本和另一個(gè)域的內(nèi)容進(jìn)行交互,是用于隔離潛在惡意文件的關(guān)鍵安全機(jī)制;關(guān)于這一點(diǎn)我們后面會(huì)舉例說(shuō)明。如果缺少了同源策略瀏覽器的安全使用會(huì)受到很大的影響??梢哉f(shuō)web是構(gòu)建在同源策略基礎(chǔ)之上的,瀏覽器只是針對(duì)同源策略的一種實(shí)現(xiàn)。
同源的三個(gè)條件:
域名、協(xié)議、端口都相同
假設(shè) URL? A:?http://192.168.1.8:8080/
下列URL與A是否同源比較
B |
|
不同源,協(xié)議不同,一個(gè)是http,一個(gè)是https |
C | http://192.168.1.8:8081/ | 不同源,端口號(hào)不同 |
D | http://192.168.1.9:8080/ | 不同源,IP不同 |
E | http://192.168.1.8:8080/api? | 同源 |
導(dǎo)致結(jié)果:
- 不能獲取不同源的 cookie,LocalStorage 和 indexDB
- 不能獲取不同源的 DOM()
- 不能發(fā)送不同源的 ajax 請(qǐng)求 (可以向不同源的服務(wù)器發(fā)起請(qǐng)求,但是返回的數(shù)據(jù)會(huì)被瀏覽器攔截)
上述的現(xiàn)象就是因?yàn)?ajax請(qǐng)求其實(shí)已經(jīng)到了后端,后端也已經(jīng)處理,但是因?yàn)闉g覽器的同源策略導(dǎo)致拒絕接收數(shù)據(jù),所以其實(shí)這個(gè)現(xiàn)象在移動(dòng)端不會(huì)出現(xiàn),只會(huì)在瀏覽器出現(xiàn)
解決方案
其他方案如前端jsonp之類(lèi)的方法,都有或大或小的弊端,看了比較久,介紹下面的方法
跨源資源共享(CORS)
Cross-Origin Resource Sharing 跨資源共享,作為W3C的標(biāo)準(zhǔn),是一種基于?HTTP?頭的機(jī)制,該機(jī)制通過(guò)允許服務(wù)器標(biāo)示除了它自己以外的其它?origin(域,協(xié)議和端口),使得瀏覽器允許這些 origin 訪問(wèn)加載自己的資源??缭促Y源共享還通過(guò)一種機(jī)制來(lái)檢查服務(wù)器是否會(huì)允許要發(fā)送的真實(shí)請(qǐng)求,該機(jī)制通過(guò)瀏覽器發(fā)起一個(gè)到服務(wù)器托管的跨源資源的"預(yù)檢"請(qǐng)求。在預(yù)檢中,瀏覽器發(fā)送的頭中標(biāo)示有 HTTP 方法和真實(shí)請(qǐng)求中會(huì)用到的頭。是跨域ajax的根本解決方法,允許任何類(lèi)型的請(qǐng)求。
客戶(hù)端和服務(wù)器之間使用 CORS 首部字段來(lái)處理權(quán)限:
先看一個(gè)http的請(qǐng)求頭和響應(yīng)頭
請(qǐng)求頭:
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example
請(qǐng)求首部字段?Origin?表明該請(qǐng)求來(lái)源于?http://foo.example
。而現(xiàn)在的VUE項(xiàng)目中都會(huì)默認(rèn)帶上這個(gè)請(qǐng)求頭。
響應(yīng)頭:
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
服務(wù)端返回的?Access-Control-Allow-Origin: *
?表明,該資源可以被?任意?外域訪問(wèn),當(dāng)然,不建議配置 * ,當(dāng)響應(yīng)的是附帶身份憑證的請(qǐng)求時(shí),即withCredentials為true時(shí),服務(wù)端?必須?明確?Access-Control-Allow-Origin
?的值,而不能使用通配符“*
”。
注:
exposedHeaders:
這個(gè)是暴露的頭部列表,其中包含零個(gè)或多個(gè)頭部名稱(chēng),如果不設(shè)置,會(huì)出現(xiàn)你在F12的network里面能看到設(shè)置的響應(yīng)頭,但是前端仍然拿不到。多用于文件下載等需要拿到響應(yīng)頭中文件名稱(chēng)的場(chǎng)景下
各個(gè)端解決方法:
后端:
后端是基于Springboot開(kāi)發(fā),提供了這么幾種方式來(lái)配置 cros
為了靈活配置,先做一個(gè)配置文件:application-security.yml
lc:
cors:
allow-credential: true
allow-mapping: '/**'
allow-method: GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH
allow-origin-pattern: http://192.168.1.11:8101,http://localhost:3002
allow-header: '*'
exposed-header: content-disposition
max-age: 1800
properties文件
@Data
@ConfigurationProperties(prefix = "lc.cors")
public class CorsProperties {
@NotNull
private Boolean enable = true;
private String allowMapping;
private List<String> allowOriginPattern;
private List<String> allowMethod;
private List<String> allowHeader;
private List<String> exposedHeader;
@ApiModelProperty("單位:秒")
private Long maxAge;
private boolean allowCredential = true;
}
方式1:重載WebMvcConfigurer方法
這種我感覺(jué)是最合適的
@Configuration
@EnableConfigurationProperties(CorsProperties.class)
public class CorsConfig {
@Autowired
private CorsProperties corsProperties;
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping(corsProperties.getAllowMapping())
.allowedOriginPatterns(corsProperties.getAllowOriginPattern().toArray(new String[0]))
.allowCredentials(corsProperties.isAllowCredential())
.allowedMethods(corsProperties.getAllowMethod().toArray(new String[0]))
.allowedHeaders(corsProperties.getAllowHeader().toArray(new String[0]))
..exposedHeaders(corsProperties.getExposedHeader().toArray(new String[0]))
.maxAge(corsProperties.getMaxAge());
}
};
}
}
方式2:配置監(jiān)聽(tīng)CorsFilter
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOriginPattern("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setAllowCredentials(true);
return corsConfiguration;
}
方式3:相關(guān)類(lèi)上加注解?@CrossOrigin
@CrossOrigin
@Controller
@RequestMapping("/problem")
@ResponseBody
public class ProblemController implements Serializable {
}
注意事項(xiàng):
allow-origin-pattern這個(gè)屬性配置時(shí),盡量不要配置*,也許考慮到聯(lián)調(diào)和線上環(huán)境的ip都要添加上
Nginx解決:
nginx解決主要是這個(gè)思路,nginx的實(shí)際原理就是配置一個(gè)代理路徑替換實(shí)際的訪問(wèn)路徑,使得瀏覽器認(rèn)為訪問(wèn)的資源都是屬于相同協(xié)議,域名和端口的,而實(shí)際訪問(wèn)的并不是代理路徑,而是通過(guò)代理路徑找到實(shí)際路徑進(jìn)行訪問(wèn),所以,不妨將nginx看作是給瀏覽器的一種障眼法
情況1:
因?yàn)槭紫仍L問(wèn)的是nginx的index.html,這種情況是不會(huì)產(chǎn)生跨域問(wèn)題的,但是訪問(wèn)接口時(shí),因?yàn)槭钦{(diào)用的后端的接口,所以會(huì)產(chǎn)生跨域問(wèn)題,這時(shí)候,我們將所有的接口配置一個(gè)代理,轉(zhuǎn)發(fā)到實(shí)際的后端接口地址。
比如我們nginx配置的前端訪問(wèn)地址為:http://192.168.1.11:8080/api/
? ? ? ? ? ? ? ? ? ? ? ? ? ? ??后端的接口地址為:?http://192.168.1.11:8081/api/
下面是nginx的配置文件 nginx.conf,其實(shí)就是訪問(wèn)首頁(yè)時(shí),訪問(wèn)的還是index.html,但是訪問(wèn)具體的url時(shí),nginx幫我們做一層轉(zhuǎn)發(fā),這樣子就可以了。也不用加什么【add_header Access-Control-Allow-Origin *;】這樣的響應(yīng)頭,因?yàn)榇藭r(shí)nginx的代理會(huì)讓瀏覽器認(rèn)為是同源的路徑
server {
listen 8101;
client_max_body_size 20m;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /api/ {
proxy_pass http://192.168.1.11:8081/api/;
}
}
前端解決:
1、JSONP
這種方式需要前后端配合解決,已經(jīng)不推薦
2、代理
通過(guò)配置代理服務(wù),下面幾種方式
1、通過(guò)nginx來(lái)配置,需要相關(guān)技術(shù)棧
2、可以通過(guò) vue-cli來(lái)配置,vue.config.js文件文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-819366.html
devServer: {
proxy: 'http://192.168.1.111:8001'
}文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-819366.html
到了這里,關(guān)于瀏覽器同源策略導(dǎo)致跨域問(wèn)題 No ‘Access-Control-Allow-Origin‘ header 原因及解決方式--(后端、nginx、前端)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!