什么是JWT
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
来自于 https://jwt.io/ 的解释
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties. JWT.IO allows you to decode, verify and generate JWT.
传统的认证模式
传统基于session认证,Session
是每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session
都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。
因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。安全性也有问题。
基于token的鉴权机制
基于token
的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。
流程:
1、用户使用用户名密码来请求服务器
2、服务器进行验证用户的信息
3、服务器通过验证发送给用户一个token
4、客户端存储token
,并在每次请求时附送上这个token
值
5、服务端验证token
值,并返回数据
这个 token
必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了 Access-Control-Allow-Origin: *
。
JWT 的构成
第一部分:头部(header)
第二部分:载荷(payload, 类似于飞机上承载的物品)
第三部分:签证(signature)。
header
jwt的头部承载两部分信息:
- 声明类型,这里是
jwt
- 声明加密的算法 通常直接使用 HMAC SHA256、HmacSha512
完整的头部就像下面这样的JSON:
{
"alg": "HS512",
"typ": "JWT"
}
然后将头部进行base64
,构成了第一部分.eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9
playload
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
- 标准中注册的声明
- 公共的声明
- 私有的声明
标准中注册的声明
- iss: jwt签发者
- sub: jwt所面向的用户
- aud: 接收jwt的一方
- exp: jwt的过期时间,这个过期时间必须要大于签发时间
- nbf: 定义在什么时间之前,该jwt都是不可用的.
- iat: jwt的签发时间
- jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
标准中注册的声明 通常情况下,只是建议不强制使用
公共的声明 :
公共的申明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
私有的声明 :
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,通过 base64编码,意味着该部分信息可以归类为明文信息。
定义一个payload:
{
"iss": "clientid",
"aud": "clientid",
"sub": "1",
"id": "1",
"iat": 1639637415,
"exp": 1639639215
}
然后将其进行base64
编码,得到Jwt
的第二部分:
eyJpc3MiOiJjbGllbnRpZCIsImF1ZCI6ImNsaWVudGlkIiwic3ViIjoiMSIsImlkIjoiMSIsImlhdCI6MTYzOTYzNzQxNSwiZXhwIjoxNjM5NjM5MjE1fQ.
signature
JWT 的第三部分是一个签证信息,这个签证信息由三部分组成:
header
(base64后的)payload
(base64后的)secret
这个部分需要 base64
编码后的 header
和 base64
后的 payload
使用。连接组成的字符串,然后通过header
中声明的加密方式进行加盐 secret
组合加密,然后就构成了jwt
的第三部分。
secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以一定保存好私钥,在任何场景都不应该流露出去。
验证流程
代码演示
//私钥
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_appSettings.Secret));
var token = new JwtSecurityToken(
claims: new Claim[] //payload 部分包含功能申明和私有申明
{
new Claim(JwtRegisteredClaimNames.Iss, "clientid"),
new Claim(JwtRegisteredClaimNames.Aud, "clientid"),
new Claim(JwtRegisteredClaimNames.Sub, "1"),
new Claim("id", "1"),
new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64),
},
expires: new DateTimeOffset(DateTime.Now.AddMinutes(30)).DateTime,
signingCredentials: new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha512) //signature
);
token 检查
生成 JWT 后在 https://jwt.io/ 可以验证
如图: