文章目录
一、身份认证
常见的服务器端身份认证机制有两种,分别为:
1.基于 session 的认证机制
HTTP 是一种无状态的协议,这意味着若无登录态维持机制,则每次请求时都需要提交用户名与密码进行身份验证,而 session 机制就是用于在服务器端记录用户的登录状态。
session 是一串在服务器生成的字符串,用以唯一地标识一个用户。第一次身份验证后,服务器生成一串字符串并将字符串保存在服务端,同时将该字符串写入返回的响应头(Cookie字段)。浏览器在收到字符串后,将该字符串保存为 Cookie。同一个用户(浏览器)再次访问服务器时,浏览器会在请求中自动添加 Cookie 以标识当前用户的身份,服务器收到请求后会与先前保存的字符串进行核验,确认用户的身份,以此来代替先前的用户名密码的认证方式。
session 认证的弊端
-
增加开销:由于 session 都是保存在服务端内存中的,随着认证用户的增加,服务端的开销会明显增大。
-
拓展性差:用户的认证记录被保存在数据库/内存中,这对于分布式的应用来说,增加了应用的拓展难度。
-
CSRF攻击:因为是基于 Cookie 来进行用户识别的,故容易受到跨站请求伪造的攻击。
2.基于 token 的鉴权机制
基于 token 的鉴权机制是无状态的,它无需在服务器端保存用户的认证信息或会话信息,这意味着应用无需考虑用户在哪一台服务器登录了,为应用拓展提供了便利:
JWT(全称Json Web Token)是一个规范,用于在用户与服务器之间传递可靠信息JTW官网。服务器端在初次验明用户的身份后,会生成带有签名的 Json 对象并将它返回客户端,客户端收到这个 Json 对象后存储起来。在之后的请求中,客户端将该 Json 对象连通请求一并发送至服务器,服务器通过这个 Json 对象标识用户。
token 的优点
只需在服务器端记录 secret 密钥,而无需保存任意会话信息。每个用户的身份,都是通过对 token 的计算实时获取的,即将空间开销转换为时间(算力)开销。
二、JWT 结构
JWT 是由三段字符串与两个.
组成,这三段字符串按顺序依次为:
1.头部(header)
头部是元数据,其是一个 Json 对象,描述了诸如签名使用的算法、令牌类型等信息。例如:
// Header
{
"alg": "HS256",
"typ": "JWT"
}
上述头部alg
属性指明了签名算法是 HS256 算法,即 JWT 的默认加密算法;typ
表示令牌类型,这里就是 JWT。
2.载荷(payload)
荷载是 JWT 的主体,同样是 Json 对象,用于包含相关声明。荷载包含三个部分:
① 标准注册声明
预定义的声明,即标准规定的字段,并不强制使用,但一般推荐使用。它们有:
-
iss:JWT 的签发者/发行人
-
sub:该 JWT 面向的用户
-
aud:接收方
-
exp:JWT 的过期时间,必须大于签发时间
-
nbf:JWT 的生效时间,在这个时间之前该 token 都是无效的
-
iat:JWT 的签发时间
-
jti:JWT 唯一身份标识,主要用来作为一次性 toekn,从而避免重放攻击
② 公共声明
公共声明可以添加任何信息,一般添加用户的相关信息或业务信息。不建议添加敏感信息,因为该部分在客户端可解密。
③ 私有声明
私有声明是服务器端与客户端共同定义的声明,同样不建议在这里添加敏感信息,因为 base64 是对称解密的,意味着该部分信息可以归类为明文信息。
下面是一个有效荷载的例子:
{
"iss": "www.wjiaman.fun",
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
对其进行加密,即可得到 JWT 的第 2 部分。
3.签名(signature)
JWT 的第3部分是一个签证信息,该签证信息由 3 个部分组成:
-
header(base64加密后)
-
payload(base64加密后)
-
secret
首先,需要指定一个密钥secret
,这个密钥只有服务器知道,用来进行 jwt 的签发与验证,它是服务端的私钥,不应泄露。只要知道了这个secret
,就能自行签发 token。
然后,使用 header 中指定的签名算法,用 secret 进行加盐加密
header.payload
,产生签证(其中 header 与 payload 都是加密后的字符串)。
// javascript
let encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
let signature = HMACSHA256(encodedString, 'secret');
计算出签名后,将 Header、Payload、Signature 用`.`组合起来后,就可以返回给用户。
三、客户端处理 JWT
客户端收到服务器返回的 token 后,可以存储在 Cookie 里,也可以存储在 localStorage 中。
此后,客户端每次与服务器通信,都要带上这个 token。若将这个 token 放在 Cookie 中,则浏览器将自动发送,但这样的缺点是不能跨域。于是更好的选择是放在 HTTP 请求头信息中的某个字段中(如Authorization
)。
另一种做法是,跨域请求时,token 就放在 POST 请求所提交的数据里。
四、JWT 的几个特点
-
由于 payload 部分的存在,故 JWT 可以在自身存储一些业务逻辑的非敏感数据。
-
JWT 是不加密的,不应在 payload 中放置任何敏感数据(payload 是唯一可以放置自定义数据的部分),客户端能够解密 payload 中的所有信息。
-
JWT 中唯一具有辨识作用的部分为 signature,服务器端通过 signature 计算结果的正确性来判断 payload 中信息(即用户信息)的正确性,从而确保 payload 中的信息没有被修改过。
-
JWT 默认是不加密的,但也可以加密。在生成原始 token 后,可以用密钥再加密一次。
-
JWT 在不加密的情况下,不能将敏感数据写入 JWT。
-
JWT 不仅可以用于认证,还能用于交换信息。有效地使用 JWT,能够降低服务器查询数据库的次数。
-
JWT 最大的缺点在于,由于服务器不保存任何状态信息,因此无法在使用过程中废除某个 token,使其失效,或更改某个 token 的权限。也即是说,一旦 JWT 一签发,在到期之前始终有效,除非服务器部署额外的逻辑。
-
JWT 本身包含认证信息,一旦泄露,任何人都将获得对应令牌的所有权限。为了减少盗用,JWT 的有效期应相应短。且不应使用 HTTP 协议明码传输,尽量使用 HTTPS 协议进行传输。