JSON Web Token是目前最流行的跨域认证解决方案,适合前后端分离项目通过Restful API进行数据交互时进行身份认证
**
关于有效期
**
由于jwt是直接给用户的,只要能验证成功的jwt都可以被视作登录成功,所以,如果不给jwt设置一个过期时间的话,用户只要存着这个jwt,就相当于永远登录了,而这是不安全的,因为如果这个令牌泄露了,那么服务器是没有任何办法阻止该令牌的持有者访问的(因为拿到这个令牌就等于随便冒充你身份访问了),所以往往jwt都会有一个有效期,通常存在于载荷部分,下面是一段生成jwt的java代码:
return JWT.create().withAudience(userId)
.withIssuedAt(new Date()) <---- 发行时间
.withExpiresAt(expiresDate) <---- 有效期
.withClaim("sessionId", sessionId)
.withClaim("userName", userName)
.withClaim("realName", realName)
.sign(Algorithm.HMAC256(userId+"HelloLehr"));
在实际的开发中,令牌的有效期往往是越短越安全,因为令牌会频繁变化,即使有某个令牌被别人盗用,也会很快失效。但是有效期短也会导致用户体验不好(总是需要重新登录),所以这时候就会出现另外一种令牌—refresh token刷新令牌。刷新令牌的有效期会很长,只要刷新令牌没有过期,就可以再申请另外一个jwt而无需登录(且这个过程是在用户访问某个接口时自动完成的,用户不会感觉到令牌替换),对于刷新令牌的具体实现这里就不详细讲啦(其实因为我也没深入研究过XD…)
**
对比Session
**
在传统的session会话机制中,服务器识别用户是通过用户首次访问服务器的时候,给用户一个sessionId,然后把用户对应的会话记录放在服务器这里,以后每次通过sessionId来找到对应的会话记录。这样虽然所有的数据都存在服务器上是安全的,但是对于分布式的应用来说,就需要考虑session共享的问题了,不然同一个用户的sessionId的请求被自动分配到另外一个服务器上就等于失效了
而Jwt不但可以用于登录认证,也把相应的数据返回给了用户(就是载荷里的内容),通过签名来保证数据的真实性,该应用的各个服务器上都有统一的验证方法,只要能通过验证,就说明你的令牌是可信的,我就可以从你的令牌上获取你的信息,知道你是谁了,从而减轻了服务器的压力,而且也对分布式应用更为友好。(毕竟就不用担心服务器session的分布式存储问题了)
**
整合Springboot 导入java-jwt包
**
pom.xml里写入
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
token的生成
/**
* 生成token
* @param userId
* @param userName
* @return
*/
public String createToken(String userId,String userName){
Calendar nowTime = Calendar.getInstance();
nowTime.add(Calendar.MINUTE,30);
Date expiresDate = nowTime.getTime();
return JWT.create().withAudience(userId) //签发对象
.withIssuedAt(new Date()) //发行时间
.withExpiresAt(expiresDate) //有效时间
.withClaim("userName", userName) //载荷,随便写几个都可以
.sign(Algorithm.HMAC256(userId+"HelloLehr")); //加密
}
token的验证
/**
* 验证token是否合法
* @param token
* @param secret
* @return
*/
public boolean verifyToken(String token, String secret){
DecodedJWT jwt = null;
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret+"HelloLehr")).build();
jwt = verifier.verify(token);
} catch (Exception e) {
//效验失败
return false;
}
return true;
}
使用
@RequestMapping("/w1")
public Object w1(){
String token = createToken("10001", "洪华宇");
return token;
}
@RequestMapping("/w2")
public Object w2(String token, String secret){
boolean verifyToken = verifyToken(token, secret);
if(verifyToken){
return "合法";
}else {
return "非法";
}
}