瞭解如何使用JSON Web令牌(JWT)實現訪問授權驗證
JSON Web令牌(JWT)可以輕鬆地在服務(應用程式/站點的內部和外部)之間傳送只讀簽名的 “ 宣告 ” 。宣告是任何一些你希望別人能夠讀取/驗證的資料,但不可改變。
IETF的定義:
“ JSON Web Token (JWT)是一種緊湊的 URL安全 方式,用來表示在不同部分之間進行傳輸的宣告,它可被編碼 為 JSON物件 , 使用JJSON Web Signature (JWS)進行數字 簽名 .“
要識別/驗證應用程式中的人員身份,需要在頁面(或API端點)的標頭Header或網址中放置基於標準的令牌,以證明該使用者已登入並允許其訪問所需內容。
例: https://www.yoursite.com/private-content/?token=eyJ0eXAiOiJKV1Qi.eyJrZXkiOi.eUiabuiKv
JWT是一串“url safe”字元,用於編碼資訊,令牌有三個元件(以句點分隔)(此處顯示為多行以便於閱讀,但用作單個文字字串)
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 // header .eyJrZXkiOiJ2YWwiLCJpYXQiOjE0MjI2MDU0NDV9 // payload .eUiabuiKv-8PYk2AkGY4Fb5KMZeorYBLw261JPQD5lM // signature |
1. Header
JWT的第一部分是一個簡單JavaScript物件的編碼字串表示,它描述了令牌以及所使用的雜湊演算法。
2.Payload
JWT的第二部分構成了令牌的核心。Payload有效載荷長度與您在JWT中儲存的資料量成比例。一般的經驗法則是:將最小值儲存在JWT中。
3. Signature簽名
JWT的第三部分也是最後一部分是基於Header(第一部分)和正文(第二部分)生成的簽名,用於驗證 JWT是否有效。
什麼是“宣告”?
宣告是預定義的鍵及其值:
- iss:令牌的發行者
- exp:到期時間戳(拒絕已過期的令牌)。注意:如規範中所定義,這必須以秒為單位。
- iat:JWT釋出的時間。可用於確定JWT的年齡
- nbf:“not before”是令牌變為活動狀態的未來時間。
- jti:JWT的唯一識別符號。用於防止JWT被重複使用或重放。
- sub:令牌的主題(很少使用)
- aud:令牌的受眾(也很少使用)
案例
一個簡單的例子。(完整原始碼位於/ example目錄中):
https://jwt.herokuapp.com/
該伺服器架構使用node.js http伺服器,我們在/example/server.js中建立了4個端點:
- / home:主頁(不是必需的,但我們的登入表單是。)
- / auth:驗證訪問者(如果失敗則返回錯誤+登入表單)
- / private:我們的受限內容 - 需要登入(有效會話令牌)才能看到此頁面
- / logout:使令牌無效並登出使用者(防止重新使用舊令牌)
我們故意讓server.js儘可能簡單:
- 可讀性
- 可維護性
- 可測試性(所有輔助/處理程式方法單獨測試)
幫助方法:
所有輔助方法都儲存在/example/lib/helpers.js中 。兩個最有趣/相關的方法是(這裡顯示的簡化版本):
// generate the JWT function generateToken(req){ var token = jwt.sign({ auth: 'magic', agent: req.headers['user-agent'], exp: Math.floor(new Date().getTime()/1000) + 7*24*60*60; // Note: in seconds! }, secret); // secret is defined in the environment variable JWT_SECRET return token; } |
當使用者認證時產生了JWT令牌(這隨後被髮送回客戶端在授權頭用於後續請求)。
以及:
// validate the token supplied in request header function validate(req, res) { var token = req.headers.authorization; try { var decoded = jwt.verify(token, secret); } catch (e) { return authFail(res); } if(!decoded || decoded.auth !== 'magic') { return authFail(res); } else { return privado(res, token); } } |
以上是檢查客戶端提供的JWT是有效的?如果有效則向請求者顯示私有(“privado”)內容,如果不是則呈現authFail 錯誤頁面。
注意:是的,這兩種方法都是同步的。但是,鑑於這些方法都不需要任何 I / O 或 網路請求,因此同步計算它們非常安全。
問題
問:如果我把JWT放在URL或Header中它是否安全?
不安全。除非你使用SSL / TLS來加密連線,明確地傳送令牌始將是不安全的(令牌可以被截獲並重新使用)。一個天真的 “ 緩解 ”方式是向令牌新增可驗證的 “宣告”,例如檢查請求是否來自相同的瀏覽器(使用者代理), IP地址或更高階的“ 瀏覽器指紋 ” http://programmers.stackexchange.com/a/122385
解決的辦法是下面任一個:
- 使用一次性令牌(在點選連結後就過期失效)
- 不要使用需要高度安全性的url-tokens。(例如:不要向某人傳送允許他們執行交易的連結)
url中一次性JWT令牌的用例是:
- 帳戶驗證 - 當您在網站上註冊後透過電子郵件向其傳送連結時。 https://yoursite.co/account/verify?token=jwt.goes.here
- 密碼重置 - 確保重新設定密碼的人員可以訪問屬於該帳戶的電子郵件。 https://yoursite.co/account/reset-password?token=jwt.goes.here
這兩者都是一次性令牌的好的使用方式(在點選之後到期)。
問:我們如何使會話無效?
使用應用的使用者的裝置(手機/平板電腦/膝上型電腦) 被盜。你如何使他們使用的令牌無效?
JWT背後的想法是令牌是無狀態的, 它們可以由叢集中的任何節點計算,並且在沒有(或慢的)請求資料庫的情況下進行驗證。
將令牌儲存在資料庫中?
1. LevelDB!如果您的應用程式很小或者您不想執行Redis伺服器,則可以透過使用LevelDB獲得Redis的大部分好處:http://leveldb.org/
我們可以任意儲存有效的DB令牌或者 我們可以儲存無效令牌。這兩個都需要往返DB以檢查是否有效/無效。所以我們更喜歡儲存所有令牌,並將令牌的 有效屬性從true更新為false。
儲存在LevelDB中的示例記錄:
"GUID" : { "auth" : "true", "created" : "timestamp", "uid" : "1234" } |
我們將透過其GUID查詢此記錄:
var db = require('level'); db.get(GUID, function(err, record){ // pseudo-code if(record.auth){ // display private content } else { // show error message } }); |
請參閱:example / lib / helpers.js 驗證詳細資訊的方法。
2. Redis
Redis是儲存令牌的可擴充套件方式。
問:返回訪問者(會話之間沒有狀態)
Cookie儲存在客戶端上,並在每次請求時由瀏覽器傳送到伺服器。如果此人關閉瀏覽器,則會保留Cookie,因此可以在停止的位置繼續操作,而無需再次登入。但是,cookie將在與路徑和釋出域匹配的所有請求上傳送,包括不需要的影像和css。
localStorage 提供了一種更好的機制,用於在瀏覽器會話期間和之間儲存令牌。
1.基於瀏覽器的應用程式
儲存JWT有兩種選擇:
- 使用localStorage在客戶端儲存JWT(意味著您需要記住在authorization標題中傳送JWT以用於後續的http / ajax請求)
- 將您的JWT儲存在cookie中(設定並忘記)
(我們顯然更喜歡無cookie方法。但如果做得好,cookie仍然在現代網路應用程式中佔有一席之地!)
2.程式(API)訪問
訪問您的API的其他服務必須將令牌儲存在檢索系統中(例如:Redis或SQLite用於移動應用程式)並在每個請求時發回令牌。
如何生成金鑰?
由於JSON網路令牌(JWT)不使用非對稱加密簽名,你不能使用ssh-keygen生成使用金鑰,您可以輕鬆使用強密碼,例如: https://www.grc.com/passwords.htm。只要它很長且隨機。碰撞的可能性(因此有人能夠解碼您編碼的JSON)非常低。如果你將兩個強密碼(字串)連線在一起,你將擁有一個128位的ASCII字串。因此,碰撞的可能性小於宇宙中[url=http://en.wikipedia.org/wiki/Observable_universeMatter_content_.E2.80.94_number_of_atoms]的原子數[/url]。
要使用Node的加密庫快速輕鬆地建立金鑰,請執行此命令。
node -e "console.log(require('crypto').randomBytes(32).toString('hex'));"
換句話說,您可以使用RSA金鑰,但你並不需要。
您需要記住的主要事項是:不要與不在核心的人(“ DevOps團隊 ”)共享金鑰,或者在提交給GitHub時突然釋出金鑰!
相關文章
- 【Azure Developer】如何驗證 Azure AD的JWT Token (JSON Web 令牌)?DeveloperJWTJSONWeb
- python使用JWT(json web token)實現驗證PythonJWTJSONWeb
- Spring Security OAuth2.0認證授權三:使用JWT令牌SpringOAuthJWT
- 使用JWT實現Spring Boot令牌認證JWTSpring Boot
- .Net Core官方的 JWT 授權驗證JWT
- Laravel 5.7 和 JSON Web 令牌(tymon/jwt-auth) - 使用者認證LaravelJSONWebJWT
- 使用 Jwt-Auth 實現 API 使用者認證以及無痛重新整理訪問令牌JWTAPI
- Laravel 5.5 使用 Jwt-Auth 實現 API 使用者認證以及無痛重新整理訪問令牌LaravelJWTAPI
- OAuth2.0實戰!使用JWT令牌認證!OAuthJWT
- Spring Boot Security OAuth2 實現支援JWT令牌的授權伺服器Spring BootOAuthJWT伺服器
- Dotnet core使用JWT認證授權最佳實踐(一)JWT
- Dotnet core使用JWT認證授權最佳實踐(二)JWT
- Laravel + JWT 實現 API 跨域授權LaravelJWTAPI跨域
- express基於JWT實現使用者登陸授權ExpressJWT
- 基於Docker的MongoDB實現授權訪問DockerMongoDB
- [譯] Angular 安全 —— 使用 JSON 網路令牌(JWT)的身份認證:完全指南AngularJSONJWT
- golang 中使用 JWT 實現登入驗證GolangJWT
- 【ASP.NET Core學習】使用JWT認證授權ASP.NETJWT
- 瞭解JWT認證JWT
- 驗證與授權
- 網路驗證之授權碼使用
- golang 基於 jwt 實現的登入授權GolangJWT
- 譯見|構建使用者管理微服務(五):使用 JWT 令牌和 Spring Security 來實現身份驗證微服務JWTSpring
- 通過 Passport 實現 API 請求認證(移動端的密碼授權令牌)PassportAPI密碼
- 透過 Passport 實現 API 請求認證(移動端的密碼授權令牌)PassportAPI密碼
- MySQL建立使用者授權訪問MySql
- 如何優雅的使用切面和註解實現許可權驗證
- technology-integration(七)---使用SpringSecurity做JWT認證授權SpringGseJWT
- Microsoft Graph for Office 365 - 身份驗證路線圖和訪問令牌ROS
- JBOSS未授權訪問
- node學習---jwt實現驗證使用者身份JWT
- Gitlab怎麼使用訪問令牌訪問Gitlab
- 【漏洞復現】Redis未授權訪問漏洞Redis
- 理解JWT(JSON Web Token)認證及python實踐JWTJSONWebPython
- asp.net core 3.1多種身份驗證方案,cookie和jwt混合認證授權ASP.NETCookieJWT
- 如何在SpringBoot中整合JWT(JSON Web Token)鑑權Spring BootJWTJSONWeb
- Spring Security 實戰乾貨:使用 JWT 認證訪問介面SpringJWT
- Django(59)驗證和授權Django