面试题答案
一键面试基于JWT实现身份验证的基本步骤
- 生成JWT:
- 用户在客户端输入用户名和密码进行登录。
- 后端接收到登录请求,验证用户名和密码。若验证成功,从数据库中获取用户相关信息(如用户ID、用户名、角色等)。
- 使用JWT库(如在Java中使用jjwt库),将用户信息作为负载(payload),选择一种加密算法(如HS256),并使用一个密钥(secret key)来生成JWT。例如在Python中使用PyJWT库:
import jwt
from datetime import datetime, timedelta
# 假设用户信息
user_info = {'user_id': 1, 'username': 'testuser', 'role': 'admin'}
# 密钥
secret_key = 'your_secret_key'
# 生成JWT
expiration = datetime.utcnow() + timedelta(minutes=30)
payload = {**user_info, 'exp': expiration}
token = jwt.encode(payload, secret_key, algorithm='HS256')
- 在请求中传递JWT:
- 后端将生成的JWT作为响应返回给客户端。
- 客户端收到JWT后,通常将其存储在本地(如localStorage、sessionStorage或cookie中,但出于安全考虑,不建议使用cookie存储JWT)。
- 后续客户端发起的每一个需要身份验证的请求,都要在请求头(Authorization header)中传递JWT,格式通常为
Bearer <JWT>
。例如在JavaScript中使用fetch发送请求:
const token = localStorage.getItem('jwt_token');
fetch('your_api_endpoint', {
headers: {
'Authorization': `Bearer ${token}`
}
})
- 验证JWT:
- 后端接收到带有JWT的请求,从请求头中提取JWT。
- 使用相同的JWT库和密钥,对JWT进行验证。验证过程包括:检查JWT的格式是否正确、签名是否有效以及JWT是否过期。若验证成功,则可以从JWT的负载中获取用户信息,用于后续的权限控制等操作。例如在Node.js中使用jsonwebtoken库:
const jwt = require('jsonwebtoken');
const secret_key = 'your_secret_key';
const token = req.headers['authorization'].split(' ')[1];
try {
const decoded = jwt.verify(token, secret_key);
// decoded包含了JWT负载中的用户信息
console.log(decoded);
} catch (err) {
// 验证失败,返回错误响应
res.status(401).send('Unauthorized');
}
JWT的结构组成
JWT由三部分组成,通过点(.
)分隔,分别是:
- Header(头部):
- 包含两部分信息:令牌的类型(即JWT)和使用的签名算法,如
{"alg": "HS256", "typ": "JWT"}
。 - 然后将这个JSON对象进行Base64Url编码,形成JWT的第一部分。
- 包含两部分信息:令牌的类型(即JWT)和使用的签名算法,如
- Payload(负载):
- 包含声明(claims),这些声明是关于实体(通常是用户)和其他数据的陈述。例如用户ID、用户名、角色、过期时间等。分为三种类型:注册声明(如iss、exp、sub等)、公共声明和私有声明。
- 同样将这个JSON对象进行Base64Url编码,形成JWT的第二部分。
- Signature(签名):
- 要创建签名部分,需要使用编码后的Header、编码后的Payload、一个密钥和Header中指定的签名算法。例如使用HS256算法,签名的计算方式为:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret_key )
。 - 签名用于验证消息在传递过程中没有被更改,并且在使用私钥签名的情况下,还可以验证JWT的发送者的身份。
- 要创建签名部分,需要使用编码后的Header、编码后的Payload、一个密钥和Header中指定的签名算法。例如使用HS256算法,签名的计算方式为: