JWT--Json Web Token
【JWT定义】
JSON Web Token(JWT)是一个开放的工业标准RFC 7519,是一个非常轻巧的规范。
【JWT用途】
这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息,在HTTP通信过程中,进行身份认证。
【为什么要身份认证】
HTTP通信是无状态的,如果不需要识别用户的浏览,客户端的请求到了服务端处理完之后就结束了。
但更多时候,是需要识别登录用户的,例如用户登录后浏览、购物车、下单、付款等一系列行为;
即使不登录的游客,系统也可能标记,需要对访问的客户端进行识别。
【常用身份认证方式:Session】
- 客户端在服务端登录成功之后,服务端会生成一个sessionID,缓存该session(会话),然后响应携带sessionID返回给客户端,客户端将sessionID保存到cookie中。
- 客户端再次发起请求的时候,携带cookie中的sessionID到服务端,服务端解析获取sessionID并与自身缓存匹配,就知道是哪个用户的请求。
常用的session解决方案,会遇到以下问题:
- session保存在服务端,当客户访问量增加时,服务端就需要存储大量的session会话,对服务器有很大的考验;
- 当服务端为集群时,用户登录其中一台服务器,会将session保存到该服务器的内存中,但是当用户的访问到其他服务器时,会无法访问。这个通常可以采用第三方分布式缓存等技术共享保存session,来解决该问题。
【另一种身份认证方式:JWT】
- 客户端通过用户名和密码登录服务器;
- 服务端对客户端身份进行验证;
- 服务端对该用户生成Token,返回给客户端;
- 客户端发起请求,需要携带该Token;
- 服务端收到请求后,首先验证Token,之后返回数据。
- 客户端将Token保存到本地浏览器,一般保存到cookie中。
与Session方案的区别是:
服务端不需要保存Token,只需要对Token中携带的信息进行验证即可;
无论客户端访问后台的那台服务器,只要可以通过用户信息的验证即可。
JWT 的原理是,
服务器认证以后,生成一个 JSON 对象,发回给用户,
例如:{ "姓名": "张三", "角色": "管理员", "到期时间": "2018年10月31日0点0分"}
以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。
为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名signature(详见后文)。
这样服务器就可以不保存任何 session 数据了,也就是说,服务器变成无状态了,从而更容易实现扩展。
也可以理解session是消耗服务器内存存储的方式,JWT是消耗服务器CPU计算的方式。
【JWT组成】
JWT由3段信息组成:
- header信息
- payload信息
- signature信息
这3段信息分别base64URL编码后,用英文句号.分隔连在一起,组成JWT字符串。
header
header承担两部分信息:
- 声明令牌类型typ:固定填写JWT。
- 声明signature使用的加密算法alg:默认使用HMAC-SHA256(写为HS256)。
{
'typ': 'JWT',
'alg': 'HS256'
}
然后将header进行base64URL加密,组成JWT的第1段。
payload
payload是JWT存放有效信息的地方,包含3部分:
- 标准中注册声明 (建议但不强制使用)
iss: JWT的签发者
sub: 服务订阅者,也就是发起请求的服务消费者
aud: 服务提供者,也就是接收请求,验证通过后才提供服务
iat: JWT的签发时间
exp: JWT的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该JWT都是不可用的
jti: JWT的唯一标识,唯一性标识token,防重放攻击
- 公共声明
可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息。
- 私有声明
服务提供者和服务消费者共同定义的声明。
然后将payload进行base64URL编码,组成JWT的第2段。
注:payload务必不要存放敏感信息,因为base64URL只是一种编码方式,payload信息本身并不加密。
signature
JWT的第3段signature是签名信息,用于保证JWT的payload信息的完整性(header和payload数据不会被篡改)。
需要指定一个私钥(secret)用于签名。该私钥保存在服务器中,并且不能向用户公开。然后,使用标头中指定的签名算法(默认情况下为HMAC SHA256)根据以下算法生成签名。
HMACSHA256 ( base64UrlEncode(header) + "." + base64UrlEncode(payload) , secret ) 。
客户端收到服务端消息后,解析出header和payload,再按同样算法,使用公钥验签。验签通过,证明这是服务端发来的消息,是完整的。
【Base64URL编码】
编码的原因:方便以字节的方式表示数据,便于存储和网络传输。
如前所述,JWT的header和payload都用到了Base64URL编码。该编码方式和常见Base64算法类似,稍有差别。
因为作为令牌的JWT可以放在URL中(例如api.example/?token=xxx),而Base64中用到了3个字符"+","/"和"=",在URL中有特殊含义,因此Base64URL中做了替换:"="去掉,"+"用"-"替换,"/"用"_"替换,这就是Base64URL算法。
【用法】
客户端接收服务器返回的JWT,将其存储在Cookie或LocalStorage中。
此后,客户端将在与服务器交互中都会带JWT。
如果将它存储在Cookie中,就可以自动发送,但是不能跨域,
因此一般是将JWT放入HTTP请求的Header头信息 Authorization字段中 Authorization: Bearer
另一种做法是,当跨域时,将JWT被放置于POST请求的数据主体中。
【注意】
1、JWT默认不加密,但可以加密。生成原始令牌后,可以使用改令牌再次对其进行加密。
2、当JWT未加密时,一些私密数据千万不要通过JWT传输。
3、JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。
4、JWT的最大缺点是:服务器不保存会话状态,所以在使用期间,不可能取消令牌或更改令牌的权限。也就是说,一旦JWT签发,在有效期内将会一直有效。
5、JWT本身包含认证信息,因此一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证。
6、为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输。