最近在重構公司以前產品的前端代碼,擯棄了以前的session-cookie鑒權方式,采用token鑒權,忙里偷閑覺得有必要對幾種常見的鑒權方式整理一下。
目前我們常用的鑒權有四種:
一.HTTP Basic Authentication
這種授權方式是瀏覽器遵守http協議實現的基本授權方式,HTTP協議進行通信的過程中,HTTP協議定義了基本認證認證允許HTTP服務器對客戶端進行用戶身份證的方法。
認證過程:
1. 客戶端向服務器請求數據,請求的內容可能是一個網頁或者是一個ajax異步請求,此時,假設客戶端尚未被驗證,則客戶端提供如下請求至服務器:
Get /index.html HTTP/1.0
Host:www.google.com
2. 服務器向客戶端發送驗證請求代碼401,(WWW-Authenticate: Basic realm=”google.com”這句話是關鍵,如果沒有客戶端不會彈出用戶名和密碼輸入界面)服務器返回的數據大抵如下:
HTTP/1.0 401 Unauthorised
Server: SokEvo/1.0
WWW-Authenticate: Basic realm=”google.com”
Content-Type: text/html
Content-Length: xxx
3. 當符合http1.0或1.1規范的客戶端(如IE,FIREFOX)收到401返回值時,將自動彈出一個登錄窗口,要求用戶輸入用戶名和密碼。
4. 用戶輸入用戶名和密碼后,將用戶名及密碼以BASE64加密方式加密,并將密文放入前一條請求信息中,則客戶端發送的第一條請求信息則變成如下內容:
Get /index.html HTTP/1.0
Host:www.google.com
Authorization: Basic d2FuZzp3YW5n
注:d2FuZzp3YW5n表示加密后的用戶名及密碼(用戶名:密碼 然后通過base64加密,加密過程是瀏覽器默認的行為,不需要我們人為加密,我們只需要輸入用戶名密碼即可)
5. 服務器收到上述請求信息后,將Authorization字段后的用戶信息取出、解密,將解密后的用戶名及密碼與用戶數據庫進行比較驗證,如用戶名及密碼正確,服務器則根據請求,將所請求資源發送給客戶端
效果:
客戶端未未認證的時候,會彈出用戶名密碼輸入框,這個時候請求時屬于pending狀態,這個時候其實服務當用戶輸入用戶名密碼的時候客戶端會再次發送帶Authentication頭的請求。

認證成功:

server.js
let express = require("express");let app = express(); app.use(express.static(__dirname+'/public')); app.get("/Authentication_base",function(req,res){ console.log('req.headers.authorization:',req.headers) if(!req.headers.authorization){ res.set({ 'WWW-Authenticate':'Basic realm="wang"' }); res.status(401).end(); }else{ let base64 = req.headers.authorization.split(" ")[1]; let userPass = new Buffer(base64, 'base64').toString().split(":"); let user = userPass[0]; let pass = userPass[1]; if(user=="wang"&&pass="wang"){ res.end("OK"); }else{ res.status(401).end(); } } }) app.listen(9090)index.html:
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>HTTP Basic Authentication</title> </head> <body> <div></div> <script src="js/jquery-3.2.1.js"></script> <script> $(function(){ send('./Authentication_base'); }) var send = function(url){ $.ajax({ url : url, method : 'GET', }); } </script> </body></html>當然有登陸就有注銷,我們會發現當我們認證成功后每次請求請求頭都會帶上Authentication及里面的內容,那么如何做到讓這次登陸失效的?
網上查了半天,目前最有效的方式就是在注銷操作的時候,專門在服務器設置一個專門的注銷賬號,當接收到的Authentication信息為注銷用戶名密碼的時候糾就帶便注銷成功了,而客戶端在注銷操作的時候,手動的的去修改請求頭重的Authentication,將他設置未服務器默認的注銷賬號和密碼。
通過上面的簡單講解 其實我們已經可以返現這種驗證方式的缺陷加密方式簡單,僅僅是base64加密,這種加密方式是可逆的。同時在每個請求的頭上都會附帶上用戶名和密碼信息,這樣在外網是很容易被嗅探器探測到的。
總結:
正式因為這樣,這種加密方式一般多被用在內部安全性要求不高的的系統上,只是相對的多,總的來說現在使用這種鑒權比較少了。如果項目需要部署在公網上,這種方式不推薦,當然你也可以和SSL來加密傳輸,這樣會好一點,這個如果我后面有時間來研究一下。
二.session-cookie
第二種這個方式是利用服務器端的session(會話)和瀏覽器端的cookie來實現前后端的認證,由于http請求時是無狀態的,服務器正常情況下是不知道當前請求之前有沒有來過,這個時候我們如果要記錄狀態,就需要在服務器端創建一個會話(seesion),將同一個客戶端的請求都維護在各自得會會話中,每當請求到達服務器端的時候,先去查一下該客戶端有沒有在服務器端創建seesion,如果有則已經認證成功了,否則就沒有認證。
session-cookie認證主要分四步:
1,服務器在接受客戶端首次訪問時在服務器端創建seesion,然后保存seesion(我們可以將seesion保存在內存中,也可以保存在redis中,推薦使用后者),然后給這個session生成一個唯一的標識字符串,然后在響應頭中種下這個唯一標識字符串。
2.簽名。這一步只是對sid進行加密處理,服務端會根據這個secret密鑰進行解密。(非必需步驟)
3.瀏覽器中收到請求響應的時候會解析響應頭,然后將sid保存在本地cookie中,瀏覽器在下次http請求de 請求頭中會帶上該域名下的cookie信息,
4.服務器在接受客戶端請求時會去解析請求頭cookie中的sid,然后根據這個sid去找服務器端保存的該客戶端的session,然后判斷該請求是否合法。

server.js(nodejs+express+seesion+redis)
var express = require('express');var RedisStore = require('connect-redis')(express.session);var app = express();var secret = "wang839305939"http:// 設置 Cookieapp.use(express.cookieParser(secret));// 設置 Sessionapp.use(express.session({ store: new RedisStore({ host: "127.0.0.1", port: 6379, db: "session_db" }), secret: secret}))app.get("/", function(req, res) { var session = req.session; session.time= session.time|| 0; var n = session.time++; res.send('hello, session id:' + session.id + ' count:' + n);});app.listen(9080);三.Token 驗證
使用基于 Token 的身份驗證方法,大概的流程是這樣的:
1. 客戶端使用用戶名跟密碼請求登錄
2. 服務端收到請求,去驗證用戶名與密碼
3. 驗證成功后,服務端會簽發一個 Token,再把這個 Token 發送給客戶端
4. 客戶端收到 Token 以后可以把它存儲起來,比如放在 Cookie 里或者 Local Storage 里
5. 客戶端每次向服務端請求資源的時候需要帶著服務端簽發的 Token
6. 服務端收到請求,然后去驗證客戶端請求里面帶著的 Token,如果驗證成功,就向客戶端返回請求的數據
總的來說就是客戶端在首次登陸以后,服務端再次接收http請求的時候,就只認token了,請求只要每次把token帶上就行了,服務器端會攔截所有的請求,然后校驗token的合法性,合法就放行,不合法就返回401(鑒權失敗)。
乍的一看好像和前面的seesion-cookie有點像,seesion-cookie是通過seesionid來作為瀏覽器和服務端的鏈接橋梁,而token驗證方式貌似是token來起到seesionid的角色。其實這兩者差別是很大的。
1. sessionid 他只是一個唯一標識的字符串,服務端是根據這個字符串,來查詢在服務器端保持的seesion,這里面才保存著用戶的登陸狀態。但是token本身就是一種登陸成功憑證,他是在登陸成功后根據某種規則生成的一種信息憑證,他里面本身就保存著用戶的登陸狀態。服務器端只需要根據定義的規則校驗這個token是否合法就行。
2. session-cookie是需要cookie配合的,居然要cookie,那么在http代理客戶端的選擇上就是只有瀏覽器了,因為只有瀏覽器才會去解析請求響應頭里面的cookie,然后每次請求再默認帶上該域名下的cookie。但是我們知道http代理客戶端不只有瀏覽器,還有原生APP等等,這個時候cookie是不起作用的,或者瀏覽器端是可以禁止cookie的(雖然可以,但是這基本上是屬于吃飽沒事干的人干的事)…,但是token 就不一樣,他是登陸請求在登陸成功后再請求響應體中返回的信息,客戶端在收到響應的時候,可以把他存在本地的cookie,storage,或者內存中,然后再下一次請求的請求頭重帶上這個token就行了。簡單點來說cookie-session機制他限制了客戶端的類型,而token驗證機制豐富了客戶端類型。
3. 時效性。session-cookie的sessionid實在登陸的時候生成的而且在登出事時一直不變的,在一定程度上安全就會低,而token是可以在一段時間內動態改變的。
4. 可擴展性。token驗證本身是比較靈活的,一是token的解決方案有許多,常用的是JWT,二來我們可以基于token驗證機制,專門做一個鑒權服務,用它向多個服務的請求進行統一鑒權。
下面就拿最常用的JWT(JSON WEB TOKEN)來說:
JWT是Auth0提出的通過對JSON進行加密簽名來實現授權驗證的方案,就是登陸成功后將相關信息組成json對象,然后對這個對象進行某中方式的加密,返回給客戶端,客戶端在下次請求時帶上這個token,服務端再收到請求時校驗token合法性,其實也就是在校驗請求的合法性。
JWT對象通常由三部分構成:
Headers: 包括類別(typ)、加密算法(alg)
{ "alg": "HS256", "typ": "JWT" }Claims :包括需要傳遞的用戶信息
{ "sub": "1234567890", "name": "John Doe", "admin": true }Signature: 根據alg算法與私有秘鑰進行加密得到的簽名字串, 這一段是最重要的敏感信息,只能在服務端解密;
HMACSHA256( base64UrlEncode(Headers) + "." + base64UrlEncode(Claims), SECREATE_KEY)
編碼之后的JWT看起來是這樣的一串字符:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
nodejs+express+jwt-simple
auth.js
let jwt = require('jwt-simple');let secret = "wangyy";let time = 10; module.exports = { /* *檢驗token合法性 */ validate:function(req,res,next){ let token = req.body.token||req.headers["xssToken"]; if(token){ let decodeToken = null; try { //防止假冒token解析
主站蜘蛛池模板:
龙泉市|
化州市|
大足县|
文山县|
柳江县|
郎溪县|
肥东县|
宝鸡市|
通榆县|
舞阳县|
珲春市|
日喀则市|
富宁县|
高台县|
尤溪县|
兴仁县|
三原县|
永春县|
宁陕县|
柘城县|
镇宁|
紫金县|
凤城市|
志丹县|
舟山市|
九寨沟县|
永泰县|
夏津县|
清苑县|
商水县|
翼城县|
乐平市|
普兰县|
吕梁市|
社会|
鹤庆县|
龙岩市|
贵阳市|
荔波县|
广昌县|
增城市|