什么是JWT
JWT全称是JSON WEB TOKEN,是一种基于JSON的开放标准(RFC 7519),主要用于用户与服务器之间安全地传输信息。简单的说,JWT就是一种认证机制。
推荐
文章将率先在公众号「
Code满满
」与个人博客「
李益的小站
」上发布,如果本文对你有帮助,就关注一下公众号吧!
什么是认证
HTTP协议本身是一种无状态的协议,在应用程序中,用户需要输入用户名和密码来进行用户认证,获取属于用户自己的信息。但是如果每次都要用户输入用户名和密码,就太过于繁琐了;于是就有了
Session认证
与
Token认证
。
传统的Session认证
Session是一种记录客户端和服务器端会话状态的机制,是基于Cookie实现的,其认证流程如下:
- 客户端第一次请求服务器,服务器根据客户端提交的信息,创建对应的Session
- 服务器一般将Session保存在内存中,然后将SessionID返回给客户端
- 客户端收到SessionID后,会将SessionID保存在Cookie中,并记录此SessionID属于哪个域名
- 客户端第二次请求时,会将此域名下的Cookie信息发送给服务器,服务器获取Cookie信息后,会从中提取出SessionID并去查找自己保存的对应的Session;如果找到,则说明认证成功,否则认证失败
Session有如下缺陷:
- 因为Session一般保存在服务器的内存中,随着用户增多,服务器的开销会越来越大
- 因为Session是存在内存中,会带来一些扩展性问题
- 因为是基于Cookie来进行用户识别的, Cookie如果被截获,用户就会很容易受到CSRF攻击(跨站请求伪造攻击)
传统的Token认证
传统的Token认证流程如下:
- 客户端输入用户名和密码来请求服务器,服务器会根据客户端提交的信息,创建一个Token
- 服务器会将Token存储在数据库中或者缓存起来,然后将Token返回给客户端
- 客户端收到Token后会保存在本地,并在每次请求时带上Token,一般会放在请求的Header中
- 服务器会从请求信息中获取Token,并去查库验证Token是否有效
这种基于Token的认证是无状态的,能有效的防止CSRF攻击,但是这种认证方式不是没有缺陷,比如:每次服务器验证Token是否有效时都要去查库,这也是一种消耗;另外,客户端退出登录时也必须调用接口来通知服务器删除Token,比较繁琐。
JWT认证
JWT是目前主流的认证方式,其认证流程如下:
- 客户端输入用户名和密码来请求服务器,服务器根据客户端提交的信息,会创建一个Token,并返回给客户端,自己不会保存这个Token
- 客户端获取Token后会保存在本地,每次请求时都带上Token,一般会放在请求的Header中
- 服务器获取请求信息中的Token后,会直接解析这个Token,从中获取用户的相关信息,不用去查库;如果解析成功,则代表认证成功,否则代表Token无效,认证失败
JWT认证与传统的Token认证不同的地方在于:JWT认证的Token是交由客户端保管的,服务器端一般不存储Token;服务器验证Token是否有效是通过能否直接解析Token来判断的,如果能解析,则表示有效,否则无效。这样做的好处,就是服务器不用存储Token,不用查库,减少开销;在分布式或者多服务器的应用中,每个服务器都可以直接判断Token是否有效并解析,而不必去单独访问一个专门存储Token的服务器,扩展性得到很大提升。
从上述内容中可以明显看出,JWT认证的强大之处在于可以从Token中解析出有用的数据,而不像传统的Token,仅仅是一个身份标识。下面我们就看一下,JWT是怎样构成的。
构成
JWT共有三部分构成:
-
Header(头部)
-
Payload(负载)
-
Signature(签名 )
Header(头部)
头部一般会记录两种信息:
-
声明Token的类型,一般声明为
JWT
-
声明加密算法名称,一般声明为
HMAC
、**SHA256 **等
完整案例如下:
{
"typ": "JWT",
"alg": "HS256"
}
然后将头部信息使用Base64编码,构成Token的第一部分。
Payload(负载)
负载是存放有效信息的地方,包含三种声明:
Registered claims(注册声明)
,
Public claims(公共声明)
以及
Private claims(私有声明)
-
注册声明:一组预定义的声明,不强制,但推荐
-
iss (issuer)
:jwt签发者 -
sub (subject)
:jwt所面向的用户 -
aud (audience)
:接收jwt的一方 -
iat (issued at)
:jwt的签发时间 -
exp (expiration)
:jwt的过期时间,这个过期时间必须要大于签发时间 -
nbf (not before)
:声明jwt在指定的时间后有效 -
jti (JWT ID)
:jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击(如果JWT标识存在,那么发出者不能复用同一JWT标识,如果一JWT的jti声明与另一JWT完全相同,则被视为重放攻击)
-
-
公共的声明:可以添加任何的信息,一般添加用户的相关信息或其它业务需要的信息,但不建议添加敏感信息
-
私有的声明:一般是签发者和接收者所共同定义的声明,不建议存放敏感信息
完整案例如下:
{
"iss": "www.liyisite.com"
"sub": "1234567890",
"exp": 1618400868
"user_id": "1",
"username": "liyi",
}
对Payload进行Base64编码就得到JWT的第二部分。
Signature(签名 )
JWT的第三部分是一个签名信息,它是用Header中指定的那个签名算法对编码过的Header、编码过的Payload以及一个秘钥进行签名,从而生成的。
签名是用于验证消息在传递过程中有没有被更改;并且,对于使用私钥签名的token,它还可以验证JWT的发行者是否为它所指定的发行者。
缺陷
JWT虽然有种种好处,但是也有缺陷。因为JWT是一经签发,在未到期前都是有效的,加上token是存放在客户端,所以服务器无法废弃token。这意味着如果token被他人获取,即使签发新token,只要旧token未到失效时间,就依然有效,他人依然可以使用,这是非常危险的。
此时,后端一般会使用Redis等工具来缓存签发的jwt,设立黑名单,当签发新的jwt后,就会把旧jwt放入黑名单,直至旧jwt失效后,从缓存中移除。不过如此一来,就有点违背JWT的原则了。