前端需知道的常見(jiàn)登錄鑒權(quán)方案
背景
說(shuō)起鑒權(quán)大家應(yīng)該都很熟悉,不過(guò)作為前端開(kāi)發(fā)來(lái)講,鑒權(quán)的流程大頭都在后端小哥那邊,本文的目的就是為了讓大家了解一下常見(jiàn)的鑒權(quán)的方式和原理。
認(rèn)知:HTTP 是一個(gè)無(wú)狀態(tài)協(xié)議,所以客戶端每次發(fā)出請(qǐng)求時(shí),下一次請(qǐng)求無(wú)法得知上一次請(qǐng)求所包含的狀態(tài)數(shù)據(jù)。
一、HTTP Auth Authentication
簡(jiǎn)介
HTTP 提供一個(gè)用于權(quán)限控制和認(rèn)證的通用框架。最常用的HTTP認(rèn)證方案是HTTP Basic Authentication
鑒權(quán)流程
加解密過(guò)程
// Authorization 加密過(guò)程
let?email?=?"[email protected]"
let?password?=?"12345678"
let?auth?=?`${email}:${password}`
const?buf?=?Buffer.from(auth,?'ascii');
console.info(buf.toString('base64'));?// cG9zdG1haWxAdGVzdC5jb206MTIzNDU2Nzg=
// Authorization 解密過(guò)程
const?buf?=?Buffer.from(authorization.split(' ')[1]?||?''),?'base64');
const?user?=?buf.toString('ascii').split(':');
其他 HTTP 認(rèn)證
通用 HTTP 身份驗(yàn)證框架有多個(gè)驗(yàn)證方案使用。不同的驗(yàn)證方案會(huì)在安全強(qiáng)度上有所不同。
IANA 維護(hù)了一系列的驗(yàn)證方案[1],除此之外還有其他類型的驗(yàn)證方案由虛擬主機(jī)服務(wù)提供,例如 Amazon AWS ,常見(jiàn)的驗(yàn)證方案包括:
Basic ( RFC 7617[2], Base64 編碼憑證. 詳情請(qǐng)參閱下文.),
Bearer ( RFC 6750[3], bearer 令牌通過(guò)OAuth 2.0保護(hù)資源),
Digest ( RFC 7616[4], 只有 md5 散列 在Firefox中支持, 查看 bug 472823[5] 用于SHA加密支持),
HOBA ( RFC 7486[6] (草案), HTTP Origin-Bound 認(rèn)證, 基于數(shù)字簽名),
Mutual ( draft-ietf-httpauth-mutual[7]),
AWS4-HMAC-SHA256 ( AWS docs[8])
二、Cookie + Session
注冊(cè)流程
思考:為什么要在密碼里加點(diǎn)“鹽”?[9]
鑒權(quán)流程
Session 存儲(chǔ)
最常用的 Session 存儲(chǔ)方式是 KV 存儲(chǔ),如Redis,在分布式、API 支持、性能方面都是比較好的,除此之外還有 mysql、file 存儲(chǔ)。
如果服務(wù)是分布式的,使用 file 存儲(chǔ),多個(gè)服務(wù)間存在同步 session 的問(wèn)題;高并發(fā)情況下錯(cuò)誤讀寫鎖的控制。
Session Refresh
我們上面提到的流程中,缺少 Session 的刷新的環(huán)節(jié),我們不能在用戶登錄之后經(jīng)過(guò)一個(gè) expires 時(shí)間就把用戶踢出去,如果在 Session 有效期間用戶一直在操作,這時(shí)候 expires 時(shí)間就應(yīng)該刷新。
以 Koa 為例,刷新 Session 的機(jī)制也比較簡(jiǎn)單:
開(kāi)發(fā)一個(gè) middleware(默認(rèn)情況下所有請(qǐng)求都會(huì)經(jīng)過(guò)該 middleware),如果校驗(yàn) Session 有效,就更新 Session 的 expires: 當(dāng)前時(shí)間+過(guò)期時(shí)間。
優(yōu)化:
頻繁更新 session 會(huì)影響性能,可以在 session 快過(guò)期的時(shí)候再更新過(guò)期時(shí)間。
如果某個(gè)用戶一直在操作,同一個(gè) sessionID 可能會(huì)長(zhǎng)期有效,如果相關(guān) cookie 泄露,可能導(dǎo)致比較大的風(fēng)險(xiǎn),可以在生成 sessionID 的同時(shí)生成一個(gè) refreshID,在 sessionID 過(guò)期之后使用 refreshID 請(qǐng)求服務(wù)端生成新的 sessionID(這個(gè)方案需要前端判斷 sessionID 失效,并攜帶 refreshID 發(fā)請(qǐng)求)。
單設(shè)備登錄
有些情況下,只允許一個(gè)帳號(hào)在一個(gè)端下登錄,如果換了一個(gè)端,需要把之前登錄的端踢下線(默認(rèn)情況下,同一個(gè)帳號(hào)可以在不同的端下同時(shí)登錄的)。
這時(shí)候可以借助一個(gè)服務(wù)保存用戶唯一標(biāo)識(shí)和 sessionId 值的對(duì)應(yīng)關(guān)系,如果同一個(gè)用戶,但 sessionId 不一樣,則不允許登錄或者把之前的踢下線(刪除舊 session )。
三、JWT
簡(jiǎn)介
JSON Web Token (JWT)是一個(gè)開(kāi)放標(biāo)準(zhǔn)(RFC 7519),它定義了一種緊湊的、自包含的方式,用于作為JSON對(duì)象在各方之間安全地傳輸信息。該信息可以被驗(yàn)證和信任,因?yàn)樗菙?shù)字簽名的。
JWT 組成
JWT 由三部分組成,分別是 header(頭部),payload(載荷),signature(簽證) 這三部分以小數(shù)點(diǎn)連接起來(lái)。
例如使用名為 jwt-token 的cookie來(lái)存儲(chǔ) JWT 例如:
jwt-token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoibHVzaGlqaWUiLCJpYXQiOjE1MzI1OTUyNTUsImV4cCI6MTUzMjU5NTI3MH0.WZ9_poToN9llFFUfkswcpTljRDjF4JfZcmqYS0JcKO8;
使用.分割值可以得到三部分組成元素,按照順序分別為:
header:
值:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Base64 解碼: {"alg": "HS256", "type": "JWT"}
payload:
值:eyJuYW1lIjoibHVzaGlqaWUiLCJpYXQiOjE1MzI1OTUyNTUsImV4cCI6MTUzMjU5NTI3MH0
Base64 解碼:
{
"name":?"lushijie",
"iat":?1532595255,?// 發(fā)布時(shí)間
"exp":?1532595270?// 過(guò)期時(shí)間
}
signature:
值:WZ9_poToN9llFFUfkswcpTljRDjF4JfZcmqYS0JcKO8
解碼:
const?headerEncode?=?base64Encode(header);
const?payloadEncode?=?base64Encode(payload);
let?signature?=?HMACSHA256(headerEncode?+?'.'?+?payloadEncode,?'密鑰');
鑒權(quán)流程
Token 校驗(yàn)
對(duì)于驗(yàn)證一個(gè) JWT 是否有效也是比較簡(jiǎn)單的,服務(wù)端根據(jù)前面介紹的計(jì)算方法計(jì)算出 signature,和要校驗(yàn)的JWT中的 signature 部分進(jìn)行對(duì)比就可以了,如果 signature 部分相等則是一個(gè)有效的 JWT。
Token Refresh
為了減少 JWT Token 泄露風(fēng)險(xiǎn),一般有效期會(huì)設(shè)置的比較短。這樣就會(huì)存在 JWT Token 過(guò)期的情況,我們不可能讓用戶頻繁去登錄獲取新的 JWT Token。
解決方案:
可以同時(shí)生成 JWT Token 與 Refresh Token,其中 Refresh Roken 的有效時(shí)間長(zhǎng)于 JWT Token,這樣當(dāng) JWT Token 過(guò)期之后,使用 Refresh Token 獲取新的 JWT Token 與 Refresh Token,其中 Refresh Token 只能使用一次。
四、OAuth
簡(jiǎn)介
有時(shí)候,我們登錄某個(gè)網(wǎng)站,但我們又不想注冊(cè)該網(wǎng)站的賬號(hào),這時(shí)我們可以使用第三方賬號(hào)登錄,比如 github、微博、微信、QQ等。
授權(quán)流程
名詞解釋:
Third-party application:第三方應(yīng)用程序又稱"客戶端"(client),比如打開(kāi)知乎,使用第三方登錄,選擇 Github 登錄,這時(shí)候知乎就是客戶端。
Resource Owner:資源所有者,本文中又稱"用戶"(user),即登錄用戶。
Authorization server:認(rèn)證服務(wù)器,即 Github 專門用來(lái)處理認(rèn)證的服務(wù)器。
Resource server:資源服務(wù)器,即 Github 存放用戶生成的資源的服務(wù)器。它與認(rèn)證服務(wù)器,可以是同一臺(tái)服務(wù)器,也可以是不同的服務(wù)器。
A. A網(wǎng)站讓用戶跳轉(zhuǎn)到 GitHub,請(qǐng)求授權(quán)碼;GitHub 要求用戶登錄,然后詢問(wèn)“知乎網(wǎng)站要求獲得 xx 權(quán)限,你是否同意?”;
B. 用戶同意,GitHub 就會(huì)重定向回 A 網(wǎng)站,同時(shí)發(fā)回一個(gè)授權(quán)碼;
C. A 網(wǎng)站使用授權(quán)碼,向 GitHub 請(qǐng)求令牌;
D. GitHub 返回令牌;
E. A 網(wǎng)站使用令牌,向 GitHub 請(qǐng)求用戶數(shù)據(jù);
其他授權(quán)模式
授權(quán)碼模式(authorization code)是功能最完整、流程最嚴(yán)密的授權(quán)模式。除了我們上面所說(shuō)的授權(quán)碼模式,其實(shí)還有其他授權(quán)模式:
簡(jiǎn)化模式(Implicit grant type)
有些 Web 應(yīng)用是純前端應(yīng)用,沒(méi)有后端。這時(shí)就不能用上面的方式了,必須將令牌儲(chǔ)存在前端。RFC 6749 就規(guī)定了第二種方式,允許直接向前端頒發(fā)令牌。這種方式?jīng)]有授權(quán)碼這個(gè)中間步驟
密碼模式(Resource Owner Password Credentials Grant)
如果你高度信任某個(gè)應(yīng)用,RFC 6749 也允許用戶把用戶名和密碼,直接告訴該應(yīng)用。該應(yīng)用就使用你的密碼,申請(qǐng)令牌
客戶端模式(Client Credentials Grant)
適用于沒(méi)有前端的命令行應(yīng)用,即在命令行下請(qǐng)求令牌
關(guān)于這些模式詳細(xì)請(qǐng)見(jiàn):OAuth2.0 的四種方式[10]
單點(diǎn)登錄
單點(diǎn)登錄(Single Sign On, SSO),即:?jiǎn)我粯?biāo)記(單點(diǎn))登錄。例如:QQ,我在QQ空間登錄一次,我可以去訪問(wèn)QQ產(chǎn)品的其他服務(wù):QQ郵箱、騰訊新聞等,都能保證你的賬戶保持登錄狀態(tài)。
延伸閱讀:
《如何實(shí)現(xiàn)單點(diǎn)登錄?》[11]
《手機(jī)掃碼登錄內(nèi)網(wǎng)怎么實(shí)現(xiàn)的?》[12]
五、總結(jié)對(duì)比
沒(méi)有最好,只有最合適!?。?/span>
HTTP Auth Authentication:
梳理總結(jié):
通用 HTTP 身份驗(yàn)證框架有多個(gè)驗(yàn)證方案使用。不同的驗(yàn)證方案會(huì)在安全強(qiáng)度上有所不同。HTTP Auth Authentication 是最常用的 HTTP認(rèn)證方案,為了減少泄露風(fēng)險(xiǎn)一般要求 HTTPS 協(xié)議。
適用場(chǎng)景:
一般多被用在內(nèi)部安全性要求不高的的系統(tǒng)上,如路由器網(wǎng)頁(yè)管理接口
問(wèn)題:
請(qǐng)求上攜帶驗(yàn)證信息,容易被嗅探到
無(wú)法注銷
適合一次性驗(yàn)證,例如注冊(cè)激活鏈接
Cookie + Session:
梳理總結(jié):
服務(wù)端存儲(chǔ) session ,客戶端存儲(chǔ) cookie,其中 cookie 保存的為 sessionID
可以靈活 revoke 權(quán)限,更新信息后可以方便的同步 session 中相應(yīng)內(nèi)容
分布式 session 一般使用 redis(或其他KV) 存儲(chǔ)
使用場(chǎng)景:
適合傳統(tǒng)系統(tǒng)獨(dú)立鑒權(quán)
JWT:
梳理總結(jié):
服務(wù)器不再需要存儲(chǔ) session,服務(wù)器認(rèn)證鑒權(quán)業(yè)務(wù)可以方便擴(kuò)展
JWT 并不依賴 cookie,也可以使用 header 傳遞
為減少盜用,要使用 HTTPS 協(xié)議傳輸
適用場(chǎng)景:
適合做簡(jiǎn)單的 RESTful API 認(rèn)證
適合一次性驗(yàn)證,例如注冊(cè)激活鏈接
問(wèn)題:
使用過(guò)程中無(wú)法廢棄某個(gè) token,有效期內(nèi) token 一直有效
payload 信息更新時(shí),已下發(fā)的 token 無(wú)法同步
OAuth:
梳理總結(jié):
OAuth是一個(gè)開(kāi)放標(biāo)準(zhǔn),允許用戶授權(quán)第三方應(yīng)用訪問(wèn)他們存儲(chǔ)在另外的服務(wù)提供者上的信息,而不需要將用戶名和密碼提供給第三方移動(dòng)應(yīng)用或分享他們數(shù)據(jù)的所有內(nèi)容。
GitHub OAuth 文檔 Identifying and authorizing users for GitHub Apps
適用場(chǎng)景:
OAuth 分為下面四種模式:
簡(jiǎn)化模式,不安全,適用于純靜態(tài)頁(yè)面應(yīng)用
授權(quán)碼模式,功能最完整、流程最嚴(yán)密的授權(quán)模式,通常使用在公網(wǎng)的開(kāi)放平臺(tái)中
密碼模式,一般在內(nèi)部系統(tǒng)中使用,調(diào)用者是以用戶為單位。
客戶端模式,一般在內(nèi)部系統(tǒng)之間的 API 調(diào)用。兩個(gè)平臺(tái)之間調(diào)用,以平臺(tái)為單位。
文內(nèi)鏈接:
1.一系列的驗(yàn)證方案:
http://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml
2.RFC 7617:
https://tools.ietf.org/html/rfc7617
3.RFC 6750:
https://tools.ietf.org/html/rfc6750
4.RFC 6716:
https://link.zhihu.com/?target=https%3A//tools.ietf.org/html/rfc6750
5.bug 472823:
https://bugzilla.mozilla.org/show_bug.cgi?id=472823
6.RFC 7486:
https://tools.ietf.org/html/rfc7486
7.draft-ietf-httpauth-mutual:
https://tools.ietf.org/html/draft-ietf-httpauth-mutual-11
8.AWS docs:
https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html
9.為什么要在密碼里加點(diǎn)“鹽”?
https://www.cnblogs.com/apolloren/p/11985083.html
10.OAuth2.0 的四種方式:
http://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html
11.如何實(shí)現(xiàn)單點(diǎn)登錄?:
https://blog.csdn.net/Faker_Wang/article/details/80877654
11.手機(jī)掃碼登錄內(nèi)網(wǎng)怎么實(shí)現(xiàn)的?:
https://blog.csdn.net/maxchenBug/article/details/88791514
??愛(ài)心三連擊
1.看到這里了就點(diǎn)個(gè)在看支持下吧,你的「點(diǎn)贊,在看」是我創(chuàng)作的動(dòng)力。
2.關(guān)注公眾號(hào)
程序員成長(zhǎng)指北,回復(fù)「1」加入Node進(jìn)階交流群!「在這里有好多 Node 開(kāi)發(fā)者,會(huì)討論 Node 知識(shí),互相學(xué)習(xí)」!3.也可添加微信【ikoala520】,一起成長(zhǎng)。
“在看轉(zhuǎn)發(fā)”是最大的支持








