JavaWeb跨域問題常用解決辦法彙總
技術標籤:跨域
一.問題丟擲
前後端除錯過程中,瀏覽器經常出現下面的錯誤,出現Access-Control-Allow-Origin的字眼,就是跨域問題
二.什麼是跨域
當一個請求url的協議、域名、埠三者之間任意一個與當前頁面url不同即為跨域
當前頁面url | 被請求頁面url | 是否跨域 | 原因 |
http://www.test.com/ | http://www.test.com/index.html | 否 | 同源(協議、域名、埠號相同) |
http://www.test.com/ | https://www.test.com/index.html | 跨域 | 協議不同(http/https) |
http://www.test.com/ | http://www.baidu.com/ | 跨域 | 主域名不同(test/baidu) |
http://www.test.com/ | http://blog.test.com/ | 跨域 | 子域名不同(www/blog) |
http://www.test.com:8080/ | http://www.test.com:7001/ | 跨域 | 埠號不同(8080/7001) |
三.為什麼會出現跨域問題
出於瀏覽器的同源策略限制。同源策略(Sameoriginpolicy)是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能都會受到影響。可以說Web是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。同源策略會阻止一個域的javascript指令碼和另外一個域的內容進行互動。所謂同源(即指在同一個域)就是兩個頁面具有相同的協議(protocol),主機(host)和埠號(port)
非同源限制:
【1】無法讀取非同源網頁的 Cookie、LocalStorage 和 IndexedDB
【2】無法接觸非同源網頁的 DOM
【3】無法向非同源地址傳送 AJAX 請求
四.跨域常用解決方法
1.JSONP
JSONP 是伺服器與客戶端跨源通訊的常用方法。最大特點就是簡單適用,相容性好(相容低版本IE),缺點是隻支援get請求,不支援post請求。
核心思想:網頁通過新增一個<script>元素
,向伺服器請求 JSON 資料,伺服器收到請求後,將資料放在一個指定名字的回撥函式的引數位置傳回來
- jQuery ajax:
$.ajax({ url: 'http://www.test.com:8080/login', type: 'get', dataType: 'jsonp', // 請求方式為jsonp jsonpCallback: "handleCallback", // 自定義回撥函式名 data: {} });
- Vue.js
this.$http.jsonp('http://www.domain2.com:8080/login', {
params: {},
jsonp: 'handleCallback'
}).then((res) => {
console.log(res);
})
2.CORS
CORS 是跨域資源分享(Cross-Origin Resource Sharing)的縮寫。它是 W3C 標準,屬於跨源 AJAX 請求的根本解決方法。
1、普通跨域請求:只需伺服器端設定Access-Control-Allow-Origin
2、帶cookie跨域請求:前後端都需要進行設定
前端設定:
根據xhr.withCredentials欄位判斷是否帶有cookie
-
jQuery ajax
$.ajax({
url: 'http://www.test.com:8080/login',
type: 'get',
data: {},
xhrFields: {
withCredentials: true // 前端設定是否帶cookie
},
crossDomain: true, // 會讓請求頭中包含跨域的額外資訊,但不會含cookie
});
-
vue-resource
Vue.http.options.credentials = true
-
axios
axios.defaults.withCredentials = true
服務端設定:
伺服器端對於CORS的支援,主要是通過設定Access-Control-Allow-Origin來進行的。如果瀏覽器檢測到相應的設定,就可以允許Ajax進行跨域的訪問。
- Java後臺
/*
* 匯入包:import javax.servlet.http.HttpServletResponse;
* 介面引數中定義:HttpServletResponse response
*/
// 允許跨域訪問的域名:若有埠需寫全(協議+域名+埠),若沒有埠末尾不用加'/'
response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com");
// 允許前端帶認證cookie:啟用此項後,上面的域名不能為'*',必須指定具體的域名,否則瀏覽器會提示
response.setHeader("Access-Control-Allow-Credentials", "true");
// 提示OPTIONS預檢時,後端需要設定的兩個常用自定義頭
response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With");
3.nginx代理
使用Nginx轉發請求。把跨域的介面寫成調本域的介面,然後將這些介面轉發到真正的請求地址。
安裝好Nginx後,修改nginx.conf檔案,把預設的server配置註釋掉。
在下面增加:
server{
# 監聽9099埠
listen 9099;
# 域名是localhost
server_name localhost;
#凡是localhost:9099/api這個樣子的,都轉發到真正的服務端地址http://localhost:9871
location ^~ /api {
proxy_pass http://localhost:9871;
}
}
為什麼不直接監聽9871,因為nginx監聽9871,就會佔用此埠,本地開發就不能以9871埠啟用
4.vue代理
在vue開發中實現跨域:在vue專案根目錄下找到vue.config.js檔案(如果沒有該檔案則自己建立),在proxy中設定跨域
devServer: {
proxy: { //配置跨域
'/api': {
target: 'http://121.121.67.254:8185/', //這裡後臺的地址模擬的;應該填寫你們真實的後臺介面
changOrigin: true, //允許跨域
pathRewrite: {
/* 重寫路徑,當我們在瀏覽器中看到請求的地址為:http://localhost:8080/api/core/getData/userInfo 時
實際上訪問的地址是:http://121.121.67.254:8185/core/getData/userInfo,因為重寫了 /api
*/
'^/api': ''
}
},
}
},
在建立axios例項的時候將baseURL設定為/api ,這時候我們的跨域就已經完成了。
// 建立axios例項
const service = axios.create({
baseURL: /api, // api的base_url
timeout: 60000 // 請求超時時間
})
在vue中使用proxy進行跨域的原理是:將域名傳送給本地的伺服器(啟動vue專案的服務,loclahost:8080),再由本地的伺服器去請求真正的伺服器。
問:proxyTable 裡面的pathRewrite裡面的‘^/api’:'' 什麼意思?
答:用代理, 首先你得有一個標識, 告訴他你這個連線要用代理. 不然的話, 可能你的 html, css, js這些靜態資源都跑去代理. 所以我們只要介面用代理, 靜態檔案用本地.
'/
api': {}
, 就是告訴node
, 我介面只要是'/
api'
開頭的才用代理.所以你的介面就要這麼寫/
api/xx/xx
. 最後代理的路徑就是http://xxx.xx.com/
api/xx/xx
.
可是不對啊, 我正確的介面路徑裡面沒有/
api啊. 所以就需要pathRewrite
,用''^/
api'':''
, 把'/
api'
去掉, 這樣既能有正確標識, 又能在請求介面的時候去掉api.
5.nginx配置
location ~* \.(eot|ttf|woff)$ {
add_header Access-Control-Allow-Origin '*';
}
Access-Control-Allow-Origin:*表示允許任何域名跨域訪問。
擴充套件
server {
listen 80;
server_name uedbox.com;
location / {
# Simple requests
if ($request_method ~* "(GET|POST)") {
add_header "Access-Control-Allow-Origin" *;
}
# Preflighted requests
if ($request_method = OPTIONS ) {
add_header "Access-Control-Allow-Origin" *;
add_header "Access-Control-Allow-Methods" "GET, POST, OPTIONS, HEAD";
add_header "Access-Control-Allow-Headers" "Authorization, Origin, X-Requested-With, Content-Type, Accept";
return 200;
}
....
# Handle request
....
}
}
還可以動態配置跨域方案
set $cors '';
if ($http_origin ~ '^https?://(localhost|www\.yourdomain\.com|www\.yourotherdomain\.com)') {
set $cors 'true';
}
if ($cors = 'true') {
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;
# required to be able to read Authorization header in frontend
#add_header 'Access-Control-Expose-Headers' 'Authorization' always;
}
if ($request_method = 'OPTIONS') {
# Tell client that this pre-flight info is valid for 20 days
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}