JWT(Json Web Token)是实现token技术的一种解决方案,JWT由三部分组成:
header(头)
、payload(载体)
、signature(签名)
。
JWT第一部分是header,header主要包含两个部分,alg指加密类型,可选值为HS256
、RSA
等等,typ=JWT
为固定值,表示token的类型。。
{
"typ": "JWT",
"alg": "HS256"
}
JWT第二部分是payload,payload是token的详细内容,一般包括iss
(发行者), exp
(过期时间), sub
(用户信息), aud
(接收者),以及其他信息,详细介绍请参考官网,也可以包含自定义字段。
{
"iat": 1493090001,
"name": "张三"
}
iss:Issuer,发行者
sub:Subject,主题
aud:Audience,观众
exp:Expiration time,过期时间
nbf:Not before
iat:Issued at,发行时间
jti:JWT ID
JWT第二部分是signature,这部分的内容是这样计算得来的:
Base64(header)
.Base64(payload)
得到一个Base64编码的字符串(下文成EncodeString)
HS256(EncodeString,”秘钥”);计算得到的即使签名。
计算得到上面三部分内容后,用.连接起来就是一个完整的 JWT TOKEN,秘钥是保存在服务器上的一个私有密钥。
将头部、声明、签名用.号连在一起就得到了我们要的JWT。
eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MTUyOTgxNDEsImtleSI6InZhdWxlIn0.orewTmil7YmIXKILHwFnw3Bq1Ox4maXEzp0NC5LRaFQ
Session方式存储用户id的最大弊病在于要占用大量服务器内存,对于较大型应用而言可能还要保存许多的状态。一般而言,大型应用还需要借助一些KV数据库和一系列缓存机制来实现Session的存储。
而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。除了用户id之外,还可以存储其他的和用户相关的信息,例如用户角色,用户性别等。
引入jar
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
<version>0.9.0version>
dependency>
demo
package user;
import io.jsonwebtoken.*;
import org.joda.time.DateTime;
import org.junit.Test;
import java.util.Date;
public class JWT {
private static final String PRIVATE_KEY = "123456789";
@Test
public void jwtTest() throws InterruptedException {
// 设置3秒后过期
String jwt = this.buildJwt(DateTime.now().plusSeconds(3).toDate());
System.out.println(jwt);
// 验证token是否可用
boolean isOk = this.isJwtValid(jwt);
System.out.println(isOk);
}
public String buildJwt(Date exp) {
String jwt = Jwts.builder()
.signWith(SignatureAlgorithm.HS256, PRIVATE_KEY)//SECRET_KEY是加密算法对应的密钥,这里使用额是HS256加密算法
.setExpiration(exp)//expTime是过期时间
.claim("key", "vaule")//该方法是在JWT中加入值为vaule的key字段
.compact();
return jwt;
}
public boolean isJwtValid(String jwt) {
try {
//解析JWT字符串中的数据,并进行最基础的验证
Claims claims = Jwts.parser()
.setSigningKey(PRIVATE_KEY)//SECRET_KEY是加密算法对应的密钥,jjwt可以自动判断机密算法
.parseClaimsJws(jwt)//jwt是JWT字符串
.getBody();
String vaule = claims.get("key", String.class);//获取自定义字段key
//判断自定义字段是否正确
if ("vaule".equals(vaule)) {
return true;
} else {
return false;
}
}
//在解析JWT字符串时,如果密钥不正确,将会解析失败,抛出SignatureException异常,说明该JWT字符串是伪造的
//在解析JWT字符串时,如果‘过期时间字段’已经早于当前时间,将会抛出ExpiredJwtException异常,说明本次请求已经失效
catch (SignatureException | ExpiredJwtException e) {
return false;
}
}
}