登錄認(rèn)證幾乎是任何一個系統(tǒng)的標(biāo)配,web 系統(tǒng)、APP、PC 客戶端等,好多都需要注冊、登錄、授權(quán)認(rèn)證。
登錄是每個網(wǎng)站中都經(jīng)常用到的一個功能,在頁面上我們輸入賬號密碼,敲一下回車鍵,就登錄了,但這背后的登錄原理你是否清楚呢?今天我們就來介紹幾種常用的登錄方式。
Cookie + Session 登錄
Token 登錄
SSO 單點登錄
OAuth 第三方登錄
HTTP Basic Auth簡單點說明就是每次請求API時都提供用戶的username和password, 簡言之,Basic Auth是配合RESTful API 使用的最簡單的認(rèn)證方式,只需提供用戶名密碼即可,但由于有把用戶名密碼暴露給第三方客戶端的 風(fēng)險,在生產(chǎn)環(huán)境下被使用的越來越少。因此,在開發(fā)對外開放的RESTful API時,盡量避免采用HTTP Basic Auth
Cookie認(rèn)證機制就是為一次請求認(rèn)證在服務(wù)端創(chuàng)建一個Session對象,同時在客戶端的瀏覽器端創(chuàng)建了一個Cookie對象;通過客戶端帶上來Cookie對象來與服務(wù)器端的session對象匹配來實現(xiàn)狀態(tài)管理的。默認(rèn)的,當(dāng)我們關(guān)閉瀏覽器的時候,cookie會被刪除。但可以通過修改cookie 的expire time使cookie在一定時間內(nèi)有效
OAuth(開放授權(quán))是一個開放的授權(quán)標(biāo)準(zhǔn),允許用戶讓第三方應(yīng)用訪問該用戶在某一web服務(wù)上存儲的私密的資源(如照片,視頻,聯(lián)系人列表),而無需將用戶名和密碼提供給第三方應(yīng)用。OAuth允許用戶提供一個令牌,而不是用戶名和密碼來訪問他們存放在特定服務(wù)提供者的數(shù)據(jù)。每一個令牌授權(quán)一個特定的第三方系統(tǒng)(例如,視頻編輯網(wǎng)站)在特定的時段(例如,接下來的2小時內(nèi))內(nèi)訪問特定的資源(例如僅僅是某一相冊中的視頻)。這樣,OAuth讓用戶可以授權(quán)第三方網(wǎng)站訪問他們存儲在另外服務(wù)提供者的某些特定信息,而非所有內(nèi)容
使用基于 Token 的身份驗證方法,在服務(wù)端不需要存儲用戶的登錄記錄。大概的流程是這樣的:
客戶端使用用戶名跟密碼請求登錄
服務(wù)端收到請求,去驗證用戶名與密碼
驗證成功后,服務(wù)端會簽發(fā)一個 Token,再把這個 Token 發(fā)送給客戶端
客戶端收到 Token 以后可以把它存儲起來,比如放在 Cookie 里
客戶端每次向服務(wù)端請求資源的時候需要帶著服務(wù)端簽發(fā)的 Token
服務(wù)端收到請求,然后去驗證客戶端請求里面帶著的 Token,如果驗證成功,就向客戶端返回請求的數(shù)據(jù)
支持跨域訪問: Cookie是不允許垮域訪問的,這一點對Token機制是不存在的,前提是傳輸?shù)挠脩粽J(rèn)證信息通過HTTP頭傳輸.
無狀態(tài)(也稱:服務(wù)端可擴展行):Token機制在服務(wù)端不需要存儲session信息,因為Token自身包含了所有登錄用戶的信息,只需要在客戶端的cookie或本地介質(zhì)存儲狀態(tài)信息.
更適用CDN:可以通過內(nèi)容分發(fā)網(wǎng)絡(luò)請求你服務(wù)端的所有資料(如:javascript,HTML,圖片等),而你的服務(wù)端只要提供API即可.
去耦:不需要綁定到一個特定的身份驗證方案。Token可以在任何地方生成,只要在你的API被調(diào)用的時候,你可以進行Token生成調(diào)用即可.
更適用于移動應(yīng)用: 當(dāng)你的客戶端是一個原生平臺(iOS, Android,Windows 8等)時,Cookie是不被支持的(你需要通過Cookie容器進行處理),這時采用Token認(rèn)證機制就會簡單得多。
CSRF:因為不再依賴于Cookie,所以你就不需要考慮對CSRF(跨站請求偽造)的防范。
性能:一次網(wǎng)絡(luò)往返時間(通過數(shù)據(jù)庫查詢session信息)總比做一次HMACSHA256計算 的Token驗證和解析要費時得多.
不需要為登錄頁面做特殊處理: 如果你使用Protractor 做功能測試的時候,不再需要為登錄頁面做特殊處理.
基于標(biāo)準(zhǔn)化:你的API可以采用標(biāo)準(zhǔn)化的 JSON Web Token (JWT). 這個標(biāo)準(zhǔn)已經(jīng)存在多個后端庫(.NET,Ruby,Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft).
以一個電商系統(tǒng),假設(shè)淘寶為例,如果我們想要下單,首先需要注冊一個賬號。擁有了賬號之后,我們需要輸入用戶名(比如手機號或郵箱)、密碼完成登錄過程。之后如果你在一段時間內(nèi)再次進入系統(tǒng),是不需要輸入用戶名和密碼的,只有在連續(xù)長時間不登錄的情況下(例如一個月沒登錄過)訪問系統(tǒng),再次需要輸入用戶名和密碼。如果使用頻率很頻繁,通常是一年都不用再輸一次密碼,所以經(jīng)常在換了一臺電腦或者一部手機之后,一些經(jīng)常使用的網(wǎng)站或 APP 不記得密碼了。
提煉出來整個過程大概就是如下幾步:
首次使用,需要通過郵箱或手機號注冊;
注冊完成后,需要提供用戶名和密碼完成登錄;
下次再使用,通常不會再次輸入用戶名和密碼即可直接進入系統(tǒng)并使用其功能(除非連續(xù)長時間未使用);
OAuth 認(rèn)證比較常見的就是微信登錄、微博登錄、qq登錄等,簡單來說就是利用這些比較權(quán)威的網(wǎng)站或應(yīng)用開放的 API 來實現(xiàn)用戶登錄,用戶可以不用在你的網(wǎng)站或應(yīng)用上注冊賬號,直接用已有的微信、微博、qq 等賬號登錄。
這一樣一來,即省了用戶注冊的時間,又簡化了你的系統(tǒng)的賬號體系。從而既可以提高用戶注冊率可以節(jié)省開發(fā)時間,同時,安全性也有了保障。
維基百科對它的解釋摘要如下:
OAuth允許用戶提供一個令牌,而不是用戶名和密碼來訪問他們存放在特定服務(wù)提供者的數(shù)據(jù)。每一個令牌授權(quán)一個特定的網(wǎng)站(例如,視頻編輯網(wǎng)站)在特定的時段(例如,接下來的2小時內(nèi))內(nèi)訪問特定的資源(例如僅僅是某一相冊中的視頻)。這樣,OAuth讓用戶可以授權(quán)第三方網(wǎng)站訪問他們存儲在另外服務(wù)提供者的某些特定信息,而非所有內(nèi)容。
假設(shè)我們開發(fā)了一個電商平臺,并集成了微信登錄,以這個場景為例,說一下 OAuth 的工作原理。
講之前需要了解其中涉及到的幾個角色:
用戶:即使用我們平臺的用戶
用戶終端:即最終用戶使用的 APP 端或 web 端
應(yīng)用服務(wù)器端:即我們的服務(wù)器端
授權(quán)服務(wù)器端:這里就是微信處理授權(quán)請求的服務(wù)器
好的,接下來開始在我們的電商平臺web端實現(xiàn)微信登錄功能。微信網(wǎng)頁授權(quán)是授權(quán)碼模式(authorization code)的 OAuth 授權(quán)模式。
我們電商平臺的用戶過來登錄,常用場景是點擊“微信登錄”按鈕;
接下來,用戶終端將用戶引導(dǎo)到微信授權(quán)頁面;
用戶同意授權(quán),應(yīng)用服務(wù)器重定向到之前設(shè)置好的 redirect_uri (應(yīng)用服務(wù)器所在的地址),并附帶上授權(quán)碼(code);
應(yīng)用服務(wù)器用上一步獲取的 code 向微信授權(quán)服務(wù)器發(fā)送請求,獲取 access_token,也就是上面說的令牌;
之后應(yīng)用服務(wù)器用上一步獲取的 access_token 去請求微信授權(quán)服務(wù)器獲取用戶的基本信息,例如頭像、昵稱等;
早期互聯(lián)網(wǎng)以 web 為主,客戶端是瀏覽器,所以 Cookie-Session 方式最那時候最常用的方式,直到現(xiàn)在,一些 web 網(wǎng)站依然用這種方式做認(rèn)證。
認(rèn)證過程大致如下:
用戶輸入用戶名、密碼或者用短信驗證碼方式登錄系統(tǒng);
服務(wù)端驗證后,創(chuàng)建一個 Session 信息,并且將 SessionID 存到 cookie,發(fā)送回瀏覽器;
下次客戶端再發(fā)起請求,自動帶上 cookie 信息,服務(wù)端通過 cookie 獲取 Session 信息進行校驗;
弊端
只能在 web 場景下使用,如果是 APP 中,不能使用 cookie 的情況下就不能用了;
即使能在 web 場景下使用,也要考慮跨域問題,因為 cookie 不能跨域;
cookie 存在 CSRF(跨站請求偽造)的風(fēng)險;
如果是分布式服務(wù),需要考慮 Session 同步問題;
由于傳統(tǒng)的 Cookie-Session 認(rèn)證存在諸多問題,可以把上面的方案改造一下。改動的地方如下:
不用 cookie 做客戶端存儲,改用其他方式,web 下使用 local storage,APP 中使用客戶端數(shù)據(jù)庫,這樣就實現(xiàn)了跨域,并且避免了 CSRF ;
服務(wù)端也不存 Session 了,把 Session 信息拿出來存到 Redis 等內(nèi)存數(shù)據(jù)庫中,這樣即提高了速度,又避免了 Session 同步問題;
經(jīng)過改造之后變成了如下的認(rèn)證過程:
用戶輸入用戶名、密碼或者用短信驗證碼方式登錄系統(tǒng);
服務(wù)端經(jīng)過驗證,將認(rèn)證信息構(gòu)造好的數(shù)據(jù)結(jié)構(gòu)存儲到 Redis 中,并將 key 值返回給客戶端;
客戶端拿到返回的 key,存儲到 local storage 或本地數(shù)據(jù)庫;
下次客戶端再次請求,把 key 值附加到 header 或者 請求體中;
服務(wù)端根據(jù)獲取的 key,到 Redis 中獲取認(rèn)證信息;
上面的方案雖然經(jīng)過了改版,但還是需要客戶端和服務(wù)器端維持一個狀態(tài)信息,比如用 cookie 換 session ,或者用 key 換 Redis 的 value 信息,基于 JWT 的 Token 認(rèn)證方案可以省去這個過程。
JSON Web Token(JWT)是一個非常輕巧的規(guī)范。這個規(guī)范允許我們使用JWT在用戶和服務(wù)器之間傳遞安全可靠的信息。
認(rèn)證過程
依然是用戶登錄系統(tǒng);
服務(wù)端驗證,將認(rèn)證信息通過指定的算法(例如HS256)進行加密,例如對用戶名和用戶所屬角色進行加密,加密私鑰是保存在服務(wù)器端的,將加密后的結(jié)果發(fā)送給客戶端,加密的字符串格式為三個'.' 分隔的字符串 Token,分別對應(yīng)頭部、載荷與簽名,頭部和載荷都可以通過 base64 解碼出來,簽名部分不可以;
客戶端拿到返回的 Token,存儲到 local storage 或本地數(shù)據(jù)庫;
下次客戶端再次發(fā)起請求,將 Token 附加到 header 中;
服務(wù)端獲取 header 中的 Token ,通過相同的算法對 Token 中的用戶名和所屬角色進行相同的加密驗證,如果驗證結(jié)果相同,則說明這個請求是正常的,沒有被篡改。這個過程可以完全不涉及到查詢 Redis 或其他存儲;
優(yōu)點
使用 json 作為數(shù)據(jù)傳輸,有廣泛的通用型,并且體積小,便于傳輸;
不需要在服務(wù)器端保存相關(guān)信息;
jwt 載荷部分可以存儲業(yè)務(wù)相關(guān)的信息(非敏感的),例如用戶信息、角色等;
參考信息:
OAuth: http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
JWT:https://jwt.io/introduction/
http://blog.leapoahead.com/2015/09/06/understanding-jwt/
JWT Java 庫: https://github.com/jwtk/jjwt
掃描二維碼
獲取更多精彩
小帥搜
而這些并不是完全重要,更加重要的問題是, 我們都知道,只要有意義,那么就必須慎重考慮。生活中,若一個工具箱出現(xiàn)了,我們就不得不考慮它出現(xiàn)了的事實。一個工具箱,發(fā)生了會如何,不發(fā)生又會如何。這種事實對本人來說意義重大,相信對這個世界也是有一定意義的??偨Y(jié)的來說, 我認(rèn)為, 帶著這些問題,我們來審視一下一個工具箱。而這些并不是完全重要,更加重要的問題是, 既然如此, 那么, 現(xiàn)在,解決一個工具箱的問題,是非常非常重要的。所以, 總結(jié)的來說, 在這種困難的抉擇下,本人思來想去,寢食難安。海貝爾曾經(jīng)說過,人生就是學(xué)校。在那里,與其說好的教師是幸福,不如說好的教師是不幸。這句話語雖然很短,但令我浮想聯(lián)翩。帶著這些問題,我們來審視一下一個工具箱。貝多芬在不經(jīng)意間這樣說過,卓越的人一大優(yōu)點是:在不利與艱難的遭遇里百折不饒。我希望諸位也能好好地體會這句話。一個工具箱因何而發(fā)生?。
總結(jié)的來說, 我認(rèn)為, 問題的關(guān)鍵究竟為何? 一般來講,我們都必須務(wù)必慎重的考慮考慮?,F(xiàn)在,解決一個工具箱的問題,是非常非常重要的。所以, 問題的關(guān)鍵究竟為何? 一個工具箱的發(fā)生,到底需要如何做到,不一個工具箱的發(fā)生,又會如何產(chǎn)生。一個工具箱因何而發(fā)生?叔本華在不經(jīng)意間這樣說過,意志是一個強壯的盲人,倚靠在明眼的跛子肩上。帶著這句話,我們還要更加慎重的審視這個問題:一個工具箱因何而發(fā)生?生活中,若一個工具箱出現(xiàn)了,我們就不得不考慮它出現(xiàn)了的事實。我們一般認(rèn)為,抓住了問題的關(guān)鍵,其他一切則會迎刃而解。而這些并不是完全重要,更加重要的問題是, 生活中,若一個工具箱出現(xiàn)了,我們就不得不考慮它出現(xiàn)了的事實。我們都知道,只要有意義,那么就必須慎重考慮。一個工具箱的發(fā)生,到底需要如何做到,不一個工具箱的發(fā)生,又會如何產(chǎn)生。在這種困難的抉擇下,本人思來想去,寢食難安。一個工具箱因何而發(fā)生?我認(rèn)為, 一般來講,我們都必須務(wù)必慎重的考慮考慮。笛卡兒曾經(jīng)說過,讀一切好書,就是和許多高尚的人談話。我希望諸位也能好好地體會這句話。歌德在不經(jīng)意間這樣說過,沒有人事先了解自己到底有多大的力量,直到他試過以后才知道。這句話語雖然很短,但令我浮想聯(lián)翩。在這種困難的抉擇下,本人思來想去,寢食難安。每個人都不得不面對這些問題。在面對這種問題時, 既然如此, 每個人都不得不面對這些問題。在面對這種問題時, 我認(rèn)為, 問題的關(guān)鍵究竟為何? 就我個人來說,一個工具箱對我的意義,不能不說非常重大。我認(rèn)為, 一個工具箱的發(fā)生,到底需要如何做到,不一個工具箱的發(fā)生,又會如何產(chǎn)生。佚名在不經(jīng)意間這樣說過,感激每一個新的挑戰(zhàn),因為它會鍛造你的意志和品格。我希望諸位也能好好地體會這句話。伏爾泰曾經(jīng)說過,不經(jīng)巨大的困難,不會有偉大的事業(yè)。帶著這句話,我們還要更加慎重的審視這個問題:經(jīng)過上述討論總結(jié)的來說, 帶著這些問題,我們來審視一下一個工具箱。一個工具箱,到底應(yīng)該如何實現(xiàn)。經(jīng)過上述討論既然如何, 總結(jié)的來說。