首先引入jar包
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.5</version>
</dependency>
然后书写工具类用于生成和校验token
@Import(cn.hutool.extra.spring.SpringUtil.class)
public class JwtUtils {
public static final long Expire = 1000*60*60*24;
public static final long RExpire = 2;
public static final TimeUnit RExpiredays = TimeUnit.DAYS;
//加密的密钥
private static final String Secret = "wuxing.666";
//为了区分业务 令牌前缀
private static final String Token_Pre = "Shop";
//颁布者:
private static final String Subject = "wuxing";
public static Map genToken(LoginUsers loginUsers){
Assert.notNull(loginUsers,"不能为null");
Long id = loginUsers.getId();
HashMap<String, Object> map = new HashMap<>();
long l = System.currentTimeMillis() + Expire;
String token = Token_Pre + Jwts.builder().setSubject(Subject)
.claim("avatar", loginUsers.getAvatar())
.claim("id", id)
.claim("ip", loginUsers.getLoginIp())
.claim("name", loginUsers.getUsername())
.claim("mail", loginUsers.getEmail())
.setIssuedAt(new Date()) //设置当前时间
.setExpiration(new Date(l))
.signWith(SignatureAlgorithm.HS256, Secret).compact();
String uuid = IdUtil.fastSimpleUUID();
RedisTemplate bean = SpringUtil.getBean("redisTemplate");
bean.opsForValue().set("rtoken:"+uuid, id,RExpire, RExpiredays);
map.put("token",token);
map.put("rtoken",uuid);
map.put("expirtime",l);
map.put("ip",loginUsers.getLoginIp());
return map;
}
public static Claims checkJwt(String token){
try {
// parseClaimsJwt() 方法是解析没有进行签名的token,如果我们的token是已经进行了签名,调用了该方法,就会报出上述错误。
// 签名的token应该使用parseClaimsJws(String jws) 方法
Claims claims = Jwts.parser().setSigningKey(Secret).parseClaimsJws(token.replace(Token_Pre, "")).getBody();
return claims;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
上面代码中
Long id = loginUsers.getId();
HashMap<String, Object> map = new HashMap<>();
long l = System.currentTimeMillis() + Expire;
String token = Token_Pre + Jwts.builder().setSubject(Subject)
.claim("avatar", loginUsers.getAvatar())
.claim("id", id)
.claim("ip", loginUsers.getLoginIp())
.claim("name", loginUsers.getUsername())
.claim("mail", loginUsers.getEmail())
.setIssuedAt(new Date(l)) //设置当前时间
.setExpiration(new Date())
.signWith(SignatureAlgorithm.HS256, Secret).compact();
String uuid = IdUtil.fastSimpleUUID();
RedisTemplate bean = SpringUtil.getBean("redisTemplate");
bean.opsForValue().set("rtoken:"+uuid, id,RExpire, RExpiredays);
map.put("token",token);
map.put("rtoken",uuid);
map.put("expirtime",l);
map.put("ip",loginUsers.getLoginIp());
是为了后续实现自动过期刷新
具体逻辑是:
用户登录成功的时候,一次性给他两个Token,分别为AccessToken和RefreshToken
AccessToken有效期较短,比如1天或者5天,用于正常请求
RefreshToken有效期可以设置长一些,例如10天、20天,作为刷新AccessToken的凭证
刷新方案:当AccessToken即将过期的时候,例如提前30分钟,客户端利用RefreshToken请求指定的API获取新的
AccessToken并更新本地存储中的AccessToken
核心逻辑
1、登录成功后,jwt生成AccessToken;UUID生成RefreshToken并存储在服务端redis中,设置过期时间
2、接口返回3个字段AccessToken/RefreshToken/访问令牌过期时间截
3、由于RefreshToken存储在服务端redis中,假如这个RefreshToken也过期,则提示重新登录;
缺点:前端每次请求需要判断token距离过期时间
优点:后端压力小,代码逻辑改动不大
里面绑定ip的话是为了校验ip是否和当前用户ip是否一致,防止滥用token
优点:服务端无需存储相关内容,性能高,假如用户广州登录,泄露了token给杭州的黑客,依旧用不了
缺点:如果用户用使用过程中ip变动频繁,则操作会经常提示重新登录,体验不友好
但为了安全,麻烦是必不可少的。相比除了ip,还有设备号,UA,header头中终端信息,地点等可以采用,进行绑定