目錄:
一、什么是JWT
二、我們?yōu)槭裁匆褂肑WT(與傳統(tǒng)的session認(rèn)證有何區(qū)別)
三、如何使用JWT
四、JWT的構(gòu)成及原理
五、JWT加解密實(shí)例
一、什么是JWT
JWT——Json web token
是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開放標(biāo)準(zhǔn),可實(shí)現(xiàn)無狀態(tài)、分布式的Web應(yīng)用授權(quán)。
二、我們?yōu)槭裁葱枰狫WT?
首先,當(dāng)前后端分離時(shí)我們會(huì)因?yàn)橥床呗远鵁o法設(shè)置cookie和sessionid。當(dāng)然了我們有很多方式去解決這個(gè)問題,比如反向代理和jsonp等。但這仍然不如直接使用jwt來的簡便。其次就是要說到j(luò)wt與傳統(tǒng)的身份認(rèn)證相比有什么優(yōu)勢了。
回答這個(gè)問題需要來看看基于token的認(rèn)證和傳統(tǒng)的session認(rèn)證的區(qū)別
1、傳統(tǒng)的session認(rèn)證
我們知道,http協(xié)議本身是一種無狀態(tài)的協(xié)議,而這就意味著如果用戶向我們的應(yīng)用提供了用戶名和密碼來進(jìn)行用戶認(rèn)證,那么下一次請求時(shí),用戶還要再一次進(jìn)行用戶認(rèn)證才行,因?yàn)楦鶕?jù)http協(xié)議,我們并不能知道是哪個(gè)用戶發(fā)出的請求,所以為了讓我們的應(yīng)用能識別是哪個(gè)用戶發(fā)出的請求,我們只能在服務(wù)器存儲(chǔ)一份用戶登錄的信息,這份登錄信息會(huì)在響應(yīng)時(shí)傳遞給瀏覽器,告訴其保存為cookie,以便下次請求時(shí)發(fā)送給我們的應(yīng)用,這樣我們的應(yīng)用就能識別請求來自哪個(gè)用戶了,這就是傳統(tǒng)的基于session認(rèn)證。
但是這種基于session的認(rèn)證使應(yīng)用本身很難得到擴(kuò)展,隨著不同客戶端用戶的增加,獨(dú)立的服務(wù)器已無法承載更多的用戶,而這時(shí)候基于session認(rèn)證應(yīng)用的問題就會(huì)暴露出來.
2、基于token的鑒權(quán)機(jī)制
基于token的鑒權(quán)機(jī)制類似于http協(xié)議也是無狀態(tài)的,它不需要在服務(wù)端去保留用戶的認(rèn)證信息或者會(huì)話信息。這就意味著基于token認(rèn)證機(jī)制的應(yīng)用不需要去考慮用戶在哪一臺服務(wù)器登錄了,這就為應(yīng)用的擴(kuò)展提供了便利。
流程上是這樣的:
1、用戶使用用戶名密碼來請求服務(wù)器 2、
2、服務(wù)器進(jìn)行驗(yàn)證用戶的信息 服務(wù)器通過驗(yàn)證發(fā)送給用戶一個(gè)token
3、 客戶端存儲(chǔ)token,并在每次請求時(shí)附送上這個(gè)token值 服務(wù)端驗(yàn)證token值,并返回?cái)?shù)據(jù)
4、這個(gè)token必須要在每次請求時(shí)傳遞給服務(wù)端,它應(yīng)該保存在請求頭里,
另外,服務(wù)端要支持CORS(跨來源資源共享)策略,一般我們在服務(wù)端這么做就可以了Access-Control-Allow-Origin:*。
也就是說相比于傳統(tǒng)基于cookie的session,基于token的jwt有以下優(yōu)點(diǎn):
Cookie是不允許垮域訪問的,這一點(diǎn)對Token機(jī)制是不存在的,前提是傳輸?shù)挠脩粽J(rèn)證信息通過HTTP頭傳輸.
無狀態(tài)(也稱:服務(wù)端可擴(kuò)展行):Token機(jī)制在服務(wù)端不需要存儲(chǔ)session信息,因?yàn)門oken 自身包含了所有登錄用戶的信息,只需要在客戶端的cookie或本地介質(zhì)存儲(chǔ)狀態(tài)信息.
更適用CDN: 可以通過內(nèi)容分發(fā)網(wǎng)絡(luò)請求你服務(wù)端的所有資料(如:javascript,HTML,圖片等),而你的服務(wù)端只要提供API即可.
去耦: 不需要綁定到一個(gè)特定的身份驗(yàn)證方案。Token可以在任何地方生成,只要在你的API被調(diào)用的時(shí)候,你可以進(jìn)行Token生成調(diào)用即可.
更適用于移動(dòng)應(yīng)用: 當(dāng)你的客戶端是一個(gè)原生平臺(iOS, Android,Windows 8等)時(shí),Cookie是不被支持的(你需要通過Cookie容器進(jìn)行處理),這時(shí)采用Token認(rèn)證機(jī)制就會(huì)簡單得多。
CSRF:因?yàn)椴辉僖蕾囉贑ookie,所以你就不需要考慮對CSRF(跨站請求偽造)的防范。
性能: 一次網(wǎng)絡(luò)往返時(shí)間(通過數(shù)據(jù)庫查詢session信息)總比做一次HMACSHA256計(jì)算 的Token驗(yàn)證和解析要費(fèi)時(shí)得多.
不需要為登錄頁面做特殊處理: 如果你使用Protractor 做功能測試的時(shí)候,不再需要為登錄頁面做特殊處理.
基于標(biāo)準(zhǔn)化:你的API可以采用標(biāo)準(zhǔn)化的 JSON Web Token (JWT). 這個(gè)標(biāo)準(zhǔn)已經(jīng)存在多個(gè)后端庫(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft).
三、如何使用jwt
安裝方式:
//--save是否需要取決于你是否需要把安裝版本寫進(jìn)package.json
npm install (--save)jwt-simple
- 1
- 2
- 3
使用方式
var jwt = require('jwt-simple');
var payload = { foo: 'bar' };
var secret = 'xxx';
// HS256 secrets are typically 128-bit random strings, for example hex-encoded:
// var secret = Buffer.from('fe1a1915a379f3be5394b64d14794932', 'hex)
// encode
var token = jwt.encode(payload, secret);
// decode
var decoded = jwt.decode(token, secret);
console.log(decoded); //=> { foo: 'bar' }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
注意secret是用于解密的密文,是必須保存在服務(wù)器端。
四、JWT的原理及構(gòu)成
JWT的構(gòu)成:
jwt由三部分構(gòu)成:頭部(header)、載荷(payload, )、簽證(signature).
頭部:jwt的頭部承載兩部分信息:
聲明類型,這里是jwt
聲明加密的算法 通常直接使用 HMAC SHA256
完整的頭部就像下面這樣的JSON:
{
'typ': 'JWT',
'alg': 'HS256'
}
- 1
- 2
- 3
- 4
然后將頭部進(jìn)行base64加密(該加密是可以對稱解密的),構(gòu)成了第一部分.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
playload
- 1
- 2
載荷:就是存放有效信息的地方。,這些有效信息包含三個(gè)部分
標(biāo)準(zhǔn)中注冊的聲明
公共的聲明
私有的聲明
標(biāo)準(zhǔn)中注冊的聲明 (建議但不強(qiáng)制使用) :
iss: jwt簽發(fā)者
sub: jwt所面向的用戶
aud: 接收jwt的一方
exp: jwt的過期時(shí)間,這個(gè)過期時(shí)間必須要大于簽發(fā)時(shí)間
nbf: 定義在什么時(shí)間之前,該jwt都是不可用的.
iat: jwt的簽發(fā)時(shí)間
jti: jwt的唯一身份標(biāo)識,主要用來作為一次性token,從而回避重放攻擊。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
公共的聲明 :
公共的聲明可以添加任何的信息,一般添加用戶的相關(guān)信息或其他業(yè)務(wù)需要的必要信息.但不建議添加敏感信息,因?yàn)樵摬糠衷诳蛻舳丝山饷?
私有的聲明 :
私有聲明是提供者和消費(fèi)者所共同定義的聲明,一般不建議存放敏感信息,因?yàn)閎ase64是對稱解密的,意味著該部分信息可以歸類為明文信息。
定義一個(gè)payload:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
- 1
- 2
- 3
- 4
- 5
然后將其進(jìn)行base64加密,得到Jwt的第二部分。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
signature
- 1
- 2
簽證:這個(gè)簽證信息由三部分header (base64后的)
payload (base64后的)
secret分需要base64加密后的header和base64加密后的payload使用.連接組成的字符串,然后通過header中聲明的加密方式進(jìn)行加鹽secret組合加密,然后就構(gòu)成了jwt的第三部分。
// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
- 1
- 2
- 3
- 4
將這三部分用.連接成一個(gè)完整的字符串,構(gòu)成了最終的jwt:
五、jwt運(yùn)用實(shí)例
/**
* 身份驗(yàn)證.
*/
const jwt = require('jwt-simple');
const moment = require('moment');
//加密過程
var getJwt = function(userId,secret)
{
var expires = moment().add( 7,'days').valueOf();
var token = jwt.encode({
iss: userId,
exp: expires
}, secret);
return token;
}
//解密過程
var testJwt = function(token,secret){
//返回{foo: 'bar'}
var decoded = jwt.decode(token,secret)
return decoded;
}
module.exports = {
create: getJwt,
test: testJwt
}