多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
## 一、概述 认证的方式,主要有三种: 1、用户名和密码鉴权,使用Session保存用户鉴权结果; 2、使用OAuth进行鉴权(其实OAuth也是一种基于Token的鉴权,只是没有规定Token的生成方式)OAuth其实对于不做开放平台的公司有些过于复杂; 3、自行采用Token进行鉴权; >[danger] 传统的web应用,一般还是用传统的session机制,而jwt适合于那些前后端分离的接口服务或restful api; ## 二、传统的session认证 ### **概述** http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。 ### **问题** **Session**: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。 **扩展性**: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。 **CSRF**: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。 ## 三、基于token的鉴权机制 ### **概述** 基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。 这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持`CORS(跨来源资源共享)`策略,一般我们在服务端这么做就可以了`Access-Control-Allow-Origin: *` ### **流程** * 用户使用用户名密码来请求服务器; * 服务器进行验证用户的信息; * 服务器通过验证发送给用户一个token; * 客户端存储token,并在每次请求时附送上这个token值; * 服务端验证token值,并返回数据; ## 四、JWT JWT是由三段信息构成的,将这三段信息文本用`.`链接一起就构成了Jwt字符串。就像这样: ``` eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ ``` ### **JWT的构成** 三个部分构成,每个部分都是用BASE64编码的,用`.`连接成一个完整的字符串,构成了最终的jwt; 第一部分我们称它为头部(header); jwt的头部承载两部分信息: * 声明类型,这里是jwt * 声明加密的算法 通常直接使用 HMAC SHA256 完整的头部就像下面这样的JSON: ``` { 'typ': 'JWT', 'alg': 'HS256' } 然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分头部的内容了; eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 ``` 第二部分我们称其为载荷(payload, 类似于飞机上承载的物品); 载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分: * 标准中注册的声明 * 公共的声明 * 私有的声明 **标准中注册的声明** (建议但不强制使用) : * **iss**: jwt签发者 * **sub**: jwt所面向的用户 * **aud**: 接收jwt的一方 * **exp**: jwt的过期时间,这个过期时间必须要大于签发时间 * **nbf**: 定义在什么时间之前,该jwt都是不可用的. * **iat**: jwt的签发时间 * **jti**: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。 **公共的声明** : 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密. **私有的声明** : 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。 定义一个payload: ``` { "sub": "1234567890", "name": "John Doe", "admin": true } 然后将其进行base64加密,得到Jwt的第二部分: eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 ``` 第三部分是签证(signature); jwt的第三部分是一个签证信息,这个签证信息由三部分组成: * header (base64后的) * payload (base64后的) * secret 这个部分需要base64加密后的header和base64加密后的payload使用`.`连接组成的字符串,然后通过header中声明的加密方式进行加盐`secret`组合加密,然后就构成了jwt的第三部分; >[danger] 注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。 ### **交互过程** ![](https://img.kancloud.cn/50/b7/50b788cd6b9ec8c8e4ba1d13604f57ff_727x400.png) ### **总结** **优点:** * 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。 * 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。 * 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。 ### **安全相关** * 不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。 * 保护好secret私钥,该私钥非常重要。 * 如果可以,请使用https协议 * 它不需要在服务端保存会话信息, 所以它易于应用的扩展