JWT全稱JSON Web Token,是一個緊湊的,自包含的,安全的信息交換協議。JWT有不少方面的應用,例如權限認證,信息交換等。本文將簡單介紹JWT登陸權限認證的一個實例操做。java
JWT由頭部(Header),負載(Payload)和簽名(Signature)三部分組成。其中頭部包含了JWT的聲明信息,例如簽名所用的算法等。算法
{ "alg": "HS256", "typ": "JWT" }
負載部分是負責信息的承載,在通訊過程當中,咱們將要交換的信息放置於負載部分。json
{ "sub": "1234567890", "name": "John Doe", "admin": true }
簽名部分是JWT安全的保障,在傳輸過程當中,頭部和負載部分會通過Base64編碼在網絡中明文傳輸,既然是明文,爲了保障信息在傳輸過程當中不被篡改。JWT會對編碼以後的頭部和負載進行一個消息簽名。安全
Signature = HMACSHA256(header + "." + payload + secret)
通過簽名以後的JWT保證了數據不會被劫持並篡改。其中secret極爲重要,即便有人劫持了消息,在不知道secret的狀況下,沒法簽名出一個有效的JWT。網絡
JWT由三部分組成:頭部,負載和簽名。最終的JWT字串能夠呈現出這三部分,在JWT中,.
爲分割各部分的分隔符,按照順序依次爲頭部,負載和簽名。不過這時你已經看不到JSON的形式,頭部和負載會通過Base64編碼,最終獲得一個字符串。不過Base64並非加密算法,它是一種編碼格式,你能夠經過必定的工具解碼以後就會獲得相應的JSON字串。架構
在互聯網Web應用開發中,最爲常見的一項工做就是認證用戶,狀態化HTTP請求和授予資源訪問權限。app
其中認證用戶經過用戶名、密碼的登陸操做實現,可是HTTP請求是無狀態的,爲了標記已經登陸成功的用戶,咱們能夠經過設置SESSION_ID
,COOKIE
來標記認證過的請求。可是它們都須要在服務端額外的存儲這些SESSION_ID
和COOKIE
。在最初的單體應用架構中,存儲能夠在服務端中開闢一塊內存,以鍵值對的形式存儲SESSION_ID
,COOKIE
與用戶的映射關係。分佈式架構中可使用REDIS
等中間件來實現SESSION
共享。分佈式
爲什要存儲映射關係?經過SESSION_ID
和COOKIE
咱們不可以直接獲得用戶信息嗎?其實並非不能夠,而是不安全,SESSION_ID
和COOKIE
是能夠供用戶自由操做的。若是直接明文形式的將用戶信息寫入其中,那麼這些信息極有可能會被篡改。因此一般咱們會在服務端生成隨機字串,寫入到SESSION_ID
或COOKIE
中,再將隨機字串與用戶之間創建一個映射。這樣,客戶端的用戶並不能隨意篡改這些信息了。由於並不知道其餘用戶的隨機字串是什麼。工具
JWT也是字串,只不過是編碼以後的字串,並且這個字串是安全的。由於它是被服務端簽名認證的。若是有用戶修改的痕跡,那麼服務端在檢驗時會發現字串被修改。正是這一特性保障了認證的安全性。ui
在業務中,JWT能夠實現雙向校驗,即通訊雙方均可以校驗JWT有無被篡改。實現方式是經過非對稱加密技術。
登錄成功以後,服務端簽發JWT代碼:
final long expireTime = 1000 * 60 * 60 * 4; //JWT有效期爲4小時 final String loginWebToken = JWT.create() .withIssuer(configurationProperties.getJwtLoginIssuer()) .withClaim("username", vo.getUsername()) // 負載部分 .withClaim("user_id", admin.getId()) .withExpiresAt(new Date(System.currentTimeMillis() + expireTime)) // 設置有效期 .sign(Algorithm.HMAC256(configurationProperties.getJwtSignKey())); // 進行簽名
簽發的JWT能夠直接返回給客戶端,有客戶端JS代碼寫入下次請求的指定位置,也能夠由服務端寫入SESSION_ID
或COOKIE
中。
校驗JWT代碼,攔截未認證的請求:
try { final String loginWebToken = request.getHeader("Authorization"); // final DecodedJWT decodeToken = JWT.decode(loginWebToken); // String username = decodeToken.getClaim("username").asString(); // JWT驗證 JWT.require(Algorithm.HMAC256(configurationProperties.getJwtSignKey())) .withIssuer(configurationProperties.getJwtLoginIssuer()) .build().verify(loginWebToken); return true; }catch (Exception ex) { Response re = new Response() .setMsg("權限受限,請登錄") .setData(null) .setSuccess(false); response.reset(); response.setCharacterEncoding("UTF-8"); response.setContentType("application/json;charset=UTF-8"); response.getWriter().print(JSON.toJSONString(re)); return false; }
業務中使用JWT實現HTTP狀態化,服務不一樣認證用戶:
@GetMapping("/user/info") public Response getUserInfo(HttpServletRequest req) { final String loginWebToken = request.getHeader("Authorization"); final DecodedJWT jwt = JWT.decode(loginWebToken); log.info("歡迎{}使用系統", jwt.getClaim("username"); return userService.getUserInfo(jwt.getClaim("user_id")); }