Json web token (JWT), 根据官网的定义,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。 JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
JWT 优逆
优势
1.体积小,因而传输速度快
2.传输方式多样,可以通过URL/POST参数/HTTP头部等方式传输
3.严格的结构化。它自身(在 payload 中)就包含了所有与用户相关的验证消息,如用户可访问路由、访问有效期等信息,服务器无需再去连接数据库验证信息的有效性,并且 payload 支持为你的应用而定制化。
4.支持跨域验证,可以应用于单点登录
不足
1.无法吊销令牌,只能等待令牌自身过期
2.令牌长度与其包含用户信息多少正相关,传输开销较大
JWT 组成部分
JWT 由三个部分组成(解码可以得到)
- 头部(Header)
{ “alg”: “HS256”, “typ”: “JWT” }
jwt的头部包含两部分信息:
声明类型,这里是jwt
声明加密的算法 通常直接使用 HMAC SHA256
- 载荷(payload)
载荷就是存放有效信息的地方。分为三种
标准中注册的声明
公共的声明
私有的声明
2.1 标准中注册的声明 (建议但不强制使用)
iss: 该JWT的签发者,一般是服务器,是否使用是可选的;
iat(issued at): 在什么时候签发的(UNIX时间),是否使用是可选的;
exp(expires): 什么时候过期,这里是一个Unix时间戳,是否使用是可选的;
aud: 接收该JWT的一方,是否使用是可选的;
sub: 该JWT所面向的用户,userid,是否使用是可选的;
nbf (Not Before):如果当前时间在nbf里的时间之前,则Token不被接受;一般都会留一些余地,比如几分钟;,是否使用是可选的;
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
2.2 公共的声明
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
2.3 私有的声明
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
3.签名(signature)
根据alg算法与私有秘钥进行加密得到的签名字串,这一段是最重要的敏感信息,只能在服务端解密。
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
这三个部分用 . 号连接成一个字符串,成为了最终的jwt。
JWT 使用
创建token
public String createJWT(Authentication authentication)
{
String authorities = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
Long date = (new Date()).getTime();
Date expDate = new Date(date+jwtProperties.getTokenValidityTime());
JwtBuilder builder = Jwts.builder()
.setIssuer(authentication.getName() ) // 签发人
.setSubject(authentication.getName()) // 主题
// claim 必须在前 过期时间才会有用
.claim(AUTHORITIES_KEY,authorities) // 权限
.setExpiration(expDate) // 到期时间
.setNotBefore(new Date()) // 生效时间
//.setIssuedAt(new Date()) // 签发时间
//.setAudience("") // 受众
//.setId("123") // JWT id
.signWith(SignatureAlgorithm.HS256,SECRET_Key);
return builder.compact();
}
校验token
Jwts.parser().setSigningKey(SECRET_Key).parseClaimsJws(authToken);