一 概述
同源策略是web安全策略中的一種,也是非常重要的一種,同源策略明確規定了不同域的客戶端在沒有被明確的授權的情況下,不能讀寫對方的資源。、
二 同源分析
同源是指請求的URI(統一資源標識符),網絡協議,主機名(域名),端口號相同。但是IE瀏覽器同源不包括端口號。
例如URL:http://www.example.com:8080/test/index.html,分析一下幾種同源情況:
URL | 是否同源 | 結果分析 |
http://www.example.com:8080/test/index1.html | 同源 | 相同協議,域名,端口 |
http://www.example.com:8080/index.html | 同源 | 相同協議,域名,端口 |
http://www.example.com/test/index.html | 看情況 | 看瀏覽器,默認8080端口或者IE忽略端口的情況下同源 |
https://www.example.com:8080/test/index.html | 不同源 | 不同協議 |
http://www.example.com:80/test/index.html | 看情況 | 看瀏覽器,IE忽略端口的情況下同源 |
http://www.apple.com:8080/index.html | 不同源 | 不同域名 |
三 同源策略的對非同源的主要限制範圍
- Cookie,LocalStorage和IndexDB無法讀取。
- DOM無法獲得。
- AJAX請求無法發送。
這些限制對於安全方面是很有必要的,但是實際開發過程中,會搭建服務其集羣,會把客戶端頁面放到一個單獨的服務器中,此時就會出現不同源的情況。這時候如果想要不同源的網站之間進行數據交互,我們需要使用到跨域請求。
四 跨域請求方案
跨域請求報錯:
XMLHttpRequest cannot load http://localhost:8080/test.json. No 'Access-control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8081' is therefore not allowed access.
1 JSONP(JSON with Padding)
JSONP可以解決主流瀏覽器的跨域數據訪問問題,實際上js是不可以請求數據但是可以跨域請求js腳本。其原理爲服務端返回一個定義好的js函數的調用 ,並且將服務器的數據封裝成js函數參數的形式傳遞過來,最後執行函數返回後端數據,這個方法需要前後端配合,而且JSONP僅支持Get請求。
var URL_Serv:"http://localhost:8080/test.json";
/**
*使用ajax內置方法getJSONP返回js腳本實現跨域請求
*將請求的服務端數據URL_Serv作爲js方法的參數
*自定義的回調函數test.getDataService。
*test.getDataService(test.json)。
**/
$.getJSONP(this.URL_Serv, test.getDataService)
2. CORS跨域資源共享
跨域請求的字段及其含義:
Access-Control-Allow-Origin
該字段是必須的。它的值要麼是請求時
Origin
字段的值,要麼是一個*
,表示接受任意域名的請求。/*服務器端 Access-Control-Allow-Credentials = true時,參數Access-Control-Allow- Origin 的值不能爲'*',如果origin.equals('*'),表示所有域名都可以,但跨域不支持cookie。oirgin可以設置特定的域名如:www.example.com,進行跨域請求。*/
Access-Control-Allow-Credentials
該字段可選。它的值是一個布爾值,表示是否允許發送Cookie。默認情況下,Cookie不包括在CORS請求之中。設爲
true
,即表示服務器明確許可,Cookie可以包含在請求中,一起發給服務器。這個值也只能設爲true
,如果服務器不要瀏覽器發送Cookie,刪除該字段即可。
Access-Control-Expose-Headers
該字段可選。CORS請求時,
XMLHttpRequest
對象的getResponseHeader()
方法只能拿到6個基本字段:Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
。如果想拿到其他字段,就必須在Access-Control-Expose-Headers
裏面指定。上面的例子指定,getResponseHeader('FooBar')
可以返回FooBar
字段的值。
Access-Control-Allow-Methods
該字段必需,它的值是逗號分隔的一個字符串,表明服務器支持的所有跨域請求的方法。注意,返回的是所有支持的方法,而不單是瀏覽器請求的那個方法。這是爲了避免多次"預檢"請求。
Access-Control-Allow-Headers
如果瀏覽器請求包括
Access-Control-Request-Headers
字段,則Access-Control-Allow-Headers
字段是必需的。它也是一個逗號分隔的字符串,表明服務器支持的所有頭信息字段,不限於瀏覽器在"預檢"中請求的字段。
Access-Control-Max-Age
該字段可選,用來指定本次預檢請求的有效期,單位爲秒。上面結果中,有效期是20天(1728000秒),即允許緩存該條迴應1728000秒(即20天),在此期間,不用發出另一條預檢請求。
XMLHttpRequest是一個JavaScript內置對象,使得JavaScript可以進行異步的HTTP通信。新版的XMLHttpRequest對象,可以向不同域名的服務器發送Http請求。這叫做“跨域資源共享”(Cross-origin resource sharing,簡稱CORS)。
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
實現跨域的前提:1.瀏覽器支持這個功能(兼容IE10+)。2.服務器必須允許這種跨域。
服務器支持跨域過濾前端請求基於註解的代碼,添加請求響應頭:
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class HeaderFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletResquest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
Strign origin = request.getHeader("origin");
//允許那些域訪問
response.setHeader("Access-Control-Allow-Origin", origin);
//允許那些請求方法
response.setHeader("Access-Control-Allow-Methods", "POST,GET");
//是否攜帶Cookie
response.setHeader("Access-Control-Allow-Credentials", "true");
//它表明了這個詢問結果的有效期,後面瀏覽器在有效期內也可以不必再次詢問
response.setHeader("Access-Control-Max-Age", "3600");
//允許那些請求頭字段
response.setHeader("Access-Control-Allow-Headers", "Content-Type,
Access-Control-Allow-Headers, Authorization, X-Requested-With");
filterChain.doFilter(servletRequest,response);
}
@Override
public void destroy() {
}
}
CORS的具體實現流程:
- 瀏覽器發送跨域請求(實際上瀏覽器不會直接發送請求,而是現發送一個不帶任何參數的試探請求OPTIONS,檢測服務器是否允許跨域,如果允許跨域同時設置的請求和響應頭一致,纔會發送第二次具體的請求。)
- 服務端接收到跨域請求之後,在響應頭中添加Access-Control-Allow-Origin Header資源權限配置,發送響應。
- 瀏覽器收到響應後,查看是否設置了header('Access-Control-Allow-Origin:請求源域名或者*');如果當前域已經授權了,則將結果返回給瀏覽器,否則瀏覽器忽略此次響應。
實際上,跨域行爲是瀏覽器行爲,響應回來的是瀏覽器安全機制的限制,對跨域響應內容進行了忽略。服務器與服務器之間並不存在跨域問題。
五 JSONP與CORS的比較
- JSONP能夠支持老版本的瀏覽器,兼容性比較好,而CORS需要瀏覽器支持CORS纔行。
- JSONP僅僅支持GET請求,發送的數據量有限,使用比較麻煩。
- CORS使用簡單,只要服務單設置允許跨域,對於客戶端來說,同一般的GET,POST請求類似,沒有什麼區別。
跨域的安全行問題:因爲跨域是需要前端同服務端配合控制的,這樣無論是JSONP還是CORS,如果沒有服務端的允許,瀏覽器都是無法實現跨域的。
提示:微服務項目工作中,可以採用一個獨立的模塊管理不同的請求避免跨域訪問,實際上跨域訪問是存在一定的風險的。