您好,登錄后才能下訂單哦!
本篇內容介紹了“JS前端認證授權技巧有哪些”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
認證 (Identification) 是驗證當前用戶的身份。
常見的認證技術:
身份證
用戶名和密碼
用戶手機:手機短信、手機二維碼掃描、手勢密碼
用戶的電子郵箱
用戶的生物學特征:指紋、語音、眼睛虹膜
授權 (Authorization) 指賦予用戶系統的訪問權限。認證完用戶身份后,系統會授予用戶部分或者全部權限。系統要是沒有權限控制需求的話,一般認證后用戶就有全部權限。
實現授權的方式有:
cookie
session
token
OAuth
鑒權 (Authentication) 是指系統鑒定用戶身份和權限。比如系統需要鑒定 session/cookie/token 的合法性和有效性。
這三個概念的關系也是很清晰,就是一個前后依次發生的關系:認證 => 授權 => 鑒權。比如我們登錄某個系統就完成了認證和授權,后續使用功能時就需要系統鑒權。
了解相關概念后,就可以開始介紹常見的認證授權方案。
基本認證 (Basic 認證) 是 HTTP/1.0 就定義的認證方式,主要通過用戶提供用戶名和密碼的方式,實現對用戶身份的驗證。
基本認證流程圖
認證步驟解析
瀏覽器請求受保護的資源
服務器返回 401,同時響應頭帶上 WWW-Authenticate: Basic realm=protected_docs,realm 代表資源的安全域,我們資源可能權限不同,放在不同的安全域中
瀏覽器彈窗請求用戶賬號密碼,用戶輸入后,瀏覽器 Base64 編碼賬號密碼,再次請求,請求頭帶上 Authorization: Basic Base64(賬號:密碼)
服務器認證賬號密碼,認證成功返回對應資源,認證失敗返回 403 forbidden
node 的簡單實現
const express = require('express') const app = express() const protectedPath = '/protected_docs' const realms = { [protectedPath]: { users: ['root'], }, } app.get(protectedPath, (req, res, next) => { const realm = realms[req.path] const authorization = req.get('authorization') if (!authorization) { // 告知用戶需要身份認證 res.statusCode = 401 res.set('WWW-Authenticate', 'Basic realm=' + encodeURIComponent(realm)) res.end() return } const usernamePasswd = authorization.split(' ')[1] // Basic Y2h6aW5ncDoxMjM0NTY const [usrname, passwd] = Buffer.from(usernamePasswd, 'base64') .toString() .split(':') if (!realm.users.includes(usrname)) { // 用戶不在realm里 res.statusCode = 401 res.set('WWW-Authenticate', 'Basic realm=' + encodeURIComponent(realm)) res.end() return } const isValid = usrname === 'root' && passwd === '123456' if (!isValid) { // 用戶賬號、密碼驗證不通過 res.statusCode = 403 res.end() return } res.end(`welecom ${usrname}`) }) app.listen(3000)
小結
優點:
簡單,基本所有瀏覽器都支持
缺點:
不安全,HTTP 上傳輸,密碼只是 Base64 編碼,可以被解碼。
無法主動注銷,除非標簽頁或瀏覽器關閉、或用戶清除歷史記錄,認證信息一直存在。
使用場景:
內部網絡,或者對安全要求不是很高的網絡。比如公司內網 wiki
這邊提一下,HTTP1.1 針對基本認證缺點,提供了摘要認證(Digest 認證),原理簡單來說就是服務端會給瀏覽器一個 nonce 隨機數,瀏覽器會將賬號、密碼和 nonce 等參數進行 md5 加密后傳給服務端(同時傳賬號數據,密碼不傳),服務端獲取到賬號后,從數據庫拿密碼同樣進行 md5 加密,加密后值和瀏覽器傳的一樣就認為認證成功。
摘要認證不再明文傳輸密碼、可以防重放和避免報文被篡改,但是需要和 Https 配合使用。
Session-Cookie 認證是利用服務端的 Session(會話)和瀏覽器(客戶端)的 Cookie 來實現的前后端通信認證模式。
什么是 Cookie
HTTP 是無狀態協議,服務端在接收到客戶端首次請求后,設置對應的 Cookie,隨后瀏覽器在請求帶上 Cookie,服務端就可以知道當前客戶端狀態。
Cookie 的特點:
Cookie 存儲在客戶端,可能被修改,不安全
有大小限制,一般是 4KB
Android 和 IOS 對 Cookie 支持不好
Cookie 有跨域限制
什么是 Session
Session 的抽象概念是會話,是無狀態協議通信過程中,為了實現中斷/繼續操作,將用戶和服務器之間的交互進行的一種抽象。
Session 一般流程是:服務端接收到客戶端首次請求后,設置一個 Session 來跟蹤用戶的會話,同時會給客戶端一個 Session ID,后續客戶端請求時在帶上 Session ID,服務端即可找到對應的 Session,此時雙方通信就是有狀態的。
Session 特點:
Session 數據保存在服務端,安全性高
Session 數據大小可以超過 4KB,存儲數
Session-Cookie 流程
Session 流程中一般會設置 Session ID,通常 Session ID 會保存在 瀏覽器 Cookie 中,接下來看下整體流程。
認證步驟解析
瀏覽器發送登錄請求
服務端校驗賬號密碼,校驗通過后生成 Session 和 Session ID,Session 保存在 Session 服務器中(一般保存在內存或者 Redis 服務器)。隨后返回數據給瀏覽器,同時設置一個 Session ID 的 Cookie
瀏覽器請求資源,一般會自動帶上 Session ID 的 Cookie
服務端獲取到 Session ID,通過 Session 服務器校驗 Session,Session 服務器校驗成功,服務端處理數據邏輯
服務端返回數據給瀏覽器
簡單代碼示例
const express = require("express"); const app = express(); const bodyParser = require("body-parser"); const port = 3000; const session = require("express-session"); app.use(bodyParser()); app.use( session({ key: 'SESSION_ID', secret: "your_secret_key", resave: false, saveUninitialized: false, cookie: { maxAge: 1000 * 60 * 60 * 8, signed: true }, }) ); app.get("/", async function (req, res) { res.send(req.session); }); app.get("/login", async function (req, res) { const authInfo = { id: '1', username: 'user', } const isValid = true if (isValid) { req.session.authInfo = authInfo res.send({ success: true, info: "登錄成功", }); } else { res.send({ success: false, info: "登錄失敗", }); } }); app.listen(port, () => { console.log(`node listening at http://localhost:${port}`); });
使用 node 起好服務器后,先訪問 /login
,在訪問首頁 /
, 可以看到首頁輸出用戶名。同時打開 F12,在 Cookie 中可以看到 SESSION_ID 的數據。
小結
優點:
Cookie 簡單易用
Session 數據保存在服務端,安全
支持 Session 管理,能主動注銷 Session
只需后端操作,前端無感
缺點:
依賴 Cookie,禁用 Cookie 情況下無法使用
因為使用了 Cookie ,所以可能有 CSRF 攻擊
Session 存在服務端,用戶量大的情況下,服務端開銷增加,性能會下降
對移動端的支持性不友好
使用場景:
一般中大型的網站都適用
上述介紹中,我們知道了 Session-Cookie 的一些缺點,及 Session 的維護給服務端造成很大困擾,必須找地方存放它,又要考慮分布式的問題,所以 Token 方案就出來了。
Token 是一個令牌,客戶端訪問服務器時,驗證通過后服務端會為其簽發一張令牌,之后,客戶端就可以攜帶令牌訪問服務器,服務端只需要驗證令牌的有效性即可。
一般 Token 的組成:
uid(用戶唯一的身份標識) + time(當前時間的時間戳) + sign(簽名,Token 的前幾位以哈希算法壓縮成的一定長度的十六進制字符串)
Token 認證流程
認證步驟解析
客戶端發送登錄請求
服務端校驗賬號密碼,生成 Token,并返回給客戶端
收到 Token 以后需要把它存儲起來,web 端一般會放在 localStorage 或 Cookie 中
客戶端請求 API 資源的時候,將 Token 通過 HTTP 請求頭 Authorization 字段或者其它方式發送給服務端
服務端拿到 Token,做解密和簽名校驗,通過校驗返回數據,否則返回 401
Token的優缺點
優點:
服務端無狀態化、可擴展性好:Token 自身包含了其所標識用戶的相關信息,這有利于在多個服務間共享用戶狀態
安全性好,可以避免 CSRF 攻擊
支持跨域調用
缺點:
性能問題,服務端需要對 Token 加解密等操作,所以會更耗性能
有效期短:為了避免 Token 被盜用,一般 Token 的有效期會設置的較短,所以就有了 Refresh Token
相比于 Session-Cookie,需要前后端配合處理
業務接口用來鑒權的 Token,我們稱之為 Access Token,為了安全性,Access Token 有效期一般設置的比較短。Access Token 過期后,需要用戶重新登錄,但是這種體驗較差。
所以有了 Refresh Token, 可以用 Refresh Token 去獲取 Access Token。
Access Token:用來訪問業務接口,由于有效期足夠短,盜用風險小。
Refresh Token:用來獲取 Access Token,有效期可以長一些,通過獨立服務和嚴格的請求方式增加安全性。
Refresh Token 的使用流程是在服務器校驗 Token, 發現過期后,客戶端可以使用 Refresh Token 發起請求,獲取新的 Access Token 和 Refresh Token。
上述 Token 中,一般只有 uid 信息,需要更多登錄信息和其他數據的話,這時就需要查詢數據庫。每次都需要查詢數據庫,就會帶來一些性能消耗。所以業界常用的 JWT 方案就出來了。
JWT 是 Auth0 提出的通過對 JSON 進行加密簽名來實現授權驗證的方案, 它的特點是自包含的,用戶信息和認證是在一起的,無需像 Cookie-Session 一樣需要 Session 服務器,或者像 Token 一樣訪問數據庫獲取用戶信息。
JWT 本質上就是一組字串,通過(.)切分成三個為 Base64 編碼的部分:
Header : 描述 JWT 的元數據,定義了生成簽名的算法以及 Token 的類型。
Payload : 用來存放實際需要傳遞的數據,JWT 規定了 7 個官方字段,比如 iss、exp 等等,還可以自定義數據。
Signature(簽名):服務器通過 Payload、Header 和一個密鑰 (Secret) 使用 Header 里面指定的簽名算法(默認是 HMAC SHA256)生成。
JWT 通常是這樣的:xxxxx.yyyyy.zzzzz。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 .eyJuYW1lIjoieGlhb21pbmciLCJkYXRhIjoiPT09PT09PT09PT09PSIsImlhdCI6MTY3OTgwNDA1NywiZXhwIjoxNjc5ODA0MTE3fQ .FdJ6UD4Ff5zOz83f4hRDh2C86kN5f8aO_KeEtIwt3cM
JWT 認證流程
其實 JWT 的認證流程與 Token 的認證流程差不多,只是不需要再單獨去查詢數據庫查找用戶信息。
JWT 實例
const { expressjwt: jwt } = require('express-jwt') const express = require('express') const app = express() const jsonwebtoken = require('jsonwebtoken') const secretOrPrivateKey = 'hello' //加密token 校驗token時要使用 app.use( jwt({ secret: secretOrPrivateKey, algorithms: ['HS256'], }).unless({ path: ['/getToken'], //除了這個地址,其他的URL都需要驗證 }) ) app.use(function (err, req, res, next) { if (err.name === 'UnauthorizedError') { res.status(401).send('invalid token...') } }) app.get('/getToken', function (req, res) { res.json({ result: 'ok', token: jsonwebtoken.sign( { name: 'xiaoming', data: '=============', }, secretOrPrivateKey, { expiresIn: 60 * 1, } ), }) }) app.get('/getData', function (req, res) { res.send('data') }) app.listen(3000)
服務啟動后,訪問 /getToken
獲取 JWT,然后在 Postman 中請求 /getData
, Header 部分加上 Authorization: Bearer your jwt
,比如 Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoieGlhb21pbmciLCJkYXRhIjoiPT09PT09PT09PT09PSIsImlhdCI6MTY3OTgwNjkyOCwiZXhwIjoxNjc5ODA2OTg4fQ.qFOT9IS_T1ZNsWWheRXP9MxYPh2l3SBGWLtp8ocnKAE
。
JWT 的優缺點
優點:
不需要在服務端保存會話信息,所以易于應用的擴展
JWT 中的 Payload 負載可以存儲常用信息,用于信息交換,有效地使用 JWT,可以降低服務端查詢數據庫的次數
缺點:
請求頭體積較大,Payload 數據量大的時候,請求頭體積也會對應增大
JWT 默認是不加密,所以 Payload 一般不放敏感信息,不過也可以再次加密。
到期問題,JWT 一旦簽發后,除非到了過期時間,不然會一直有效,服務端無法主動注銷掉。
使用場景:
一般對安全要求不高,對負載要求高的場景都會可以使用 JWT,安全敏感的場景推薦使用 token + redis
單點登錄
OAuth 這種方式登錄相信大家都使用過,比如我們想登錄某個網站時,通常會發現可以通過第三方的 QQ 或者微信登錄,這個就是使用到了 OAuth。
OAuth 是一個開放標準,允許用戶授權第三方網站訪問他們存儲在另外的服務提供者上的信息,而不需要將用戶名和密碼提供給第三方網站。常見的提供 OAuth 認證服務的廠商:支付寶、QQ、微信、微博
OAuth 認證流程
認證步驟解析
客戶端發起使用第三方登錄,服務端攜帶 client_id
直接重定向到授權服務器登錄頁面,授權服務器要求客戶端登錄。
客戶端完成第三方登錄并同意授權后,授權服務器重定向到服務端,并攜帶授權碼 code
,服務器攜帶 client_id
, client_secret
, code
向授權服務器請求令牌。
授權服務器通過認證,并返回令牌
服務端用令牌向授權服務器請求用戶基本信息,授權服務器認證令牌通過后返回數據
服務端返回用戶信息給客戶端
使用 github 登錄示例
準備工作:
1、創建 OAuth App
2、填寫基本信息
3、獲取 client_id
和 client_secret
代碼示例
const express = require('express') const app = express() const querystring = require('querystring') const axios = require('axios') // GitHub登錄參數配置;配置授權應用生成的Client ID和Client Secret const config = { client_id: 'xxx', client_secret: 'xxxxxx' } // 登錄接口 app.get('/github/login', function (req, res) { // 重定向到GitHub認證接口,并配置參數 let path = 'https://github.com/login/oauth/authorize?client_id=' + config.client_id // 轉發到授權服務器 res.redirect(path) }) // GitHub授權登錄成功回調,地址必須與GitHub配置的回調地址一致 app.get('/passport/github/callback', async function (req, res) { console.log('callback...') // 服務器認證成功,回調帶回認證狀態code const code = req.query.code const params = { client_id: config.client_id, client_secret: config.client_secret, code: code } // 申請令牌token let tokenRes = await axios.post('https://github.com/login/oauth/access_token', params) const access_token = querystring.parse(tokenRes.data).access_token // 根據token獲取用戶信息 userRes = await axios.get(`https://api.github.com/user`, { headers: { 'Authorization': 'token ' + access_token } }) // 渲染頁面 res.end(` <h2>Hello ${userRes.data.login}</h2> <img src="${userRes.data.avatar_url}" alt=""> `) }) app.listen(7001, () => { console.log('listening port at 7001...') })
服務啟動后,訪問 /github/login
,后續會跳轉 github 登錄授權,完成后即可看到你的 github 用戶名和頭像。
“JS前端認證授權技巧有哪些”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。