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工具类
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); }