学习jwt
JWT的全称是
json web token,jwt本身基于http的,而http是无状态的,所以jwt是无状态的。
jwt的构成
JWT 是一种以紧凑、可验证的形式在双方之间传输信息的手段。在 JWT 正文中编码的信息位称为 。JWT 的扩展形式采用 JSON 格式,因此每个都是 JSON 对象中的一个键。
jwt字符由三部分构成,每个部分都由
.隔开例
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.ipevRNuRP6HflG8cFKnmUPtypruRC4fb1DWtoLL62SY每个部分都采用Base64URL编码
针对上面的解析
第一部分是标题
header{ "alg":"HSC256" }第二部分是
body,用于存放具体的验证信息,过期时间等{ "sub":"Joe" }第三部分是签证,即签证需要的算法和密钥
签证加密算法如下:
HS256:使用SHA-256的HMACHS384:使用SHA-384的HMACHS512:使用SHA-512的HMACES256: 使用 P-256 和 SHA-256 的 ECDSAES384: 使用 P-384 和 SHA-384 的 ECDSAES512: 使用 P-521 和 SHA-512 的 ECDSARS256: RSASSA-PKCS-v1_5 使用 SHA-256RS384: RSASSA-PKCS-v1_5 使用 SHA-384RS512: RSASSA-PKCS-v1_5 使用 SHA-512PS256:使用 SHA-256 的 RSASSA-PSS 和带有 SHA-1 的 MGF256PS384:使用 SHA-384 的 RSASSA-PSS 和带有 SHA-1 的 MGF384PS512:使用 SHA-512 的 RSASSA-PSS 和带有 SHA-1 的 MGF512
jdk项目使用jwt
maven引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- 工具类用到的security依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 解决configurationProperties注解报红问题 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- 解决项目启动时,报javax.xml.bind.DatatypeConverter找不到错误 -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
jwt测试类
@SpringBootTest(classes = test.class)
public class test {
// 设置过期时间
private long expiration = 1000 * 60 * 60;
//设置密钥对
private String secret = "jwtTest";
@Test//生成token
public void createToken() {
JwtBuilder builder = Jwts.builder();//获取token生成构建类
builder.setHeaderParam("date", new Date());//设置header
HashMap<String, Object> claim = new HashMap<>();//创建map,存放键值对,最后通过setClaims方法生成Claims
claim.put("username", "tzf");
claim.put("password", 20030317);
//也可以创建Claims
Claims claims = new DefaultClaims();
claims.put("username", "tzf");
claims.put("password", 20030317);
builder.setClaims(claims);//设置body,
//设置主题
builder.setSubject("test");
//设置userid
builder.setId("30248392");
//设置过期时间
builder.setExpiration(new Date(System.currentTimeMillis() + expiration));
//设置签证,同时需要密钥对
builder.signWith(SignatureAlgorithm.HS256, secret);
//最后通过compact方法将上面三部分合起来组成jwt
String compact = builder.compact();
System.out.println(compact);
}
@Test
public void parseToken() {
JwtParser parser = Jwts.parser();//获取token解析构建类
Jws<Claims> claimsJws = parser.setSigningKey(secret).parseClaimsJws("eyJkYXRlIjoxNjg0MTQ3NDg3MDMwLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InR6ZiIsInBhc3N3b3JkIjoyMDAzMDMxNywic3ViIjoidGVzdCIsImp0aSI6IjMwMjQ4MzkyIiwiZXhwIjoxNjg0MTUxMDg3fQ.l2sTkI_csKsaRKJPpAakzXDxhJrowfLSpGjFAEe4568");
Claims body = claimsJws.getBody();//返回Claim对象,里面封装了该token的第一二部分的信息
System.out.println(body.get("username"));//获取username
System.out.println(body.get("password"));//获取password
System.out.println(body.getExpiration());//获取过期时间
String subject = body.getSubject();//获取主题
System.out.println(subject);
}
}
工具类
import io.jsonwebtoken.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
//需要springSecurity依赖以及lombok以及jwt依赖
//注册组件
@Component
//@Data和@ConfigurationProperties结合使用用于在yaml中对其常量进行注入
@Data
@ConfigurationProperties("jwt.data")
@Slf4j
public class JWTUtil {
//创建对象主题
private static final String CLAIM_KEY_SUBJECT = "subject";
//创建创建时间
private static final String CLAIM_KEY_CREATED = "created";
//创建用户名
private static final String CLAIM_KEY_USERNAME = "username";
//@Value这个注解一定要引入spring-boot-starter-validation才能使用
//@Value注解可以代替@Data和@ConfigurationProperties结合
//这两个二者选一即可
//我建议使用@Data和@ConfigurationProperties结合
//@Value("${jwt.data.SECRET}")
private String SECRET;//创建加密盐
//过期时间
private Long expiration;
//根据用户名生成token
//传入的是使用SpringSecurity里的UserDetails
public String createToken(UserDetails userDetails) {
HashMap<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return createToken(claims);//装入map
}
//根据token获取用户名
public String getUsernameFromToken(String token) {
String username = "";
try {
Claims claims = getClaimsFromToken(token);
username = claims.get(CLAIM_KEY_USERNAME,String.class);
} catch (Exception e) {
username = null;
log.info("error:{}", "用户名未能获取 from token");
}
return username;
}
//从token中获取荷载
private Claims getClaimsFromToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
} catch (ExpiredJwtException e) {
e.printStackTrace();
} catch (UnsupportedJwtException e) {
e.printStackTrace();
} catch (MalformedJwtException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
return claims;
}
//根据负载生成jwt token
private String createToken(Map<String, Object> claims) {
//jjwt构建jwt builder
//设置信息,过期时间,主题,signnature
return Jwts.builder()
.setClaims(claims)
.setSubject(CLAIM_KEY_SUBJECT)
.setExpiration(expirationDate())
.signWith(SignatureAlgorithm.ES512, SECRET)
.compact();
}
//生成token失效时间
private Date expirationDate() {
//失效时间为:系统当前毫秒数+我们设置的时间(s)*1000=》毫秒
//其实就是未来7天
return new Date(System.currentTimeMillis() + expiration * 1000);
}
//判断token是否有效
public boolean validateToken(String token, UserDetails userDetails) {
//判断token是否过期
//判断token是否和userDetails中的一致
//我们要做的 是先获取用户名
String username = getUsernameFromToken(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
//判断token、是否失效
//失效返回true
private boolean isTokenExpired(String token) {
Date expiredDate = getExpiredDateFeomToken(token);
return expiredDate.before(new Date());
}
//从荷载中获取时间
private Date getExpiredDateFeomToken(String token) {
Claims claims = getClaimsFromToken(token);
return claims.getExpiration();
}
//判断token是否可以被刷新
//过期(销毁)就可以
public boolean canBeRefreshed(String token){
return !isTokenExpired(token);
}
//刷新token
public String refreshToken(String token){
Claims claims = getClaimsFromToken(token);
//修改为当前时间
claims.put(CLAIM_KEY_CREATED,new Date());
return createToken(claims);
}
}