JWT(JSON Web Token) 多網站的單點登入,放棄session

來源:互聯網
上載者:User

標籤:proc   invalid   私密金鑰   複雜   ror   業務   mdk   傳統   產生   

多個網站之間的登入資訊共用, 一種解決方案是基於cookie - session的登入認證方式,這種方式跨域比較複雜。

另一種替代方案是採用基於演算法的認證方式, JWT(json web token)的方式。

參考連結:

  • http://www.tuicool.com/articles/IRJnaa
  • https://coderwall.com/p/8wrxfw/goodbye-php-sessions-hello-json-web-tokens
一、概念和定義1、什麼是jwt?Json web token (JWT), 是為了在網路應用環境間傳遞聲明而執行的一種基於JSON的開放標準((RFC 7519).該token被設計為緊湊且安全的,特別適用於分布式網站的單點登入(SSO)情境。JWT一般用來在身份提供者和服務提供者間傳遞被認證的使用者身份資訊,以便於從資原始伺服器擷取資源,也可以增加一些額外的其它商務邏輯所必須的聲明資訊,JWT也可直接被用於認證,也可被加密。2、背景和意義

說起JWT,我們應該對基於token的認證方式和傳統的session認證方式進行一下比較。

2.1、傳統的session認證

我們知道,http協議本身是一種無狀態的協議,而這就意味著如果使用者向我們的應用提供了使用者名稱和密碼來進行使用者認證,那麼下一次請求時,使用者還要再一次進行使用者認證才行,因為根據http協議,我們並不能知道是哪個使用者發出的請求,

所以為了讓我們的應用能識別是哪個使用者發出的請求,我們只能在伺服器儲存一份使用者登入的資訊,這份登入資訊會在響應時傳遞給瀏覽器,告訴其儲存為cookie,以便下次請求時發送給我們的應用,這樣我們的應用就能識別請求來自哪個使用者了,

這就是傳統的基於session認證。但是這種基於session的認證使應用本身很難得到擴充,

隨著不同用戶端使用者的增加,獨立的伺服器已無法承載更多的使用者,而這時候基於session認證應用的問題就會暴露出來. 基於session認證所顯露的問題:

Session: 每個使用者經過我們的應用認證之後,我們的應用都要在服務端做一次記錄,以方便使用者下次請求的鑒別,通常而言session都是儲存在記憶體中,而隨著認證使用者的增多,服務端的開銷會明顯增大。

擴充性: 使用者認證之後,服務端做認證記錄,如果認證的記錄被儲存在記憶體中的話,這意味著使用者下次請求還必須要請求在這台伺服器上,這樣才能拿到授權的資源,這樣在分布式的應用上,相應的限制了負載平衡器的能力。這也意味著限制了應用的擴充能力。

CSRF: 因為是基於cookie來進行使用者識別的, cookie如果被截獲,使用者就會很容易受到跨站請求偽造的攻擊。

2.2、基於token的鑒權機制

基於token的鑒權機制類似於http協議也是無狀態的,它不需要在服務端去保留使用者的認證資訊或者會話資訊。這就意味著基於token認證機制的應用不需要去考慮使用者在哪一台伺服器登入了,這就為應用的擴充提供了便利。

流程上是這樣的:

  1. 使用者使用使用者名稱密碼來請求伺服器
  2. 伺服器進行驗證使用者的資訊,驗證通過伺服器發送給使用者一個token
  3. 用戶端儲存token,並在每次請求時附送上這個token值
  4. 服務端驗證token值,並返回使用者要訪問的資料

這個token必須要在每次請求時傳遞給服務端,它應該儲存在要求標頭裡,另外,服務端要支援CORS(跨來源資源共用)策略,一般我們在服務端這麼做就可以了Access-Control-Allow-Origin: *。

3、jwt的構成

JWT長什麼樣?JWT是由三段資訊構成的,將這三段資訊文本用.連結一起就構成了Jwt字串。就像這樣:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

第一部分我們稱它為頭部(header),第二部分我們稱其為載荷(payload, 類似于飛機上承載的物品),第三部分是簽證(signature).

3.1、header

jwt的頭部承載兩部分資訊:

宣告類型,這裡是jwt
聲明加密的演算法 通常直接使用 HMAC SHA256

完整的頭部就像下面這樣的JSON:

{
    ‘typ‘: ‘JWT‘,
    ‘alg‘: ‘HS256‘
}
然後將頭部進行base64加密(該加密是可以對稱解密的),構成了第一部分:     eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

3.2、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

3.3、signature

jwt的第三部分是一個簽證資訊,這個簽證資訊由三部分組成:

  • 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:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

注意:secret是儲存在伺服器端的,jwt的簽發產生也是在伺服器端的,secret就是用來進行jwt的簽發和jwt的驗證,所以,它就是你服務端的私密金鑰,在任何情境都不應該流露出去。一旦用戶端得知這個secret, 那就意味著用戶端是可以自我簽發jwt了。 

二、JWT組裝過程

JWT(JSON Web Token)是一個非常輕巧的規範,這個規範允許我們使用JWT在使用者和伺服器之間傳遞安全可靠的資訊。一個JWT實際上就是一個字串,它由三部分組成,頭部、載荷與簽名。

1、載荷(Payload)

我們先將使用者認證的操作描述成一個JSON對象。其中添加了一些其他的資訊,協助今後收到這個JWT的伺服器理解這個JWT。
{
    "sub": "1",
    "iss": "http://localhost:8000/auth/login",
    "iat": 1451888119,
    "exp": 1454516119,
    "nbf": 1451888119,
    "jti": "37c107e4609ddbcc9c096ea5ee76c667"
}
這裡面的前6個欄位都是由JWT的標準所定義的。

  • sub: 該JWT所面向的使用者
  • iss: 該JWT的簽發者
  • iat(issued at): 在什麼時候簽發的token
  • exp(expires): token什麼時候到期
  • nbf(not before):token在此時間之前不能被接收處理
  • jti:JWT ID為web token提供唯一標識

將上面的JSON對象進行base64編碼可以得到下面的字串:

eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwMDFcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNDUxODg4MTE5LCJleHAiOjE0NTQ1MTYxMTksIm5iZiI6MTQ1MTg4ODExOSwianRpIjoiMzdjMTA3ZTQ2MDlkZGJjYzljMDk2ZWE1ZWU3NmM2NjcifQ
這個字串我們將它稱作JWT的Payload(載荷)。

如果你使用Node.js,可以用Node.js的包base64url來得到這個字串:

var base64url = require(‘base64url‘)
var header = {
"from_user": "B",
"target_user": "A"
}
console.log(base64url(JSON.stringify(header)))

註:Base64是一種編碼,也就是說,它是可以被翻譯回原來的樣子來的。它並不是一種加密過程。

2、頭部(Header)

JWT還需要一個頭部,頭部用於描述關於該JWT的最基本的資訊,例如其類型以及簽名所用的演算法等。這也可以被表示成一個JSON對象:

{
"typ": "JWT",
"alg": "HS256"
}
在這裡,我們說明了這是一個JWT,並且我們所用的簽名演算法(後面會提到)是HS256演算法。
對它也要進行Base64編碼,之後的字串就成了JWT的Header(頭部):
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

3、簽名(簽名)

將上面的兩個編碼後的字串都用句號.串連在一起(頭部在前),就形成了:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvbG9jYWx
ob3N0OjgwMDFcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNDUxODg4MTE5LCJleHAiOjE0NTQ1MTYxMTksIm5iZiI6MTQ1MTg4OD
ExOSwianRpIjoiMzdjMTA3ZTQ2MDlkZGJjYzljMDk2ZWE1ZWU3NmM2NjcifQ
最後,我們將上面拼接完的字串用HS256演算法進行加密。在加密的時候,我們還需要提供一個密鑰(secret):

HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
這樣就可以得到我們加密後的內容:wyoQ95RjAyQ2FF3aj8EvCSaUmeP0KUqcCJDENNfnaT4
這一部分又叫做簽名。

4、組裝<header>.<payload>.<簽名>

最後將這一部分簽名也拼接在被簽名的字串後面,我們就得到了完整的JWT:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvbG9jYWx
ob3N0OjgwMDFcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNDUxODg4MTE5LCJleHAiOjE0NTQ1MTYxMTksIm5iZiI6MTQ1MTg4OD
ExOSwianRpIjoiMzdjMTA3ZTQ2MDlkZGJjYzljMDk2ZWE1ZWU3NmM2NjcifQ.wyoQ95RjAyQ2FF3aj8EvCSaUmeP0KUqc

三、附錄

Goodbye PHP Sessions, Hello JSON Web Tokens
REST API‘s are meant to be stateless. What that means is that each request from a client should include all the information needed to process the request. In other words, if you are writing a REST API in PHP then you should not be using $_SESSION to store data about the client‘s session. But then how do we remember if a client is logged in or anything else about their state? The only possibility is that the client must be tasked with keeping track of the state. How could this ever be done securely? The client can‘t be trusted!

Enter JSON web tokens. A JSON web token is a bit of JSON, perhaps something that looks like this:

{
"user": "alice",
"email": "[email protected]"
}
Of course, we can‘t just give this to a client and have them give it back to us without some sort of assurance that it hasn‘t been tampered with. After all, what if they edit the token as follows:

{
"user": "administrator",
"email": "[email protected]"
}
The solution to this is that JSON web tokens are signed by the server. If the client tampers with the data then the token‘s signature will no longer match and an error can be raised.

The JWT PHP class makes this easy to do. For example, to create a token after the client successfully logs in, the following code could be used:

$token = array();
$token[‘id‘] = $id;
echo JWT::encode($token, ‘secret_server_key‘);
And then on later API calls the token can be retrieved and verified by this code:

$token = JWT::decode($_POST[‘token‘], ‘secret_server_key‘);
echo $token->id;
If the token has been tampered with then $token will be empty there will not be an id available. The JWT class makes sure that invalid data is never made available. If the token is tampered with, it will be unusable. Pretty simple stuff!

You can get the PHP JWT class as a single file from: https://github.com/rmcdaniel/angular-codeigniter-seed/blob/master/api/application/helpers/jwt_helper.php

as it is used by the AngularJS CodeIgniter Seed project:

https://github.com/rmcdaniel/angular-codeigniter-seed

or the original code from:

https://github.com/luciferous/jwt

Written by Richard McDaniel

JWT(JSON Web Token) 多網站的單點登入,放棄session

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.