什么是JWT
JWT全称Json Web Token,翻译为JSON格式的网络令牌,很多时候又简称为Token(JWT是Token的一种实现方式)。它要解决的问题,就是为多种终端设备,提供统一的、安全的令牌格式。因此,JWT只是一个令牌格式而已,你可以把它存储到Cookie,也可以存储到localstorage,没有任何限制!同样的,对于传输,你可以使用任何传输方式来传输JWT,一般来说,我们会使用HTTP请求头(Authorization)来传输它。比如,当登录成功后,服务器可以给客户端响应一个JWT。
JWT的结构解析

第一部分我们称它为头部(header),第二部分我们称其为载荷(payload),第三部分是签证(signature)
Header
Header通常由两部分组成:令牌的类型(即JWT)和所使用的签名算法(例如HMAC SHA256或RSA)。 例如:
{
"alg": "HS256",
"typ": "JWT"
}
到目前为止,jwt的签名算法有三种:
-
HMAC【哈希消息验证码(对称)】:(HS256/HS384/HS512)
-
RSASSA【RSA签名算法(非对称)】(RS256/RS384/RS512)(一般用这个)
-
ECDSA【椭圆曲线数据签名算法(非对称)】(ES256/ES384/ES512)
Header会被Base64Url编码为JWT的第一部分。即为:
$ echo -n '{"alg":"HS256","typ":"JWT"}'| base64
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Base64编码概述:Base64不是一种加密算法,它实际上是一种“二进制转换到文本”的编码方式,它能够将任意二进制数据转换为ASCII字符串的形式,以便在只支持文本的环境中也能够顺利地传输二进制数据。
(1)base64编码:把二进制数据转为字符;
(2)base64解码:把字符转为二进制数据;
Base64编码原理详见:C语言base64实现-优快云博客
payload
载荷就是存放有效信息的地方,它包含声明(要求)。声明有三种类型:
-
registered claims:标准中注册的声明。这里有一组预定义的声明,它们不是强制的,但是推荐
- iss: jwt签发者
- sub: jwt所面向的用户
- aud: 接收jwt的一方
- exp: jwt的过期时间,这个过期时间必须要大于签发时间
- nbf: 定义在什么时间之前,该jwt都是不可用的
- iat: jwt的签发时间
- jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
-
public claims:公共的声明
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
-
private claims:私有的声明
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
问:jwt 上的私有(private)声明和公共(public)声明有什么性质上的区别?
公开声明需要是抗冲突的自定义声明名称。它们的名称应该是 UUID 或以 URL 为前缀,以便为它们创建安全的命名空间并避免冲突。而私有声明不需要抗冲突的自定义声明名称。总结他们的本质区别就是:公共声明必须具有普遍的抗碰撞性,而私有声明则不需要。
Payload会被Base64Url编码为JWT的第二部分。即为:
$ echo -n '{"iat":1593955943,"exp":1593955973,"uid":10,"username":"test","scopes":["admin","user"]}'| base64
eyJ1c2VybmFtZSI6InRlc3QiLCJpYXQiOjE1OTM5NTU5NDMsInVpZCI6MTAsImV4cCI6MTU5Mzk1NTk3Mywic2NvcGVzIjpbImFkbWluIiwidXNlciJdfQ
注意:对于已签名的令牌,此信息尽管可以防止篡改,但任何人都可以读取。除非将其加密,否则请勿将机密信息放入JWT的payload或Header中。
Signature
令牌签名:按照头部固定的签名算法对整个令牌进行签名,该签名的作用是:保证令牌不被伪造和篡改
签名的过程就是对前两部分(头部和负载)通过Base64URL编码后的内容使用服务端存储的私钥和头部的加密算法进行加密,生成签名部分的数据,头部、负载、签名三部分通过.符号进行连接。
JWT认证过程
JWT和普通Token的区别
- 普通Token在服务端验证的时候,服务端要进行数据的查询操作
- 使用JWT验证Token的方式是使用服务端存储的私钥进行验证,不需要数据库的查询
前端如何发送token给后端
使用Authorization请求头的方式发送,具体格式如下:
str="Bearer "+token;
'Authorization':str,
Bearer token是一种比较安全的身份验证方式
Basic token和Bearer token的区别
-
Basic token是一种基本的身份验证方式,它使用base64编码的用户名和密码,将其加密成一个字符串,在http request的header中以"Authorization: Basic [编码后的字符串]"的形式发送到服务器进行身份验证。这种身份验证方式的缺点在于,由于用户名和密码是以base64编码的形式传递,因此容易被拦截并进行破解,存在风险。
-
Bearer token是一种更为安全的身份验证方式,它通过在用户登陆成功后,由服务器返回一个随机的token,这个token是具有时效性和唯一性(同一用户,不同时间返回的token不一样)的,客户端在每次请求时,将token放在http request的header中以"Authorization: Bearer [token]"的形式发送到服务器进行身份验证。服务器端通过token来验证客户端的身份是否合法。Bearer token的安全性更高,每个用户拥有独立的token,token的时效性保证了一段时间后即使被截获,也不能被长时间滥用,提高了安全性。
因此,二者之间的本质区别在于安全性的差异。
问:token可以跨域,但是普通session不能跨域的本质原因是什么?
-
Session是一种服务器端的状态管理机制,而token是一种客户端的认证与授权机制。
-
Session在服务器端存储,客户端请求时需要将session ID发送到服务器来恢复用户状态,因此只适用于同一域名下的请求。如果在跨域请求时希望保持用户状态,需要跨域共享session ID,但这需要使用特殊的技术手段来实现。
-
Token则是将认证信息和密钥加密后放在客户端,客户端每次请求时携带该token,在服务器端对该token进行验证并恢复用户身份信息。由于token存储在客户端中,可以跨域使用,但需要注意安全性问题。
因此,Session不能跨域的本质原因是Session需要在服务器端存储和管理,仅适用于同一域名下的请求;而Token是一种可跨域使用的认证授权机制,但需要注意其安全性问题。
问:在使用JWT进行跨域时,需要注意哪些安全问题?
-
CSRF(Cross-site request forgery)攻击:攻击者可以伪造请求,在用户不知情的情况下执行非法操作。为了防止CSRF攻击,可以在JWT中添加CSRF令牌,每次请求时校验CSRF令牌是否匹配。
-
XSS(Cross-site scripting)攻击:攻击者可以通过注入恶意代码来盗取用户的登录信息。为了防止XSS攻击,可以在JWT中添加HttpOnly属性,禁止JavaScript代码读取该cookie。
-
安全算法的选择:选择安全的签名算法和加密方式来保护JWT的完整性和保密性。通常建议使用HS256算法(使用HMAC SHA-256哈希算法)或RS256算法(使用RSA加密算法)。
-
JWT的有效期和过期处理:设置JWT的有效期和过期处理方式,例如在JWT中添加过期时间和刷新Token等机制,以减少因长时间保留JWT而带来的安全风险。
-
Token管理:管理JWT的签名秘钥和密钥,及时更新并存储安全的密钥,控制Token的发布和撤回,防止Token被盗用。