实际上就是一个字符串:
由三部分组成
(1)头部 - header:
描述该JWT的最基本的信息,如:类型(即JWT)以及签名所用算法
{
"alg": "HS256",
"typ": "JWT"
}
typ:类型
alg:签名的算法,上边使用的算法是HS256
会对头部进行 BASE64编码,编码格式如下:
dsHHKLjklljLKJ678jklHJKjhJK657hjk
(2)载荷 - payload
存放有效信息的地方。
分三部分
标准声明:
iss: 该JWT的签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt过期时间,必须大于签发时间
nbf: 定义在什么时间之前,该jwt是不可用的
iat: jwt的签发时间
公共声明:一般存放用户的信息。
私有声明:提供者和消费者共同定义的声明。
(3)签名 - secret 保证jwt安全性的唯一部分
jwt的第三部分,是一个签证信息,这个签证信息有三部分组成:
1> header (base64后的header)
2> payload(base64后的payload)
3> secret (盐一定要保密)
三者组成之后进行加密,三部分以点分割。
注意:secret是保存在都服务器上的,jwt的签发生成也是在服务器上,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你的服务端的私钥,在任何场景下都不能泄露出去,一旦客户端得知这个secret那就意味着客户端也可以签单jwt了。
sringboot 使用jwt
生成token
第一步:安装依赖
(提示:如果你的java版本为最新版本,使用jjwt旧版,在生成token是会抛出异常,大致错误找不到类,此时需要下载最新版jjwt)
<!--jwt依赖-->
<!--新版本-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.1</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
<version>0.11.1</version>
<scope>runtime</scope>
</dependency>
第二步: 构建jwt对象,生成token
public class TokenTest {
public static void main(String[] args) {
String userId = "18";
// 生成 secret密钥
// SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
// String scretString = Encoders.BASE64.encode(key.getEncoded());
String scretString = "WAGFd+7X2q1evBcU2p+IEDzuR39SXyoNn0UlnntpnLg=";
System.out.println("1:"+scretString);
// 构建jwt
JwtBuilder jwtBuilder = Jwts.builder()
// jti
.setId(userId)
// sub 签发给谁
.setSubject("rose")
// iat 签发时间
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, scretString); // 签名
// 根据对象生成 令牌 token
String token = jwtBuilder.compact();
System.out.println(token);
}
}
结果如下:
关于签名算法,很多种:
一个完整的token 生成及解析demo:
package com.lxc.springboot.controller;
import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.DeserializationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class TokenTest {
static Logger log = LoggerFactory.getLogger(TokenTest.class);
public static void main(String[] args) {
String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoi5ZCV5pif6L6wIiwiYWdlIjoyMywiaWF0IjoxNjI2MjUxOTg5LCJqdGkiOiIxMCIsInN1YiI6InJvc2UiLCJleHAiOjE2MjY4NTY3ODl9.z3dO5x2r0xjBNgboAOncUQ7-acrl4cucd2JziFVQUB0";
// 解析token
parseToken(token);
// 生成token
// System.out.println(generateToken());
}
/**
* 生成token
* @return void
*/
static public String generateToken() {
// 有效期默认7天
Long time = 1000 * 60 * 60 * 24 * 7L;
// 用户的id
String userId = "10";
// 存储的用户的信息 - 不建议把用户信息全部存到token里。
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("name", "吕星辰");
userInfo.put("age", 23);
return generateToken(userId, userInfo, time);
}
/**
* 生成token
* @param userId
* @param claims
* @param time
* @return string
*/
static public String generateToken(String userId, Map<String, Object> claims, Long time) {
// 生成 secret密钥
// SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
// String scretString = Encoders.BASE64.encode(key.getEncoded());
// 这里其实就是new一个JwtBuilder,设置jwt的body
JwtBuilder jwtBuilder = Jwts.builder()
// 自定义用户的一些信息, 必须放最前面,不然后面设置的东西都会没有:如setExpiration会没有时间 !!!
.setClaims(claims)
// iat 签发时间
.setIssuedAt(getIssuedAt())
// 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
.setId(userId)
// sub 签发给谁
.setSubject("rose")
// 设置过期时间
.setExpiration(getExpiration(time))
.signWith(SignatureAlgorithm.HS256, getSecretKey()); // 签名
// 根据对象生成 令牌 token
return jwtBuilder.compact();
}
/**
* 解析token
* @param token
*/
static public void parseToken(String token) throws DeserializationException {
String msg = null;
try{
Claims claims = Jwts.parserBuilder()
.setSigningKey(getSecretKey()) // 拿到签名时的秘钥
.build()
.parseClaimsJws(token)
.getBody();
// claims就是一个map
// {"name":"吕星辰","age":23,"iat":1626251989,"jti":"10","sub":"rose","exp":1626856789}
System.out.println(JSONObject.toJSONString(claims));
System.out.println(claims.getId()); // 获取id
System.out.println(claims.getIssuedAt()); // 获取签发时间
System.out.println(claims.getSubject()); // 获取签收人
System.out.println(claims.getExpiration()); // 获取过期时间
System.out.println(claims.get("name")); // 获取用户 name
System.out.println(claims.get("age")); // 获取用户 age
}catch (SignatureException se) {
msg = "密钥错误";
log.error(msg, se);
}catch (MalformedJwtException me) {
msg = "密钥算法,或者密钥转换错误,或者token不正确";
log.error(msg, me);
}catch (MissingClaimException mce) {
msg = "密钥缺少校验数据";
log.error(msg, mce);
}catch (ExpiredJwtException mce) {
msg = "token已过期";
log.error(msg, mce);
}catch (JwtException jwte) {
msg = "密钥解析错误";
log.error(msg, jwte);
}
}
/**
* 获取密钥 这个可以在资源配置文件中定义
* @return string
*/
static private String getSecretKey() {
return "WAGFd+7X2q1evBcU2p+IEDzuR39SXyoNn0UlnntpnLg=";
}
/**
* 设置过期时间
* @param time
* @return Date
*/
static private Date getExpiration(Long time) {
// 把时间戳转化为Date时间格式
// System.currentTimeMillis() + time 【当前时间 + 传进来的时间 = 过期时间】
return new Date(System.currentTimeMillis() + time);
}
/**
* 签发时间 - 默认为当前时间
* @return date
*/
static private Date getIssuedAt() {
return getIssuedAt(new Date());
}
static private Date getIssuedAt(Date date) {
return date;
}
}