JSON Web Token (JWT) 是一种开放标准 ( RFC 7519 ),我们常说的jwt token(令牌),其实就是按照jwt制定的标准生成的字符串令牌。
jwt组成
jwt由header,playload,signature三部分组成,令牌最终的格式像这样:xxxxx.yyyyy.zzzzz
header(标头):通常由两部分组成:令牌的类型(JWT)和所使用的签名算法(如HMAC SHA256或RSA),经过Base64Url编码后形成JWT的第一部分,如下图。
{
"alg": "HS256",
"typ": "JWT"
}
playload(有效负载):存放用户自定义的信息,通常会把用户信息和令牌到期时间放在这里,同样是一个json对象,里面的key和value可随意设置,经过Base64Url编码后形成JWT的第二部分,由于部分是没有加密的,建议只存放一些非敏感信息,如下图。
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
signature(签名):使用标头的算法和私钥对第一部分和第二部分进行加密,通过Base64Url编码后形成JWT的第三部分,如下图。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
完整的jwt令牌像这样:eyJhbGciOiJIUzI1NiIsInppcCI6IkRFRiJ9.eNqqVkqtKFCyMjSzMDQ0MDI0NNdRKsrPSS1WsopWSkzJzcxTitVRKi1OLcpMAaqCMPMSc1OVrJRKUotLQoFcQ6VaAAAAAP__.DsSib4mXUN7D0Ln0O-f1Eq19ojOp9c97OnLcnpbgsA0
jwt优势
无状态:由于jwt的playload特性,令牌的生产方式无需存储的,一旦令牌成功创建,如果没设置过期时间的话,那么这个令牌是可以一直用的,任何人拿到它,都可以使用。
防篡改:由于jwt的signature特性,再私钥没有泄露的前提下,令牌是无法被篡改的,这也为无状态提供了支撑。
通用性:由于是json格式的,所以JWT可以跨语言支持,比如Java、JavaScript、PHP、Node等等
jwt弊端
无法中途废弃:因为一旦签发了一个 jwt,在到期之前始终都是有效的,如果用户信息发生更新了,只能等旧的 jwt 过期后重新签发新的 jwt。
无法承载过多的用户信息:如果playload的内容非常多,那么jwt令牌将会是一个很长的字符串,面对复杂的权限系统,单靠playload负载用户信息是不够的,这又会违背了无状态的初衷。
jwt实现
maven依赖如下;
<!-- JWT依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<!--JWT依赖,jdk1.8以上的还需引入以下依赖-->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
代码如下
/*
* @description: 创建jwt <br>
* @create: 2023/4/10 10:42 <br>
* @param playload 自定义的jwt载体
* @return java.lang.String
*/
public String createJwt(Map<String, Object> playload){
JwtBuilder jwtBuilder = Jwts.builder();
//设置加密算法和秘钥
jwtBuilder.signWith(SignatureAlgorithm.HS256, jwt_security_key);
//设置压缩压缩
jwtBuilder.compressWith(CompressionCodecs.DEFLATE);
//设置claims
Claims claims = new DefaultClaims();
//过期时间
Date expiration = new Date(System.currentTimeMillis() + jwt_expiration);
claims.setExpiration(expiration);
//自定义参数
claims.putAll(playload);
jwtBuilder.setClaims(claims);
return jwtBuilder.compact();
}
/*
* @description: 校验jwt <br>
* @create: 2023/4/10 15:09 <br>
* @param jwtToken
* @return boolean
*/
public boolean checkJwt(String jwtToken){
return Jwts.parser().isSigned(jwtToken);
}
/*
* @description: 解析jwt <br>
* @create: 2023/4/10 15:13 <br>
* @param jwtToken
* @return io.jsonwebtoken.Claims
*/
public Claims parseJwt(String jwtToken){
return Jwts.parser().setSigningKey(jwt_security_key).parseClaimsJws(jwtToken).getBody();
}