JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间以json数据格式安全的传输信息。
JWT令牌由三个部分组成,分别是头部(Header)、载荷(Payload)和签名(Signature),它们之间使用点(.)分隔。
一个JWT令牌的典型结构如下:
xxxxx.yyyyy.zzzzz
Header(头部): 头部通常由两部分组成:令牌类型(即 "JWT")和所使用的签名算法(如HMAC SHA256或RSA)。头部是一个JSON对象,使用Base64Url编码。
{
"alg": "HS256",
"typ": "JWT"
}
Payload(载荷): 携带一些自定义信息、默认信息等。载荷部分包含声明(claims),声明是关于实体(通常是用户)和其他数据的声明。有三种类型的声明:注册声明(registered claims)、公共声明(public claims)和私有声明(private claims)。这个部分也是一个JSON对象,使用Base64Url编码。
{
"id": "1",
"name": "John",
"exp": 1516239022
}
Signature(签名): 签名部分是为了确保令牌不被篡改。它由编码后的头部、编码后的载荷和一个密钥通过指定签名算法计算而来。
正是因为jwt令牌数字签名部分的存在,所以整个jwt 令牌是非常安全可靠的。一旦jwt令牌当中任何一个部分、任何一个字符被篡改了,整个令牌在校验的时候都会失败,所以它是非常安全可靠的。
假如采用HMAC SHA256算法,签名的完整创建过程如下所示:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
假设Header为:
{
"alg": "HS256",
"typ": "JWT"
}
编码后的Header为:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
假设Payload为:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
编码后的Payload为:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
生成签名:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
假设secret为:your-256-bit-secret
生成的签名(Base64Url):
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
完整的 JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
传统会话跟踪技术(同一次会话中多次请求之间的数据可以共享)是通过cookie和session,但这两者有许多缺点。
以用户验证这一实际场景举例,如果使用JWT令牌进行用户验证,服务器在用户成功登录后生成一个JWT令牌,并将其发送给客户端浏览器。客户端在后续的每个请求中都携带这个令牌,服务器通过验证这个令牌是否存在、令牌是否合法这两个方式来确认用户身份。JWT令牌的优点:
因为JWT是无状态的,包含所有必要的信息,并且可以通过签名来验证其完整性,所以不同服务器只需知道签名密钥即可验证令牌、共享数据,而不需要共享会话状态,从而实现了多服务器的之间的共享。
JWT令牌多服务器共享的场景举例:
假设有一个负载均衡的应用,分布在多个服务器上,用于处理用户的请求。系统架构主要包含以下三个部分:
步骤1:用户通过HTTP POST请求,用于发送用户的登录信息到服务器,以进行身份验证。负载均衡器将请求分配到服务器1。
POST /login HTTP/1.1
Host: example.com
Content-Type: application/json
{
"username": "user1",
"password": "password123"
}
步骤2:服务器1验证用户凭证,如果有效,则生成一个JWT令牌,包含用户ID和其他信息,并使用服务器的签名密钥进行签名。以下代码声明令牌的主题为user1,令牌有效期为1个小时,使用HS512(HMAC SHA-512)算法和密钥“shared-secret-key”对JWT进行签名。
String jwtToken = Jwts.builder()
.setSubject("user1")
.setExpiration(new Date(System.currentTimeMillis() + 3600000))
.signWith(SignatureAlgorithm.HS512, "shared-secret-key")
.compact();
步骤3:服务器1将JWT令牌的字符串形式token返回给客户端,客户端将这个令牌存储在本地存储或Cookie中。唠叨:客户端在后续的每次请求中,都需要在请求头header中将这个令牌携带到服务端,请求头的名称为 token ,值为登录时下发的JWT令牌,验证通过后才能放行处理请求。
HTTP/1.1 200 OK
Content-Type: application/json
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
步骤4:客户端请求一个受保护的资源,负载均衡器将请求分配到服务器2。服务器2接收到请求后,从HTTP请求头部提取名为token的JWT令牌,并使用共享的签名密钥"shared-secret-key"验证令牌。如果令牌签名验证成功且未过期,则处理请求并返回响应。
// 验证JWT令牌
Claims claims = Jwts.parser()
.setSigningKey("shared-secret-key")
.parseClaimsJws(eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...)
.getBody();
String username = claims.getSubject();
步骤5:服务器2验证令牌成功后,处理请求并返回响应。
HTTP/1.1 200 OK
Content-Type: application/json
{
"message": "This is a protected resource."
}
1.数据存储方式
Session:
JWT:
2.扩展性
Session:
JWT:
3.安全性
Session:
JWT:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。