分享一下群友面试虾皮遇到的对于 JWT 的面试真题。
相干面试题如下:
- 什么是 JWT? 为什么要用 JWT?
- JWT 由哪些局部组成?
- 如何基于 JWT 进行身份验证?
- JWT 如何避免 Token 被篡改?
- 如何增强 JWT 的安全性?
- 如何让 Token 生效?
- ……
什么是 JWT?
JWT(JSON Web Token)是目前最风行的跨域认证解决方案,是一种基于 Token 的认证受权机制。从 JWT 的全称能够看出,JWT 自身也是 Token,一种规范化之后的 JSON 构造的 Token。
Token 本身蕴含了身份验证所须要的所有信息,因而,咱们的服务器不须要存储 Session 信息。这显然减少了零碎的可用性和伸缩性,大大加重了服务端的压力。
能够看出,JWT 更合乎设计 RESTful API 时的「Stateless(无状态)」准则。
并且,应用 Token 认证能够无效防止 CSRF 攻打,因为 Token 个别是存在在 localStorage 中,应用 JWT 进行身份验证的过程中是不会波及到 Cookie 的。
我在 JWT 优缺点剖析 [1] 这篇文章中有具体介绍到应用 JWT 做身份认证的劣势和劣势。
上面是 RFC 7519[2] 对 JWT 做的较为正式的定义。
JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted. ——JSON Web Token (JWT)[3]
JWT 由哪些局部组成?
JWT 实质上就是一组字串,通过(.
)切分成三个为 Base64 编码的局部:
- Header : 形容 JWT 的元数据,定义了生成签名的算法以及
Token
的类型。 - Payload : 用来寄存理论须要传递的数据
- Signature(签名):服务器通过 Payload、Header 和一个密钥 (Secret) 应用 Header 外面指定的签名算法(默认是 HMAC SHA256)生成。
JWT 通常是这样的:xxxxx.yyyyy.zzzzz
。
示例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
你能够在 jwt.io 这个网站上对其 JWT 进行解码,解码之后失去的就是 Header、Payload、Signature 这三局部。
Header 和 Payload 都是 JSON 格局的数据,Signature 由 Payload、Header 和 Secret(密钥)通过特定的计算公式和加密算法失去。
Header
Header 通常由两局部组成:
typ
(Type):令牌类型,也就是 JWT。alg
(Algorithm):签名算法,比方 HS256。
示例:
{
"alg": "HS256",
"typ": "JWT"
}
JSON 模式的 Header 被转换成 Base64 编码,成为 JWT 的第一局部。
Payload
Payload 也是 JSON 格局数据,其中蕴含了 Claims(申明,蕴含 JWT 的相干信息)。
Claims 分为三种类型:
- Registered Claims(注册申明):预约义的一些申明,倡议应用,但不是强制性的。
- Public Claims(私有申明):JWT 签发方能够自定义的申明,然而为了防止抵触,应该在 IANA JSON Web Token Registry[5] 中定义它们。
- Private Claims(公有申明):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 局部默认是不加密的,肯定不要将隐衷信息寄存在 Payload 当中!!!
JSON 模式的 Payload 被转换成 Base64 编码,成为 JWT 的第二局部。
Signature
Signature 局部是对前两局部的签名,作用是避免 Token(次要是 payload)被篡改。
这个签名的生成须要用到:
- Header + Payload。
- 寄存在服务端的密钥(肯定不要泄露进来)。
- 签名算法。
签名的计算公式如下:
HMACSHA256(base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
算出签名当前,把 Header、Payload、Signature 三个局部拼成一个字符串,每个局部之间用 ” 点 ”(.
)分隔,成为 JWT 的第三局部。
如何基于 JWT 进行身份验证?
在基于 Token 进行身份验证的的应用程序中,服务器通过 Payload、Header 和 Secret
(密钥) 创立Token
(令牌)并将 Token
发送给客户端。客户端接管到 Token
之后,会将其保留在 Cookie 或者 localStorage 外面,当前客户端收回的所有申请都会携带这个令牌。
简化后的步骤如下:
- 用户向服务器发送用户名、明码以及验证码用于登陆零碎。
- 如果用户用户名、明码以及验证码校验正确的话,服务端会返回曾经签名的
Token
。 - 用户当前每次向后端发申请都在 Header 中带上这个
Token
。 - 服务端查看
Token
并从中获取用户相干信息。
两点倡议:
- 倡议将
Token
寄存在 localStorage 中,放在 Cookie 中会有 CSRF 危险。 - 申请服务端并携带 Token 的常见做法是将
Token
放在 HTTP Header 的Authorization
字段中(Authorization: Bearer Token
)。
spring-security-jwt-guide[6] 就是一个基于 JWT 来做身份认证的简略案例,感兴趣的能够看看。
JWT 如何避免 Token 被篡改?
有了签名之后,即便 Token 被泄露或者解惑,黑客也没方法同时篡改 Signature、Header、Payload。
这是为什么呢?因为服务端拿到 Token 之后,会解析出其中蕴含的 Header、Payload 以及 Signature。服务端会依据 Header、Payload、密钥再次生成一个 Signature。拿新生成的 Signature 和 Token 中的 Signature 作比照,如果一样就阐明 Header 和 Payload 没有被批改。
不过,如果服务端的秘钥也被泄露的话,黑客就能够同时篡改 Signature、Header、Payload 了。黑客间接批改了 Header 和 Payload 之后,再从新生成一个 Signature 就能够了。
密钥肯定保存好,肯定不要泄露进来。JWT 平安的外围在于签名,签名平安的外围在密钥。
如何增强 JWT 的安全性?
- 应用安全系数高的加密算法。
- 应用成熟的开源库,没必要造轮子。
- Token 寄存在 localStorage 中而不是 Cookie 中,防止 CSRF 危险。
- 肯定不要将隐衷信息寄存在 Payload 当中。
- 密钥肯定保存好,肯定不要泄露进来。JWT 平安的外围在于签名,签名平安的外围在密钥。
- Payload 要退出
exp
(JWT 的过期工夫),永恒无效的 JWT 不合理。并且,JWT 的过期工夫不易过长。 - ……