參考文章:html
Cookie跨域
因爲HTTP
是一種無狀態的協議,服務器單從網絡鏈接上無從知道客戶身份。怎麼辦呢?就給客戶端們頒發一個通行證吧,每人一個,不管誰訪問都必須攜帶本身通行證。這樣服務器就能從通行證上確認客戶身份了。這就是。 cookie
指的就是瀏覽器裏面能永久存儲的一種數據,僅僅是瀏覽器實現的一種數據存儲功能。瀏覽器
Cookie
其實是一小段的文本信息。客戶端請求服務器,若是服務器須要記錄該用戶狀態,就使用response
向客戶端瀏覽器頒發一個Cookie
。客戶端瀏覽器會把Cookie
保存起來。當瀏覽器再請求該網站時,瀏覽器把請求的網址連同該Cookie
一同提交給服務器。服務器檢查該Cookie
,以此來辨認用戶狀態。服務器還能夠根據須要修改Cookie
的內容。安全
注意:Cookie
功能須要瀏覽器的支持。若是瀏覽器不支持Cookie
(如大部分手機中的瀏覽器)或者把Cookie
禁用了,Cookie
功能就會失效。不一樣的瀏覽器採用不一樣的方式保存Cookie
。IE
瀏覽器會以文本文件形式保存,一個文本文件保存一個Cookie
。bash
Cookie
具備不可跨域名性。根據Cookie
規範,瀏覽器訪問Google
只會攜帶Google
的Cookie
,而不會攜帶Baidu
的Cookie
。瀏覽器判斷一個網站是否能操做另外一個網站Cookie
的依據是域名。服務器
Sessioncookie
Session
是另外一種記錄客戶狀態的機制,不一樣的是Cookie
保存在客戶端瀏覽器中,而Session
保存在服務器上。客戶端瀏覽器訪問服務器的時候,服務器把客戶端信息以某種形式記錄在服務器上。這就是Session
。客戶端瀏覽器再次訪問時只須要從該Session
中查找該客戶的狀態就能夠了。
若是說Cookie
機制是經過檢查客戶身上的「通行證」來肯定客戶身份的話,那麼Session
機制就是經過檢查服務器上的「客戶明細表」來確認客戶身份。
session
也是相似的道理,服務器要知道當前發請求給本身的是誰。爲了作這種區分,服務器就要給每一個客戶端分配不一樣的「身份標識」,而後客戶端每次向服務器發請求的時候,都帶上這個「身份標識」,服務器就知道這個請求來自於誰了。對於瀏覽器客戶端,你們都默認採用 cookie
的方式,保存這個「身份標識」。
服務器使用session
把用戶的信息臨時保存在了服務器上,用戶離開網站後session
會被銷燬。這種用戶信息存儲方式相對cookie
來講更安。
但是session
有一個缺陷:若是web
服務器作了負載均衡,那麼下一個操做請求到了另外一臺服務器的時候session
會丟失。
提示:Session
的使用比Cookie
方便,可是過多的Session
存儲在服務器內存中,會對服務器形成壓力。
Cookie與Session的區別和聯繫
cookie
數據存放在客戶的瀏覽器上,session
數據放在服務器上;
cookie
不是很安全,別人能夠分析存放在本地的COOKIE
並進行COOKIE
欺騙,考慮到安全應當使用session
;
session
會在必定時間內保存在服務器上。當訪問增多,會比較佔用你服務器的性能。考慮到減輕服務器性能方面,應當使用COOKIE
;
單個cookie在客戶端的限制是3K,就是說一個站點在客戶端存放的COOKIE不能超過3K;
Cookie
和Session
的方案雖然分別屬於客戶端和服務端,可是服務端的session
的實現對客戶端的cookie
有依賴關係的,上面我講到服務端執行session
機制時候會生成session
的id值,這個id
值會發送給客戶端,客戶端每次請求都會把這個id
值放到http
請求的頭部發送給服務端,而這個id
值在客戶端會保存下來,保存的容器就是cookie
,所以當咱們徹底禁掉瀏覽器的cookie
的時候,服務端的session
也會不能正常使用。
在大多數使用Web API
的互聯網公司中,tokens
是多用戶下處理認證的最佳方式。
如下幾點特性會讓你在程序中使用基於Token的身份驗證
1.無狀態、可擴展
2.支持移動設備
3.跨程序調用
4.安全
在介紹基於Token
的身份驗證的原理與優點以前,不妨先看看以前的認證都是怎麼作的。
咱們都是知道HTTP
協議是無狀態的,這種無狀態意味着程序須要驗證每一次請求,從而辨別客戶端的身份。
在這以前,程序都是經過在服務端存儲的登陸信息來辨別請求的。這種方式通常都是經過存儲Session
來完成。
1.Seesion
:每次認證用戶發起請求時,服務器須要去建立一個記錄來存儲信息。當愈來愈多的用戶發請求時,內存的開銷也會不斷增長。
2.可擴展性:在服務端的內存中使用Seesion
存儲登陸信息,伴隨而來的是可擴展性問題。
3.CORS
(跨域資源共享):當咱們須要讓數據跨多臺移動設備上使用時,跨域資源的共享會是一個讓人頭疼的問題。在使用Ajax
抓取另外一個域的資源,就能夠會出現禁止請求的狀況。
4.CSRF
(跨站請求僞造):用戶在訪問銀行網站時,他們很容易受到跨站請求僞造的攻擊,而且可以被利用其訪問其餘的網站。
在這些問題中,可擴展行是最突出的。所以咱們有必要去尋求一種更有行之有效的方法。
基於Token的身份驗證是無狀態的,咱們不將用戶信息存在服務器中。這種概念解決了在服務端存儲信息時的許多問題。NoSession
意味着你的程序能夠根據須要去增減機器,而不用去擔憂用戶是否登陸。
1.用戶經過用戶名和密碼發送請求。
2.服務器端程序驗證。
3.服務器端程序返回一個帶簽名的token
給客戶端。
4.客戶端儲存token
,而且每次訪問API
都攜帶Token
到服務器端的。
5.服務端驗證token
,校驗成功則返回請求數據,校驗失敗則返回錯誤碼。
在客戶端存儲的Tokens
是無狀態的,而且可以被擴展。基於這種無狀態和不存儲Session
信息,負載負載均衡器可以將用戶信息從一個服務傳到其餘服務器上。 tokens
本身hold
住了用戶的驗證信息。
請求中發送token
而再也不是發送cookie
可以防止CSRF
(跨站請求僞造)。即便在客戶端使用cookie
存儲token
,cookie
也僅僅是一個存儲機制而不是用於認證。不將信息存儲在Session
中,讓咱們少了對session
操做。
token
是有時效的,一段時間以後用戶須要從新驗證。
Tokens
可以建立與其它程序共享權限的程序。
咱們提早先來談論一下CORS
(跨域資源共享),對應用程序和服務進行擴展的時候,須要介入各類各類的設備和應用程序。
對於這個問題,咱們不妨先看兩個例子。一個例子是登陸密碼,通常要求按期改變密碼,以防止泄漏,因此密碼是有有效期的;另外一個例子是安全證書。SSL
安全證書都有有效期,目的是爲了解決吊銷的問題。因此不管是從安全的角度考慮,仍是從吊銷的角度考慮,Token
都須要設有效期。
只能說,根據系統的安全須要,儘量的短,但也不能短得離譜
Token
過時失效了,要求用戶從新登陸……用戶體驗豈不是很糟糕?一種方案,使用 Refresh Token
,它能夠避免頻繁的讀寫操做。這種方案中,服務端不須要刷新 Token
的過時時間,一旦 Token
過時,就反饋給前端,前端使用 Refresh Token
申請一個全新Token
繼續使用。這種方案中,服務端只須要在客戶端請求更新 Token
的時候對 Refresh Token
的有效性進行一次檢查,大大減小了更新有效期的操做,也就避免了頻繁讀寫。固然 Refresh Token
也是有有效期的,可是這個有效期就能夠長一點了,好比,以天爲單位的時間。
使用 Token
和 Refresh Token
的時序圖以下:
1)登陸
2)業務請求 3)Token
過時,刷新
Token
上面的時序圖中並未提到
Refresh Token
過時怎麼辦。不過很顯然,
Refresh Token
既然已通過期,就該要求用戶從新登陸了。
使用基於 Token
的身份驗證方法,在服務端不須要存儲用戶的登陸記錄。大概的流程是這樣的:
1.前端使用用戶名跟密碼請求首次登陸
2.後服務端收到請求,去驗證用戶名與密碼是否正確
3.驗證成功後,服務端會根據用戶id
、用戶名、定義好的祕鑰、過時時間生成一個 Token
,再把這個 Token
發送給前端
4.前端收到 返回的Token
,把它存儲起來,好比放在 Cookie
裏或者 Local Storage
裏
export interface User {
token: string;
userInfo: UserInfo | any;
companyInfo: CompanyInfo | any;
resources?: string[];
}
複製代碼
save(key: string, value: any, storageType ?: StorageType) {
return this.storageService.put(
{
pool: key,
key: 'chris-app',
storageType: StorageType.localStorage
},
value
);
}
this.storageService.save(CACHE_USER_KEY, user);
複製代碼
5.前端每次路由跳轉,判斷 localStroage
有無 token
,沒有則跳轉到登陸頁。有則請求獲取用戶信息,改變登陸狀態; 6.前端每次向服務端請求資源的時候須要在請求頭裏攜帶服務端簽發的Token
HttpInterceptor => headers = headers.set('token', this.authService.getToken());
複製代碼
7.服務端收到請求,而後去驗證前端請求裏面帶着的 Token
。沒有或者 token
過時,返回401
。若是驗證成功,就向前端返回請求的數據。
8.前端獲得 401
狀態碼,重定向到登陸頁面。
HttpInterceptor =>
401: '用戶登錄狀態失效,請從新登錄。'
複製代碼