2019独角兽企业重金招聘Python工程师标准>>>
什么是JWT?
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
jwt的组成
- Header: 标题包含了令牌的元数据,并且在最小包含签名和/或加密算法的类型
- Claims: Claims包含您想要签署的任何信息
- JSON Web Signature (JWS): 在header中指定的使用该算法的数字签名和声明
示例:
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:
有关JWT的详细介绍,请阅读这篇文章:JWT学习(一):什么是JWT?- JSON WEB TOKEN
JJWT
JJWT是一个提供端到端的JWT创建和验证的Java库。永远免费和开源(Apache License,版本2.0),JJWT很容易使用和理解。它被设计成一个以建筑为中心的流畅界面,隐藏了它的大部分复杂性。
- JJWT的目标是最容易使用和理解用于在JVM上创建和验证JSON Web令牌(JWTs)的库。
- JJWT是基于JWT、JWS、JWE、JWK和JWA RFC规范的Java实现。
- JJWT还添加了一些不属于规范的便利扩展,比如JWT压缩和索赔强制。
JJWT 规范兼容
- 创建和解析明文压缩JWTs
- 创建、解析和验证所有标准JWS算法的数字签名压缩JWTs(又称JWSs):
- HS256:使用SHA-256的HMAC
- HS384:使用SHA-384的HMAC
- HS512:使用SHA-512的HMAC
- RS256:使用SHA-256的RSASSA-PKCS-v1_5
- RS384:使用SHA-384的RSASSA-PKCS-v1_5
- RS512:使用SHA-512的RSASSA-PKCS-v1_5
- PS256:使用SHA-256的RSASSA-PSS和使用SHA-256的MGF1
- PS384:使用SHA-384的RSASSA-PSS和使用SHA-384的MGF1
- PS512:使用SHA-512的RSASSA-PSS和使用SHA-512的MGF1
- ES256:使用P-256和SHA-256的ECDSA
- ES384:使用P-384和SHA-384的ECDSA
- ES512:使用P-521和SHA-512的ECDSA
下面我们根据 https://github.com/jwtk/jjwt 上的demo,来介绍下 JJWT 的用法。
jjwt 安装
jjwt 提供了 Maven 和 Gradle 两种构建方式,Maven 配置如下即可使用 JJWT。
1 2 3 4 5 |
|
Gradle 使用方式如下:
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 |
常量类: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,验证是否正确、是否过期等。