基於前後端分離的身份認證方式——JWT

目錄:
一、什麼是JWT
二、我們爲什麼要使用JWT(與傳統的session認證有何區別)
三、如何使用JWT
四、JWT的構成及原理
五、JWT加解密實例

一、什麼是JWT
JWT——Json web token
是爲了在網絡應用環境間傳遞聲明而執行的一種基於JSON的開放標準,可實現無狀態、分佈式的Web應用授權。

二、我們爲什麼需要JWT?
首先,當前後端分離時我們會因爲同源策略而無法設置cookie和sessionid。當然了我們有很多方式去解決這個問題,比如反向代理和jsonp等。但這仍然不如直接使用jwt來的簡便。其次就是要說到jwt與傳統的身份認證相比有什麼優勢了。
回答這個問題需要來看看基於token的認證和傳統的session認證的區別
1、傳統的session認證

我們知道,http協議本身是一種無狀態的協議,而這就意味着如果用戶向我們的應用提供了用戶名和密碼來進行用戶認證,那麼下一次請求時,用戶還要再一次進行用戶認證才行,因爲根據http協議,我們並不能知道是哪個用戶發出的請求,所以爲了讓我們的應用能識別是哪個用戶發出的請求,我們只能在服務器存儲一份用戶登錄的信息,這份登錄信息會在響應時傳遞給瀏覽器,告訴其保存爲cookie,以便下次請求時發送給我們的應用,這樣我們的應用就能識別請求來自哪個用戶了,這就是傳統的基於session認證。

但是這種基於session的認證使應用本身很難得到擴展,隨着不同客戶端用戶的增加,獨立的服務器已無法承載更多的用戶,而這時候基於session認證應用的問題就會暴露出來.

2、基於token的鑑權機制

基於token的鑑權機制類似於http協議也是無狀態的,它不需要在服務端去保留用戶的認證信息或者會話信息。這就意味着基於token認證機制的應用不需要去考慮用戶在哪一臺服務器登錄了,這就爲應用的擴展提供了便利。

流程上是這樣的:

1、用戶使用用戶名密碼來請求服務器 2、
2、服務器進行驗證用戶的信息 服務器通過驗證發送給用戶一個token
3、 客戶端存儲token,並在每次請求時附送上這個token值 服務端驗證token值,並返回數據
4、這個token必須要在每次請求時傳遞給服務端,它應該保存在請求頭裏,
另外,服務端要支持CORS(跨來源資源共享)策略,一般我們在服務端這麼做就可以了Access-Control-Allow-Origin:*。

也就是說相比於傳統基於cookie的session,基於token的jwt有以下優點:

Cookie是不允許垮域訪問的,這一點對Token機制是不存在的,前提是傳輸的用戶認證信息通過HTTP頭傳輸.
無狀態(也稱:服務端可擴展行):Token機制在服務端不需要存儲session信息,因爲Token 自身包含了所有登錄用戶的信息,只需要在客戶端的cookie或本地介質存儲狀態信息.
更適用CDN: 可以通過內容分發網絡請求你服務端的所有資料(如:javascript,HTML,圖片等),而你的服務端只要提供API即可.
去耦: 不需要綁定到一個特定的身份驗證方案。Token可以在任何地方生成,只要在你的API被調用的時候,你可以進行Token生成調用即可.
更適用於移動應用: 當你的客戶端是一個原生平臺(iOS, Android,Windows 8等)時,Cookie是不被支持的(你需要通過Cookie容器進行處理),這時採用Token認證機制就會簡單得多。
CSRF:因爲不再依賴於Cookie,所以你就不需要考慮對CSRF(跨站請求僞造)的防範。
性能: 一次網絡往返時間(通過數據庫查詢session信息)總比做一次HMACSHA256計算 的Token驗證和解析要費時得多.
不需要爲登錄頁面做特殊處理: 如果你使用Protractor 做功能測試的時候,不再需要爲登錄頁面做特殊處理.
基於標準化:你的API可以採用標準化的 JSON Web Token (JWT). 這個標準已經存在多個後端庫(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft).

三、如何使用jwt

安裝方式:

//--save是否需要取決於你是否需要把安裝版本寫進package.json
npm install --save)jwt-simple

使用方式

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' } 

注意secret是用於解密的密文,是必須保存在服務器端。

四、JWT的原理及構成
JWT的構成:
jwt由三部分構成:頭部(header)、載荷(payload, )、簽證(signature).

頭部:jwt的頭部承載兩部分信息:

聲明類型,這裏是jwt
聲明加密的算法 通常直接使用 HMAC SHA256
完整的頭部就像下面這樣的JSON:

{
  'typ': 'JWT',
  'alg': 'HS256'
}

然後將頭部進行base64加密(該加密是可以對稱解密的),構成了第一部分.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
playload

載荷:就是存放有效信息的地方。,這些有效信息包含三個部分

標準中註冊的聲明
公共的聲明
私有的聲明
標準中註冊的聲明 (建議但不強制使用) :

iss: jwt簽發者
sub: jwt所面向的用戶
aud: 接收jwt的一方
exp: jwt的過期時間,這個過期時間必須要大於簽發時間
nbf: 定義在什麼時間之前,該jwt都是不可用的.
iat: jwt的簽發時間
jti: jwt的唯一身份標識,主要用來作爲一次性token,從而回避重放攻擊。

公共的聲明 :
公共的聲明可以添加任何的信息,一般添加用戶的相關信息或其他業務需要的必要信息.但不建議添加敏感信息,因爲該部分在客戶端可解密.

私有的聲明 :
私有聲明是提供者和消費者所共同定義的聲明,一般不建議存放敏感信息,因爲base64是對稱解密的,意味着該部分信息可以歸類爲明文信息。

定義一個payload:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

然後將其進行base64加密,得到Jwt的第二部分。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
signature

簽證:這個簽證信息由三部分header (base64後的)
payload (base64後的)
secret分需要base64加密後的header和base64加密後的payload使用.連接組成的字符串,然後通過header中聲明的加密方式進行加鹽secret組合加密,然後就構成了jwt的第三部分。

// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

將這三部分用.連接成一個完整的字符串,構成了最終的jwt:

五、jwt運用實例

/**
 * 身份驗證.
 */
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
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章