Redis实现JWT(JSON Web TOKEN)自动延长TOKEN过期时间

想养只大黄 2024-07-11 11:33:01 阅读 72

JWT是JSON WEB TOKEN的简写,常用于生成及校验Token。

常见的使用场景为:用户携带name和秘钥访问后端服务器,应用后端在校验通过后使用JWT生成并返回一串Token,后续用户只需要携带此Token就可以访问服务器,在此不多赘述。

本文目的是基于redis实现token自动更新其过期时间,在校验用户姓名和密码后使用JWT工具类生成会过期的Token,当用户携带此Token访问服务器后会自动延长其过期时间。

例如:用户A携带账户名及秘钥获取token,该token过期时间为2小时,过了1小时后用户再次携带该token访问系统,系统会自动将该token过期时间设置为此刻往后2小时候过期。

1、前提

1.1、JWT工具类

<code>public class JwtUtil {

public static final String JWT_ID = "dshsdhsdgjhjdsh";

/**

* jwt 加密解密密钥(可自行填写Base64加密)

*/

private static final String JWT_SECRET = "ahsagsggfTwGGFff";

/**

* 创建JWT

*/

public static String createJwt(Map<String, Object> claims, Long time) {

//指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。

SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

Date now = new Date(System.currentTimeMillis());

SecretKey secretKey = generalKey();

//下面就是在为payload添加各种标准声明和私有声明了,new一个JwtBuilder,设置jwt的body

JwtBuilder builder = Jwts.builder()

//如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的

.setClaims(claims)

//设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。

.setId(JWT_ID)

//iat: jwt的签发时间

.setIssuedAt(now)

//设置过期时间

.setExpiration(new Date(System.currentTimeMillis() + time))

//设置签名使用的签名算法和签名使用的秘钥

.signWith(signatureAlgorithm, secretKey);

return builder.compact();

}

/**

* 验证jwt

*/

public static Claims verifyJwt(String token) {

//签名秘钥,和生成的签名的秘钥一模一样

SecretKey key = generalKey();

Claims claims;

try {

//得到DefaultJwtParser

claims = Jwts.parser()

//设置签名的秘钥

.setSigningKey(key)

.parseClaimsJws(token).getBody();

} catch (Exception e) {

claims = null;

}//设置需要解析的jwt

return claims;

}

/**

* 刷新token并设置过期时间

* @param token 旧的token

* @param newExpirationInMillis 过期时间,单位毫秒

* @return 新的jwt token

*/

public static String updateTokenExpiration(String token, Long newExpirationInMillis) {

SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

SecretKey secretKey = generalKey();

Claims claims = Jwts.parser()

.setSigningKey(secretKey)

.parseClaimsJws(token)

.getBody();

claims.setExpiration(new Date(System.currentTimeMillis()+newExpirationInMillis));

return Jwts.builder()

.setClaims(claims)

.setId(JWT_ID)

.signWith(signatureAlgorithm, secretKey)

.compact();

}

/**

* 由字符串生成加密key

*

* @return

*/

public static SecretKey generalKey() {

byte[] encodedKey = Base64.decodeBase64(JWT_SECRET);

SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");

return key;

}

}

1.2、Maven依赖

<dependency>

<groupId>io.jsonwebtoken</groupId>

<artifactId>jjwt</artifactId>

<version>0.9.1</version>

</dependency>

1.3、配置过期时间及其他常量

private static final long EXPIRE_TIME = 7200 * 1000;

private static final String USER_NAME = "user_name";

private static final String SECRET = "password";

private static final String JWT_TOKEN_USERNAME = "jwt_token:username";

2、思路与流程

2.1、生成Token

步骤一:用户携带userName和Password访问后端接口,当校验通过后使用JWT工具类生成Token;

//校验appId和秘钥

...

//如果校验通过则生成JWT

HashMap<String, Object> jwtMap = new HashMap<>(5);

jwtMap.put(USER_NAME, userName);

jwtMap.put(SECRET, password);

//生成JWT

String jwt = JwtUtil.createJwt(jwtMap, EXPIRE_TIME);

步骤二:将生成Token及过期时间放入redis数据库中

String oldToken = (String) redisClient.get(JWT_TOKEN_USERNAME + USER_NAME);

//判断是否存在旧的Token

if (oldToken != null) {

redisClient.delete(oldToken);

}

//多次获取token只生效最后一次

redisClient.set(jwt, jwt, EXPIRE_TIME);

redisClient.set(JWT_TOKEN_USERNAME + appId, jwt, EXPIRE_TIME);

2.2、校验Token

String valueToken = (String) redisClient.get(token);

if (valueToken == null) {

//输出无效信息

log.error("TOKEN:{}无效", token);

//抛异常

throw new Exception();

} else {

Claims claims = JwtUtil.verifyJwt(valueToken);

if (claims == null) {

log.error("TOKEN:{}已过期", token);

throw new Exception();

}

String newToken = JwtUtil.updateTokenExpiration(valueToken, EXPIRE_TIME);

log.info("刷新后的token为:{}", newToken);

redisClient.set(token, newToken, EXPIRE_TIME);

String appKey = (String) claims.get(USER_NAME);

redisClient.expire(JWT_TOKEN_USERNAME + username, EXPIRE_TIME,TimeUnit.MILLISECONDS);

return claims.get(USER_NAME);

}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。