本文首发于 hzzly的博客javascript
原文连接:JWT--JSON WEB TOKENhtml
JSON Web Token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519),该token被设计为紧凑且安全的,特别适用于分布式站点的单点登陆(SSO)场景。JWT的声明通常被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也能够增长一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。java
JWT是目前最流行的跨域认证解决方案之一。redis
在看为何使用以前,咱们必需要先了解以前咱们是如何进行验证请求的。算法
在 Session 认证方式中,用户登陆后发给服务器,服务器在接收并验证发送过来的帐号密码请求以后,就会把这个用户信息放入 Session 中,而后把 Session 存在服务器上,这样服务器就知道了这个用户的存在,当下一次用户访问的时候,就能认证了。api
但由于咱们知道http协议是一种无状态的协议,也就是说当下一次用户发送请求的时候,请求中没有任何信息能代表用户身份!也就是说不知道请求是谁发出来了,这样也就不能认证了。跨域
因此就须要利用 Cookie 来管理 Session,即把 SessionID 放入 HTTP 响应中发给客户端,并保存在客户端,当客户端发送下一次请求的时候,就把这个 SessionID 一块儿发送回来,这样就能此次的请求是谁发出来的了。浏览器
扩展:Cookie 是由客户端(一般是浏览器)保存的小型文本信息,其内容是一系列的键值对,是由 HTTP 服务器设置并保存在浏览器上的信息。缓存
内存开销大
: 咱们知道 Session 是存在服务器上的,实际上为了加快认证的速度,咱们通常都会放在内存中,这样当用户基数大的时候,内存的开销就会很大。固然也能够将 Session 存入到 Session 表或者是缓存(redis等)中,可是依旧会有这样的问题。安全
安全性(CSRF)
: 由于是基于 Cookie 进行用户识别,若是 Cookie 被截获,用户就会很容易收到跨站请求伪造的攻击。
分布式负载均衡
: 由于 Session 信息是被单个服务器所保存的,因此在分布式系统中就不能适用了。好比 Session 一开始是保存在 A 服务器上,可是下一次请求的时候,这个请求被服务器负载均衡转发到了 B 服务器,而 B 服务器则没有这个 Session 信息,因此就不能用过认证了。
由于 JWT 是由服务端生成的,经过请求传给客户端(客户端能够以任意方式存放)。因此服务器不须要存储任何 JWT 信息。这样就能避免了上述 Session 的几个问题了。固然 JWT 还有其自身的一些优势。
轻量级
:JWT是很是轻量级的,传输的方式多样化,能够经过URL/POST参数/HTTP头部等方式传输。
无状态/跨域认证
:token包含全部用于标识用户的信息,这消除了对会话状态的须要。若是咱们使用负载均衡,咱们依然能够将token传递给任何服务器,而不是存储在咱们登陆的同一台服务器上。
安全性
:无需担忧跨站请求伪造(CSRF)攻击。
由三个部分组成:header.payload.signature
header
:包含了两个部分 typ 和 alg,分别是声明类型和JWT的加密算法。
{
"alg": "HS256",
"typ": "JWT"
}
复制代码
通过 base64URL 加密以后获得 JWT 的第一部分信息:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
复制代码
payload
:负载,存放有效信息的地方。这些有效信息包含三个部分:标准中注册的声明、公共的声明 和 私有的声明。
除了官方字段,你还能够在这个部分定义私有字段:
{
"sub": "1234567890",
"nickname": "hzzly",
"username": "hzzly",
"scopes": [ "admin", "user" ]
}
复制代码
通过 base64URL 加密以后获得 JWT 的第二部分信息:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmlja25hbWUiOiJoenpseSIsInVzZXJuYW1lIjoiaHp6bHkiLCJzY29wZXMiOlsiYWRtaW4iLCJ1c2VyIl19
复制代码
signature
:是对前两部分的签名,防止数据篡改。由三个部分组成:header、payload 和 secret。其中 header 和 payload 都是加密后的字符串,secret就是一个字符串(密钥)。
const signature = HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret);
复制代码
算出签名之后,把 header、payload、signature 三个部分拼成一个字符串,每一个部分之间用"点"(.)分隔,就能够返回给客户端。
最终的jwt:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmlja25hbWUiOiJoenpseSIsInVzZXJuYW1lIjoiaHp6bHkiLCJzY29wZXMiOlsiYWRtaW4iLCJ1c2VyIl19.sXaHGg9SWyRpl-rhiSBFuD01G4yE3Gmi5m-JD7u6YyI
复制代码
面提到,header 和 payload 串型化的算法是 base64URL。这个算法跟 base64 算法基本相似,但有一些小的不一样。
JWT 做为一个令牌(token),有些场合可能会放到 URL(好比 api.example.com/?token=xxx)。base64 有三个字符+、/和=,在 URL 里面有特殊含义,因此要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。
客户端收到服务器返回的 JWT,能够储存在 sessionStorage 或 localStorage 里面。
此后,客户端每次与服务器通讯,都要带上这个 JWT。须要把它放在 HTTP 请求的头信息 Authorization 字段里面。
Authorization: Bearer <token>
复制代码
另外一种作法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。