Json web token (JWT), 是爲了在網絡應用環境間傳遞聲明而執行的一種基於JSON的開放標準((RFC 7519).該token被設計爲緊湊且安全的,特別適用於分佈式站點的單點登陸(SSO)場景。JWT的聲明通常被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便於從資源服務器獲取資源,也能夠增長一些額外的其它業務邏輯所必須的聲明信息,該token也可直接被用於認證,也可被加密。java
jwt的組成git
示例:github
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Header: { "alg": "HS256", "typ": "JWT" } Claims: { "sub": "1234567890", "name": "John Doe", "admin": true } Signature: base64UrlEncode(Header) + "." + base64UrlEncode(Claims) |
加密生成的token:web
有關JWT的詳細介紹,請閱讀這篇文章:JWT學習(一):什麼是JWT?- JSON WEB TOKEN算法
JJWT是一個提供端到端的JWT建立和驗證的Java庫。永遠免費和開源(Apache License,版本2.0),JJWT很容易使用和理解。它被設計成一個以建築爲中心的流暢界面,隱藏了它的大部分複雜性。apache
下面咱們根據 https://github.com/jwtk/jjwt 上的demo,來介紹下 JJWT 的用法。json
jjwt 提供了 Maven 和 Gradle 兩種構建方式,Maven 配置以下便可使用 JJWT。後端
1 2 3 4 5 |
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency> |
Gradle 使用方式以下:api
1 |
dependencies { compile 'io.jsonwebtoken:jjwt:0.9.0' } |
注意:JJWT依賴於Jackson 2.x. 若是您已經在您的應用程序中使用了舊版本的 Jackson 請升級相關配置。數組
簽發JWT
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public static String createJWT() { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; SecretKey secretKey = generalKey(); JwtBuilder builder = Jwts.builder() .setId(id) // JWT_ID .setAudience("") // 接受者 .setClaims(null) // 自定義屬性 .setSubject("") // 主題 .setIssuer("") // 簽發者 .setIssuedAt(new Date()) // 簽發時間 .setNotBefore(new Date()) // 失效時間 .setExpiration(long) // 過時時間 .signWith(signatureAlgorithm, secretKey); // 簽名算法以及密匙 return builder.compact(); } |
驗證JWT
1 2 3 4 5 6 7 |
public static Claims parseJWT(String jwt) throws Exception { SecretKey secretKey = generalKey(); return Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(jwt) .getBody(); } |
完整示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
import com.google.gson.Gson; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.apache.tomcat.util.codec.binary.Base64; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.util.Date; import java.util.HashMap; import java.util.Map; public class JwtUtil { /** * 由字符串生成加密key * * @return */ public SecretKey generalKey() { String stringKey = Constant.JWT_SECRET; // 本地的密碼解碼 byte[] encodedKey = Base64.decodeBase64(stringKey); // 根據給定的字節數組使用AES加密算法構造一個密鑰 SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES"); return key; } /** * 建立jwt * @param id * @param issuer * @param subject * @param ttlMillis * @return * @throws Exception */ public String createJWT(String id, String issuer, String subject, long ttlMillis) throws Exception { // 指定簽名的時候使用的簽名算法,也就是header那部分,jjwt已經將這部份內容封裝好了。 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 生成JWT的時間 long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); // 建立payload的私有聲明(根據特定的業務須要添加,若是要拿這個作驗證,通常是須要和jwt的接收方提早溝通好驗證方式的) Map<String, Object> claims = new HashMap<>(); claims.put("uid", "123456"); claims.put("user_name", "admin"); claims.put("nick_name", "X-rapido"); // 生成簽名的時候使用的祕鑰secret,切記這個祕鑰不能外露哦。它就是你服務端的私鑰,在任何場景都不該該流露出去。 // 一旦客戶端得知這個secret, 那就意味着客戶端是能夠自我簽發jwt了。 SecretKey key = generalKey(); // 下面就是在爲payload添加各類標準聲明和私有聲明瞭 JwtBuilder builder = Jwts.builder() // 這裏其實就是new一個JwtBuilder,設置jwt的body .setClaims(claims) // 若是有私有聲明,必定要先設置這個本身建立的私有的聲明,這個是給builder的claim賦值,一旦寫在標準的聲明賦值以後,就是覆蓋了那些標準的聲明的 .setId(id) // 設置jti(JWT ID):是JWT的惟一標識,根據業務須要,這個能夠設置爲一個不重複的值,主要用來做爲一次性token,從而回避重放攻擊。 .setIssuedAt(now) // iat: jwt的簽發時間 .setIssuer(issuer) // issuer:jwt簽發人 .setSubject(subject) // sub(Subject):表明這個JWT的主體,即它的全部人,這個是一個json格式的字符串,能夠存放什麼userid,roldid之類的,做爲何用戶的惟一標誌。 .signWith(signatureAlgorithm, key); // 設置簽名使用的簽名算法和簽名使用的祕鑰 // 設置過時時間 if (ttlMillis >= 0) { long expMillis = nowMillis + ttlMillis; Date exp = new Date(expMillis); builder.setExpiration(exp); } return builder.compact(); } /** * 解密jwt * * @param jwt * @return * @throws Exception */ public Claims parseJWT(String jwt) throws Exception { SecretKey key = generalKey(); //簽名祕鑰,和生成的簽名的祕鑰如出一轍 Claims claims = Jwts.parser() //獲得DefaultJwtParser .setSigningKey(key) //設置簽名的祕鑰 .parseClaimsJws(jwt).getBody(); //設置須要解析的jwt return claims; } public static void main(String[] args) { User user = new User("tingfeng", "bulingbuling", "1056856191"); String subject = new Gson().toJson(user); try { JwtUtil util = new JwtUtil(); String jwt = util.createJWT(Constant.JWT_ID, "Anson", subject, Constant.JWT_TTL); System.out.println("JWT:" + jwt); System.out.println("\n解密\n"); Claims c = util.parseJWT(jwt); System.out.println(c.getId()); System.out.println(c.getIssuedAt()); System.out.println(c.getSubject()); System.out.println(c.getIssuer()); System.out.println(c.get("uid", String.class)); } catch (Exception e) { e.printStackTrace(); } } } |
常量類:Constant.java
1 2 3 4 5 6 7 8 9 10 11 |
import java.util.UUID; public class Constant { public static final String JWT_ID = UUID.randomUUID().toString(); /** * 加密密文 */ public static final String JWT_SECRET = "woyebuzhidaoxiediansha"; public static final int JWT_TTL = 60*60*1000; //millisecond } |
輸出示例:
1 2 3 4 5 6 7 8 9 |
JWT:eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiIxMjM0NTYiLCJzdWIiOiJ7XCJuaWNrbmFtZVwiOlwidGluZ2ZlbmdcIixcIndlY2hhdFwiOlwiYnVsaW5nYnVsaW5nXCIsXCJxcVwiOlwiMTA1Njg1NjE5MVwifSIsInVzZXJfbmFtZSI6ImFkbWluIiwibmlja19uYW1lIjoiWC1yYXBpZG8iLCJpc3MiOiJBbnNvbiIsImV4cCI6MTUyMjMxNDEyNCwiaWF0IjoxNTIyMzEwNTI0LCJqdGkiOiJhNGQ5MjA0Zi1kYjM3LTRhZGYtODE0NS1iZGNmMDAzMzFmZjYifQ.B5wdY3_W4MZLj9uBHSYalG6vmYwdpdTXg0otdwTmU4U 解密 a4d9204f-db37-4adf-8145-bdcf00331ff6 Thu Mar 29 16:02:04 CST 2018 {"nickname":"tingfeng","wechat":"bulingbuling","qq":"1056856191"} Anson 123456 |
通常咱們把驗證操做做爲中間件或者攔截器就好了,請求後端API接口時候,經過過濾器獲取header中的token,驗證是否正確、是否過時等。