什么是token
简单来说token就是前后端交互的一种令牌,现在多数项目是前后端分离,无状态的
那么后端的接口不能随便让知道这个接口的前端都能访问到
那么token机制就生成了,前端用户登录成功后,后端生成一个令牌token,返回给前端
token一般是一串随机的字符串,后续这个串传到后端,后端能通过这个串去获取这个串保存的用户信息
比如token里面封装了用户的id,和用户的姓名,那么有个接口需要根据用户id查询这个用户的详细信息
没有token的时候你的接口是不是需要传用户id到后端,这种敏感信息用户id是不是就暴露在接口了
如果有token机制,你只需传一串token这个是一串随机字符传很安全,只有传到后端,后端去通过token获取到用户id直接查询,返回给前端
一般token都有过期时间,一但token过期就验证失败使用户重新登录获取新的token
后端怎么生成token
用redis封装token
上面说的这个token是不是用redis很好实现,当用户登录完成,将用户的id加密成一串字符当做redis的key
把用户的信息一般是一个json串,json串里面有用户的详细信息 用户id 用户姓名等用户信息
再加一个过期时间,这个key返回给前端,不就是一个token
当前端把这个key(token传到后端)后端通过这个key去redis就能获取用户的全部信息
jwt
JWT:Json Web Token,是基于Json的一个公开规范,这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息,他的两大使用场景是:认证和数据交换
今天主要是快速的写一个jwt的工具类达到生成token和通过token获取用户信息
开整:
- 一个JWT实际上就是一个字符串,它由三部分组成,
头部
、载荷
与签名
- 生成好的token是三段字符,通过.号连接的如下
token:eyJ0eXAiOiJKc29uV2ViVG9rZW4iLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImxlaXhoYW9ob25nIiwidXNlcklkIjoiMTIzNDU1IiwiaWF0IjoxNjYxNjA1Njk5LCJuYmYiOjE2NjE2MDU2OTksImV4cCI6MTY2MTcwNTY5OX0.7vNQpzYhuTXP9MM4nG4tVwIld-5QFY7CG8jj9YiUPzE
代码实现
- 需要下载的jwt的maven依赖如下:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
</dependency>
- 建token的实体,包含token,有效时间 过期时间
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class Token {
/**
* token
*/
private String token;
/**
* 有效时间:单位:秒
*/
private Long expire;
/**
* 过期时间
*/
private LocalDateTime expiration;
public Token(String token, Long expire, LocalDateTime expiration) {
this.token = token;
this.expire = expire;
this.expiration = expiration;
}
}
- 生成jwt并把参数封装到token实体返回
/**
* 功能简述:
*
* @author leixiaohong
* @date 8/27/22 8:11 PM
*/
@Slf4j
public class JwtUtils {
/**
* JWT token 签名
* 签名密钥长度至少32位
*/
private static final String JWT_SIGN_KEY = "leixiaohong_jwt_token_json_sign_key";
private static final String BASE64_SECURITY = Base64.getEncoder().encodeToString(JWT_SIGN_KEY.getBytes(StandardCharsets.UTF_8));
/***
* @Description: 创建令牌
* @Param:
* @return: com.tapque.mmo.api.util.Token
* @Author: leixiaohong
* @Date: 8/27/22
*/
public static Token createJwt(Map<String, String> user, long expire) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
//生成签名密钥
byte[] apiKeySecretBytes = Base64.getDecoder().decode(BASE64_SECURITY);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
//添加JWT的类
JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JsonWebToken");
//设置参数到jwt
user.forEach(builder::claim);
//Token过期时间
long expMillis = nowMillis + expire * 1000;
Date exp = new Date(expMillis);
builder.setIssuedAt(now).setNotBefore(now).setExpiration(exp).signWith(signingKey, signatureAlgorithm);
//组装Token信息
Token tokenInfo = new Token();
tokenInfo.setToken(builder.compact());
tokenInfo.setExpire(expire);
tokenInfo.setExpiration(localDateTime(exp));
return tokenInfo;
}
/**
* Date转换为LocalDateTime
*
* @param date 日期
*/
public static LocalDateTime localDateTime(Date date) {
if (date == null) {
return LocalDateTime.now();
}
Instant instant = date.toInstant();
ZoneId zoneId = ZoneId.systemDefault();
return instant.atZone(zoneId).toLocalDateTime();
}
}
- 获取Claims
/**
* 功能简述:
*
* @author leixiaohong
* @date 8/27/22 8:11 PM
*/
@Slf4j
public class JwtUtils {
/**
* JWT token 签名
* 签名密钥长度至少32位
*/
private static final String JWT_SIGN_KEY = "leixiaohong_jwt_token_json_sign_key";
private static final String BASE64_SECURITY = Base64.getEncoder().encodeToString(JWT_SIGN_KEY.getBytes(StandardCharsets.UTF_8));
/***
* @Description: 获取Claims
* @Param:
* @return: io.jsonwebtoken.Claims
* @Author: leixiaohong
* @Date: 8/27/22
*/
public static Claims getClaims(String token, long allowedClockSkewSeconds) {
if (StrUtil.isEmpty(token)) {
return null;
}
return parseJwt(token, allowedClockSkewSeconds);
}
/***
* @Description: 解析jwt
* @Param:
* @return: io.jsonwebtoken.Claims
* @Author: leixiaohong
* @Date: 8/27/22
*/
public static Claims parseJwt(String jsonWebToken, long allowedClockSkewSeconds) {
try {
return Jwts.parserBuilder()
.setSigningKey(Base64.getDecoder().decode(BASE64_SECURITY))
.setAllowedClockSkewSeconds(allowedClockSkewSeconds)
.build()
.parseClaimsJws(jsonWebToken)
.getBody();
} catch (ExpiredJwtException ex) {
log.error("token过期", ex);
//过期
//抛异常 让系统捕获到返回到前端
} catch (SignatureException ex) {
log.error("签名错误", ex);
//签名错误
//抛异常 让系统捕获到返回到前端
} catch (IllegalArgumentException ex) {
log.error("token为空", ex);
//token 为空
//抛异常 让系统捕获到返回到前端
} catch (Exception e) {
log.error("解析token异常", e);
//抛异常 让系统捕获到返回到前端
}
return null;
}
}
说明:如果获取不到需要封装成异常返回给前端
- 验证
生成的token接受的参数是个map,我们测试封装的key为userId和userName,值分别是123455和leixiaohong
如果能生成的token,并通过token能获取userId是123455和userName是leixiaohong就说明验证成功
public static void main(String[] args) {
Map<String, String> param = MapUtil.newHashMap();
param.put("userId", "123455");
param.put("userName", "leixiaohong");
Token token = JwtUtils.createJwt(param, 100000L);
System.out.println("token:" + token.getToken());
System.out.println("token的有效期:" + token.getExpire());
System.out.println("token过期时间:" + token.getExpiration());
Claims claims = JwtUtils.getClaims(token.getToken(), 60L);
System.out.println("从token中获取的userId:" + Convert.toStr(claims.get("userId")));
System.out.println("从token中获取userName:" + Convert.toStr(claims.get("userName")));
}
- 结果
token:eyJ0eXAiOiJKc29uV2ViVG9rZW4iLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImxlaXhpYW9ob25nIiwidXNlcklkIjoiMTIzNDU1IiwiaWF0IjoxNjYxNjA4NTQ4LCJuYmYiOjE2NjE2MDg1NDgsImV4cCI6MTY2MTcwODU0OH0.Dm7xRdvMQ0vI5NEPTtPQ4XEAvIikC7hNYeU3bTl5iXM
token的有效期:100000
token过期时间:2022-08-29T01:42:28.216
从token中获取的userId:123455
从token中获取userName:leixiaohong
- 工具类代码可以直接使用 完