前后端分离 之 JWT简介

简介

Json web token (JWT),是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519)。该标准被设计为紧凑且安全的,一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息。当然该标准也可直接被用于认证,也可被加密。

JWT 定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法。JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名。

特点:

1、简洁(Compact):尺寸较小,意味着传输速度越快。JWT可以通过URL,POST参数或HTTP头部发送。
2、自包含(Self-contained):有效载荷包含有关用户的所有必需信息,避免了多次查询数据库的需要。

1、优点
(1)因为Json的通用性,所以JWT是支持跨语言的,像Java、JavaScript、NodeJS、PHP等很多语言都可以使用。
(2)因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
(3)便于传输,JWT的构成非常简单,字节占用很小,所以它是非常便于传输的。
(4)它不需要在服务端保存会话信息, 所以它易于应用的扩展
2、安全相关
(1)不应该在JWT的payload部分存放敏感信息,因为该部分是客户端可解密的部分。
(2)保护好secret私钥,该私钥非常重要。
(3)如果可以,请使用HTTPS协议,不!是务必使用HTTPS!

和Session方式存储id的比较

Session方式存储用户id的最大弊病在于Session是存储在服务器端的,所以需要占用大量服务器内存,对于较大型应用而言可能还要保存许多的状态。一般而言,大型应用还需要借助一些KV数据库和一系列缓存机制来实现Session的存储。而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。除了用户id之外,还可以存储其他的和用户相关的信息,例如该用户是否是管理员、用户所在的分组等。虽说JWT方式让服务器有一些计算压力(例如加密、编码和解码),但是这些压力相比磁盘存储而言可能就不算什么了。

使用场景:

  • 验证
    JWT最常见的应用情况。 一旦用户登录,每个后续请求将包括JWT。它将允许用户访问该令牌允许的路由,服务和资源。
    单点登录是当今广泛使用JWT的一项功能,因为它的开销很小,而且能够轻松地跨不同域使用。

  • 信息交换
    JWT是在各方之间安全传输信息的好方法, 因为JWT可以被签名(例如使用公钥/私钥对进行签名)。此外,由于使用头部(header)和有效载荷(payload)计算签名,因此还可以验证内容是否未被篡改。

结构说明

JWT包含三个由点(.)分隔的部分,它们是:

  • 头部(header)
  • 有效负载(payload)
  • 签名(signature)
    因此,JWT通常看起来如下所示:
    在这里插入图片描述

Header 头部

头部包含token 类型(即JWT)和采用的加密算法(如HMAC SHA256或RSA)。

{
  "alg": "HS256",
  "typ": "JWT"
}

然后,将这个JSON用Base64编码,形成JWT的第一部分。

Payload 负载

这部分是包含声明的有效载荷(存放具体信息,比如用户ID)。 声明是关于实体(通常是用户)和附加元数据的声明。 有三种类型的声明:

  • 标准声明
  • 公开声明
  • 私人声明

(1)标准声明规范里面预先定义了如下几个常用的声明(建议非强制):

  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的.
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
{
    "iss": "lion1ou JWT",
    "iat": 1441593502,
    "exp": 1441594722,
    "aud": "www.example.com",
    "sub": "[email protected]"
}

然后将有效载荷进行Base64编码,以形成JSON Web令牌的第二部分。

Signature 签名

签名通常用于验证JWT的发件人是谁,并JWT在传送的过程中不被篡改。

前面两部分都是使用 Base64 进行编码的,即前端可以解密知道JWT里面的信息。
Signature 需要使用编码后的 header 和 payload 以及我们提供的密钥,然后使用 header 中指定的签名算法(HS256)进行签名。

要创建签名部分,必须采用头部(header),有效载荷(payload),密钥(secret),以及头部中指定的算法。例如,如果你想使用HMAC SHA256算法,签名将按以下方式创建:
在这里插入图片描述
注意:上图红框中的secret是保存在服务器端的,JWT的签发生成也是在服务器端的,secret就是用来进行JWT的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret,那就意味着客户端是可以自我签发jwt了。

签名的过程,实际上是对头部以及负载内容进行签名,防止内容被窜改。如果有人对头部以及负载的内容解码之后进行修改,再进行编码,最后加上之前的签名组合形成新的JWT的话,那么服务器端会判断出新的头部和负载形成的签名和JWT附带上的签名是不一样的。如果要对新的头部和负载进行签名,在不知道服务器加密时用的密钥的话,得出来的签名也是不一样的。

注意

Base64是一种是可逆的编码技术,这样我们的信息就有可能被暴露。所以,在JWT中,不应该在负载里面加入任何敏感的数据。在上面的例子中,我们传输的是用户的User ID。这个值实际上不是什么敏感内容,一般情况下被知道也是安全的。但是像密码这样的内容就不能被放在JWT中了。如果将用户的密码放在了JWT中,那么怀有恶意的第三方通过Base64解码就能很快地知道用户的密码了。
因此,JWT适合用于向Web应用传递一些非敏感信息。JWT还经常用于设计用户认证和授权系统,甚至实现Web应用的单点登录。

使用

在这里插入图片描述

  1. 前端通过Web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。
  2. 后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT。形成的JWT就是一个形同xxxxx.yyyyy.zzzzz的字符串。
  3. 后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或sessionStorage上(关于JWT存放到哪里,有人说存放到本地存储,有人说存 cookie。个人偏向于放在本地存储),退出登录时前端删除保存的JWT即可。
  4. 前端在每次请求时将JWT放入HTTP Header中的Authorization位(解决XSS和XSRF问题)。
  5. 后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选)。
  6. 验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果。

附:

单点登录
Session方式来存储用户id,一开始用户的Session只会存储在一台服务器上。对于有多个子域名的站点,每个子域名至少会对应一台不同的服务器,例如:www.taobao.com,nv.taobao.com,nz.taobao.com,login.taobao.com。所以如果要实现在login.taobao.com登录后,在其他的子域名下依然可以取到Session,这要求我们在多台服务器上同步Session。使用JWT的方式则没有这个问题的存在,因为用户的状态已经被传送到了客户端。
发布了258 篇原创文章 · 获赞 678 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/lianghecai52171314/article/details/104066823
今日推荐