系列目錄html
1.1 建立jquery
1.2 完善git
二. 搭建項目總體架構github
三. 集成輕量級ORM框架——SqlSugarweb
3.1 搭建環境ajax
3.2 實戰篇:利用SqlSugar快速實現CRUDjson
四. 集成JWT受權驗證設計模式
五. 實現CORS跨域
源碼下載:https://github.com/WangRui321/RayPI_V2.0
(新增的跨域部分的代碼尚未更新上去,但一共就只有15行代碼,須要的徹底能夠本身編寫。等晚上有時間再去git更新下,地址不變~)
先從一個最最根本的問題開始,咱們爲何要「跨域」?
答案很簡單,用一句話就能夠歸納:跨域的惟一目的,就是要繞過「同源策略」的限制。
根據百度百科:
同源策略(Same origin policy)是一種約定,它是瀏覽器最核心也最基本的安全功能,若是缺乏了同源策略,則瀏覽器的正常功能可能都會受到影響。能夠說整個Web都是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。
同源策略是由Netscape提出的一個著名的安全策略,所謂同源是指協議、域名、端口相同。如今全部支持JavaScript的瀏覽器都會使用這個策略。
能夠看出,同源策略是一種Web安全策略,它限制了一個域內發起的請求只能訪問它所處當前域內的資源,而不能訪問其餘域內的資源。好比,有兩個網站,A和B,在同源策略下,網站A的js就不能夠訪問網站B的資源(好比接口)。
與其說咱們「運用」同源策略,不如說是咱們「遵照」同源策略。就像交通規則同樣,它雖然限制了每一個人的自由,但同時也保證了每一個人的相對安全。
判斷是否同源有三個要素,咱們暫且稱它們爲「同源三要素」:
1)協議
好比http(超文本傳輸協議)或https(安全套接字層超文本傳輸協議)。若是協議不一樣,則必定不一樣源。
2)域名
好比www.cnblogs.com。若是域名不一樣,則必定不一樣源。
3)端口
好比www.raywang.com:8080和www.raywang:8081,它們一個爲8080端口,一個爲8081端口,因此它們不一樣源。
若是兩個資源時同源的,那麼它們必須同時知足這個條件都相同。舉幾個例子:
https://www.raywang.com與http://www.raywang.com不一樣源,由於它們協議不一樣。
https://www.raywang.com與http://www.baidu.com不一樣源,由於它們域名不一樣。
https://www.raywang.com:8080與https://www.raywang.com:8081不一樣源,由於它們端口不一樣。
以api設計模式開發的系統,前端頁面和後端接口通常都是分離了。在開發初期,後端接口可能被髮布到某臺服務器上,而前端頁面可能被搭建在開發人員本地的iis中,就算是項目上線後,頁多是前端頁面和後臺服務發佈到兩臺不一樣的服務器上。固然,這些狀況都是不符合同源策略的。
拿咱們正在搭建的web api爲例,我這裏寫了一個測試用的小網頁,代碼以下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <script src="js/jquery-2.1.4.js"></script> <script> function GetToken() { var tokenModel = { id: $("#tid").val(), name: $("#tname").val(), sub: $("#tsub").val() }; $.ajax({ url: "http://localhost:3607/api/System/Token", type: "get", dataType: "json", data: tokenModel, async: false, success: function (d) { alert(JSON.stringify(d)); $("#jwt").val(d); }, error: function (d) { alert(JSON.stringify(d)); $("#jwt").val(JSON.stringify(d)); } }); } function GetStudent() { var s = { name: $("#sname").val() }; $.ajax({ url: "http://localhost:3607/api/Client/Student/GetByName", type: "get", dataType: "json", data: s, async: false, headers: { "Authorization": "Bearer " + $("#jwt").val().trim() }, success: function (d) { alert(JSON.stringify(d)); $("#student").val(JSON.stringify(d)); }, error: function (d) { alert(JSON.stringify(d)); $("#student").val(JSON.stringify(d)); } }); } </script> </head> <body> <div style="width:350px; margin:100px auto 0;"> I D:<input type="text" id="tid" value="1" /><br /> Name:<input type="text" id="tname" value="張三" /><br /> Sub :<input type="text" id="tsub" value="Client" /><br /> <input type="button" value="獲取Token" onclick="GetToken()" /><br /> <br /> <p>token:</p> <textarea id="jwt" style="width:300px; height:200px; "></textarea> <br /> 學生姓名:<input type="text" id="sname" value="張三" /> <input type="button" value="點擊查詢" onclick="GetStudent()" /> <br /> <textarea id="student" style="width:300px;height:200px;"></textarea> </div> </body> </html>
我把它放到RayPI下的wwwroot下,
運行以後,瀏覽器訪問是這樣的:
其中「獲取Token」按鈕會調用接口http://localhost:3607/api/System/Token,「點擊查詢」按鈕會調用接口http://localhost:3607/api/Client/Student/GetByName,結果以下:
此時,頁面http://localhost:3607/index.html和接口http://localhost:3607/api/System/Token,http://localhost:3607/api/Client/Student/GetByName是同源的(由於同在一個項目中),因此它們之間能夠互相訪問傳遞資源(json),沒有任何問題。
下面咱們將這個網頁單獨拿出來發佈到我本地的iis中,域名取爲http://localhost:8083/index.html
點擊按鈕,結果以下
顯然,此時這個前端頁面與後端接口是非同源的,因此它不能訪問接口資源。
解決跨域的方法不少,好比運用代理跨域,window.name+iframe跨域。
可是最經常使用的跨域方式應該是兩種:JSONP和CORS跨域。
RayPI選擇的跨域方式是後者。
CORS,即Cross-Origin Resource Sharing,跨源資源共享。
CORS將跨域請求分紅如下兩種:
一個簡單的請求大體知足以下條件:
HEAD
GET
POST
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type
,但僅能是下列之一
application/x-www-form-urlencoded
multipart/form-data
text/plain
任何一個不知足上述要求的請求,即被認爲是複雜請求。
一個完整的CORS跨域請求的流程是這樣的:
先別慌,圖片看着多,實際上是三塊內容,咱們把它分爲左、中、右三部分,從最左邊開始看起。
1)左:基礎
一個http請求被髮起以後,瀏覽器會根據是不是跨域請求(判斷標準就是上面說的同源策略三要素),決定是否在http請求的頭部添加「Origin」字段,並將發起請求的域名附加在該字段後面。
服務器接收到客服端的http請求後,會先嚐試讀取「Origin」字段,若是不存在,說明該請求不是跨域請求,直接將該請求放行,把結果返回給客戶端;
若是頭部存在「Origin」字段,說明該請求來自和當前服務器不一樣源的一個客戶端,是一個跨域請求。這時先判斷該「Origin」字段後的域名和請求方式(request method)是否合法,若是不合法,就直接返回403錯誤碼。若是是合法的,再根據上面說的原則判斷該請求是簡單請求仍是複雜請求。若是是簡單請求,就進入圖片中間簡單請求流程,若是是複雜請求,就進入圖片最右邊複雜請求流程。
2)中:簡單請求
服務器端根據本身設置的CORS跨域規則,配置相應的Access-Control-Allow-Origin和Access-Control-Allow-Methods等響應頭信息。
當收到客戶端的請求後,服務端將驗證客戶端請求頭中的信息是否符合設置的CORS規則,若是符合,則將請求的資源連同跨域響應頭(Access-Control-Allow-Origin等)返回給客戶端。
客服端(瀏覽器)收到Response Headers後,會驗證這些響應頭信息,判斷是否經過了跨域請求,若是經過,則返回狀態碼200,併成功獲取到跨域資源:
若是服務端收到客戶端的請求後,發現客戶端請求頭中的信息不符合設置的CORS規則,這時則不會講配置的跨域響應頭(Access-Control-Allow-Origin等)返回給客戶端。
客戶端收到Response Headers後,驗證跨域響應頭信息,沒有發現相應的跨域響應頭,說明跨域請求不經過,將不會返回資源。(這裏理論上應該返回錯誤碼403的,可是Chrome顯示的是200狀態碼,我也不知道是爲啥。。。)
能夠經過瀏覽器的Console查看具體的驗證失敗緣由:
3)右:複雜請求
一個複雜請求不只有包含通訊內容的請求,同時也包含預請求(preflight request)。
瀏覽器發現是複雜請求的時候,並不會直接發起原請求,而是先發送Preflight requests(預先驗證請求),Preflight requests是一個OPTION請求,用於詢問服務器是否容許當前域名下的頁面發起跨域請求。
以下例,點擊「點擊查詢」後,須要向接口傳遞token驗證(接口的受權驗證是前一章講的內容),所該請求是一個複雜跨域請求。以下圖:
OPTIONS請求頭部中通常會包含如下頭部:Origin、Access-Control-Request-Method、Access-Control-Request-Headers。
服務器收到OPTIONS請求後,設置Access-Control-Allow-Origin、Access-Control-Allow-Method、Access-Control-Allow-Headers頭部與瀏覽器溝通來判斷是否容許這個請求。
若是Preflight requests驗證經過,瀏覽器纔會發送真正的跨域請求。
若是Preflight requests驗證失敗,瀏覽器則不會發送真正的跨域請求。(理論上應該返回403錯誤碼,可是這裏仍是返了200。。。)
能夠經過瀏覽器的Console查看具體的驗證失敗緣由:
原理了然,下面就開始實現了。
方法有兩個,程序實現和服務器實現。
打開主項目下的Startup.cs文件,編輯ConfigureServices函數
代碼以下:
#region CORS services.AddCors(c => { c.AddPolicy("AllowAnyOrigin", policy => { policy.AllowAnyOrigin()//容許任何源 .AllowAnyMethod()//容許任何方式 .AllowAnyHeader()//容許任何頭 .AllowCredentials();//容許cookie }); c.AddPolicy("AllowSpecificOrigin", policy => { policy.WithOrigins("http://localhost:8083") .WithMethods("GET", "POST", "PUT", "DELETE") .WithHeaders("authorization"); }); }); #endregion
這裏添加了兩個策略,AllowAnyOrigin策略幾乎直接徹底無視了「同源策略」的限制,因此我的建議儘可能不要這麼寫。AllowSpecificOrigin策略我暫時只放了一個測試用的源,若有須要可根據狀況更改。
下面只須要在控制器頭上(或某個函數頭上)添加標識:
[EnableCors("AllowSpecificOrigin")]
這裏我用的IIS做爲例子,方法以下:
在iis新建網站,添加RayPI,點擊IIS下的「HTTP響應標頭」
右側店家添加,依次添加以下表頭信息:
Access-Control-Allow-Origin : * Access-Control-Allow-Methods : GET,POST,PUT,DELETE,HEAD,OPTIONS Access-Control-Allow-Headers : authorization
F5運行程序,從http://localhost:8083/進行測試,結果以下
跨域訪問成功~
參考內容:
https://blog.csdn.net/u014344668/article/details/54948546
https://blog.csdn.net/badboyer/article/details/51261083