详解JWT
详解JWT
1.JWT的介绍
1.1. 什么是JWT
JWT的全称是JSON Web Token。它是目前比较流行的一种跨域认证解决方法,是一种基于Token的认证授权,它可以在各个服务器以JSON对象的形式安全的传输信息。
1.2.JWT的使用场景
- 身份认证: JWT 可以被用作用户登录的身份验证凭证。当用户成功登录后,服务端可以生成一个包含用户信息的 JWT(但是不要携带敏感信息),并将其返回给客户端。以后,客户端在每次请求时都会携带这个 JWT,服务端通过验证 JWT 的签名来确认用户的身份。
- 授权: 在用户登录后,服务端可以生成包含用户角色、权限等信息的 JWT,并在用户每次请求时进行验证。通过解析 JWT 中的声明信息,服务端可以判断用户是否有权限执行特定的操作或访问特定的资源。
2.JWT的结构
JWT由三部分组成,Header(头部)、Payload(载荷)、Signature(签名)。
- Header(头部) : 描述 JWT 的元数据,定义了生成签名的算法以及 Token 的类型。Header 被 Base64Url 编码后成为 JWT 的第一部分。
- Payload(载荷) : 用来存放实际需要传递的数据,包含声明(Claims),如sub(subject,主题)、jti(JWT ID)。Payload 被 Base64Url 编码后成为 JWT 的第二部分。
- Signature(签名):服务器通过 Payload、Header 和一个密钥(Secret)使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。生成的签名会成为 JWT 的第三部分。
2.1.Header(头部)
Header是JSON格式的数据。它由两部分组成,分别是令牌类型和签名算法。
- typ(Type):令牌类型,也就是 JWT。
- alg(Algorithm):签名算法,比如 HS256。
{"alg": "HS256","typ": "JWT"
}
2.2.Payload(载荷)
Payload 也是 JSON 格式的数据,你可以用来存放一下实际需要传递的数据。JWT共有七个官方字段,你可以选择性的使用。
- iss(issuer):JWT 签发方。
- iat(issued at time):JWT 签发时间。
- sub(subject):JWT 主题。
- aud(audience):JWT 接收方。
- exp(expiration time):JWT 的过期时间。
- nbf(not before time):JWT 生效时间,早于该定义的时间的 JWT 不能被接受处理。
- jti(JWT ID):JWT 唯一标识。
当然了,除了官方给的字段,你也可以定义自己的字段进行传递。如下:
{"uid": "ff1212f5-d8d1-4496-bf41-d2dda73de19a","sub": "1234567890","name": "John Doe","exp": 15323232,"iat": 1516239022,"scope": ["admin", "user"]
}
Payload默认是不加密的,但是后文描述JWT的优点会写,也可以选择对Payload进行加密,来保护用户的相关信息。
2.3.Signature(签名)
Signature 部分是对前两部分的签名,作用是防止 JWT(主要是 payload) 被篡改。这个签名的生成需要用到:
- Header + Payload。
- 存放在服务端的密钥(一定不要泄露出去)。
- 签名算法。
签名的计算公式如下:
HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
3.JWT的样式
JWT本质就是一组字串,通过 分隔符. 进行分割。第一部分是Base64Url 编码后的Header,第二部分是Base64Url 编码后Payload,第三部分是生成的签名。通常就是这样的:xxxxx.yyyyy.zzzzz。示例如下:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
你可以在jwt.io这个网站上对JWT进行解码,然后可以获得Header、Payload、secret这三部分。当然,secret是无法解码出来的。
4.JWT进行身份验证的流程
在基于 JWT 进行身份验证的的应用程序中,客户端成功登录后,服务器通过 Payload、Header 和 Secret(密钥)创建 JWT 并将 JWT 发送给客户端。客户端接收到 JWT 之后,会将其保存在 Cookie 或者 localStorage 里面,以后客户端发出的所有请求中的Header都会携带这个令牌。服务端检查JWT并从中获取用户相关信息。
下面是俩个建议:
- 建议将 JWT 存放在 localStorage 中,放在 Cookie 中会有 CSRF 风险。
CSRF(Cross Site Request Forgery) 一般被翻译为 跨站请求伪造 。带来的危害就是可能使用你的身份去发送一些对你不友好的请求,比如转账,注销账号之类的。因为Cookie存储在客户端且随 HTTP/HTTPS 请求自动发送,极易成为攻击者的目标,导致数据泄露或身份被盗。
- 请求服务端并携带 JWT 的常见做法是将其放在 HTTP Header 的 Authorization 字段中(Authorization: Bearer Token)。
5.JWT的优缺点
5.1.优点
- 无状态: JWT 的验证是基于密钥的,因此它不需要在服务端存储用户信息。这使得 JWT 可以作为一种无状态的身份认证机制。
- 避免 CSRF 攻击: 一般情况下我们使用 JWT 的话,在我们登录成功获得 JWT 之后,一般会选择存放在 localStorage 中。前端的每一个请求后续都会附带上这个 JWT,但是整个过程压根不会涉及到 Cookie。因此,即使你点击了非法链接发送了请求到服务端,这个非法请求也是不会携带 JWT 的,所以这个请求将是非法的。就是说使用JWT不需要用到cookie,因此就不需要担心CSRF攻击了。
- 适合移动端应用: 移动端应用通常是无状态的,连接也可能不稳定或者中断。刚好JWT可以解决这些问题。
- 单点登录(SSO)友好:SSO 英文全称 Single Sign On,单点登录。SSO 是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。使用JWT,登陆后JWT保存在客户端,可以实现单点登录。
5.2.缺点
- 不可控:JWT最大的缺点就是不可控。因为它是无状态的,如果说我们想要在JWT的有效期内废弃掉这个JWT或者是更改它的权限,通常是无法做到的,只能等到JWT的有效期过了才可以。JWT不可控的缺点有很多影响,比如令牌无法即使撤销,令牌的有效期无法及时调整,无法精确追踪令牌的状态。
- 注销登陆后JWT还有效:即使用户退出登录或者修改密码或者账号被注销,但是JWT任然有效。
- JWT体积太大:JWT 结构复杂(Header、Payload 和 Signature),包含了更多额外的信息,还需要进行 Base64Url 编码,这会使得 JWT 体积较大,增加了网络传输的开销。
- JWT的续签:JWT的有效期一般都不会设置的太长(为了避免不可控带来的危害),那么如何实现动态刷新JWT,避免用户需要频繁的重新登陆也是一个问题。有一些解决办法,但是都有一些缺点,这里就不阐述了。