2021-01-14
背景:
同域:域名(父域名和子域名都相同),端口,協議都相同
跨域:非同域的請求
?
問題:
瀏覽器上,我們訪問127.0.0.1:80,但是127.0.0.1:80 會去請求127.0.0.1:81的數據(比如js文件,ajax請求等),此時80訪問81會出現跨域問題,但我們瀏覽器能直接訪問81的數據。
?
注意:
跨域不是請求發不出去,而是服務端正常返回結果后被瀏覽器攔截返回結果。(瀏覽器為了防止非同源的請求 拿到服務器的返回數據結果)
?
解決辦法:
跨域有2種請求,瀏覽器對于2種請求的處理不一樣
簡單請求:
請求方法只能是:HEAD,GET,POST
主動設置的頭信息不能超過以下字段:Accept,Accept-Language,Content-Language,Last-Event-ID,Content-Type(只限于三個值application/x-www-form-urlencoded、multipart/form-data、text/plain)
注意:這里的頭信息是指我們主動設置的頭部信息,在查看請求過程時會發現 瀏覽器會在頭信息里設置Origin等信息,這些不算主動設置的。
非簡單請求:不能同時滿足簡單請求的2個條件
?
?
簡單請求:
瀏覽器會在 請求頭信息 里增加一個Origin字段,表明本次請求的來源,若服務器返回的頭部信息里需包含?Access-Control-Allow-Origin并包含Origin?則瀏覽器正常返回數據,否則出現跨域錯誤。
如果需要攜帶cookie,請求時需設置(比如XMLHttpRequest.withCredentials = true)。若返回頭部信息有?Access-Control-Allow-Credentials:true,則瀏覽器會正常返回數據,否則瀏覽器會攔截結果報 跨域錯誤。
注意:服務器會正常返回數據,只是瀏覽器攔截了結果。
服務器在返回頭信息里設置(必須):
Access-Control-Allow-Origin: Origin? ? ? ?
Eg:Access-Control-Allow-Origin:127.0.0.1:80
如果想設置匹配所有的Origin且不帶cookie的,可以設置:
Access-Control-Allow-Origin: *
如果需要帶Cookie,需設置:
Access-Control-Allow-Credentials:true
如果想匹配所有的Origin且帶cookie:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: 請求的Origin(從request獲取后填入)
千萬不能同時設置Credentials=true且Origin=*,瀏覽器會報錯:
has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute
例如服務器nginx配置案例:
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Origin $http_origin;
?
非簡單請求:
常見的情況是請求方法是PUT? 或者??Content-Type字段類型是application/json?或者? ?頭信息里自定義了屬性
過程:
此時瀏覽器將請求分成2步:預檢請求? ?+? 簡單請求
預檢請求:真正請求前增加一次預檢(preflight)請求(請求方法是OPTIONS),瀏覽器進行校驗,如果返回狀態碼是2XX表示驗證通過
簡單請求:預檢通過后,發送簡單請求到服務器(瀏覽器校驗Access-Control-Allow-Origin和Origin是否匹配 +??Access-Control-Allow-Credentials 和 需要攜帶Cookie 相匹配?)。
?
預檢校驗:
請求方法:OPTIONS
Header里增加:
Access-Control-Request-Headers(若單獨設置了header,比如 resource:tom)
Access-Control-Request-Method:GET(例如真正的請求的請求方式是GET)
Origin:請求的域
?
瀏覽器通過和Response里的相對應的內容進行對比(例如返回PUT,DELETE,請求的是PUT,則校驗通過)
返回的Methods默認是包括 簡單請求 里的HEAD,POST,GET請求的(所以返回的里面填入*,也只能匹配到HEAD GET POST,不能匹配PUT等)
返回的Headers默認是包括 簡單請求 里的幾個字段
千萬注意:返回里面設置Methods 或者 Headers為 * 不是代表匹配到任意的內容
注:
預檢請求返回時,服務器可以額外配置Access-Control-Max-Age:xxx(單位秒),表示在此時間內請求不再發出另一條預檢請求。
?
例如服務器nginx里配置跨域(針對OPTIONS請求直接返回2XX):
location /file {
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin $http_origin;
? ? ? ? add_header Access-Control-Allow-Methods $http_access_control_request_method;
? ? ? ? add_header Access-Control-Allow-Credentials true;
? ? ? ? add_header Access-Control-Allow-Headers $http_access_control_request_method;
? ? ? ? add_header Access-Control-Max-Age 1728000;
?return 204;
? ? }
?
后記:
同源策略是瀏覽器保護用戶的措施,防止第三方網站請求拿到返回的數據(比如cookie和請求的返回結果)。
針對現在前后端分離,一般會在?后端/nginx?設置僅允許?前端的IP/域名?才能跨域請求拿到結果。
?
?
?
nginx里完整的跨域配置:
? ? server {
? ? ? ? ?listen ?80 default_server;
? ? ? ? ?server_name _;??
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Origin $http_origin;
?
? ? ? ? ? ? ?
location /file {
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin $http_origin;
? ? ? ? add_header Access-Control-Allow-Methods $http_access_control_request_method;
? ? ? ? add_header Access-Control-Allow-Credentials true;
? ? ? ? add_header Access-Control-Allow-Headers $http_access_control_request_headers;
? ? ? ? add_header Access-Control-Max-Age 1728000;
? return 204;
}? ?
}
}
?Spring里跨域配置:
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true); //支持cookie 跨域
config.setAllowedOrigins(Arrays.asList("*"));
config.setAllowedHeaders(Arrays.asList("*"));
config.setAllowedMethods(Arrays.asList("*"));
config.setMaxAge(300L);//設置時間有效
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
?
注:這里設置AllowCredentials為*沒問題,是因為程序里做了處理(在CorsConfiguration類里):
?
參考:http://www.ruanyifeng.com/blog/2016/04/cors.html
前端跨域測試:https://blog.csdn.net/qq_35720307/article/details/83616682
總結
以上是生活随笔為你收集整理的2021-01-14的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: thinkphp6集成JWT
- 下一篇: 微信小程序登陆