商务合作加微信:2230304070
学习与交流:PHP技术交流微信群
JetBrains Ai 使用Claude4.6 Opus,codex,gemini
https://web.52shizhan.cn/activity/ai-assistant
在现代 Web 应用中,用户认证是一个非常重要的环节。传统的 Session 认证方式存在一些问题,例如难以扩展、无法跨域等。
而 JWT(JSON Web Token)作为一种无状态的认证机制,逐渐成为主流。
JWT 的优点是轻量、自包含、易于跨域使用,但它也存在一个缺点:Token 的有效期是固定的,过期后用户需要重新登录,这会影响用户体验。
为了解决这个问题,我们可以使用双令牌(Access Token 和 Refresh Token)机制,并结合无感续约技术,实现用户无感知的 Token 自动续约。
本文将详细介绍 JWT 的原理、数据结构、认证流程,以及如何通过双令牌机制实现无感续约,并提供 PHP 代码实例。
JWT 原理
JWT 是一种基于 JSON 的开放标准(RFC 7519),用于在网络应用环境间安全地传递声明(Claims)。JWT 由三部分组成:
- Header(头部)
:描述 JWT 的元数据,例如签名算法和 Token 类型。 - Payload(负载)
:包含实际的声明(Claims),例如用户 ID、过期时间等。 - Signature(签名)
:用于验证 Token 的完整性和真实性。
JWT 的格式如下:
Header.Payload.Signature
JWT 数据结构
1. Header
Header 通常包含两部分:
alg:签名算法,例如 HMAC SHA256 或 RSA。 typ:Token 类型,通常是 JWT。
示例:
{
"alg": "HS256",
"typ": "JWT"
}
2. Payload
Payload 包含声明(Claims),声明分为三类:
- Registered Claims
:预定义的声明,例如 exp(过期时间)、sub(主题)等。 - Public Claims
:自定义的公开声明。 - Private Claims
:自定义的私有声明。
示例:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622
}
3. Signature
Signature 是对 Header 和 Payload 的签名,用于验证 Token 是否被篡改。签名算法的公式如下:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
JWT 认证流程
- 用户登录,服务器验证用户凭证。
- 服务器生成 Access Token 和 Refresh Token,返回给客户端。
- 客户端在每次请求时携带 Access Token。
- 服务器验证 Access Token,如果有效则返回请求的数据。
- 如果 Access Token 过期,客户端使用 Refresh Token 请求新的 Access Token。
- 服务器验证 Refresh Token,如果有效则生成新的 Access Token 并返回。
双令牌解决方案
1. Access Token
- 作用
:用于访问受保护的资源。 - 特点
:有效期较短(例如 15 分钟),安全性较高。
2. Refresh Token
- 作用
:用于获取新的 Access Token。 - 特点
:有效期较长(例如 7 天),存储在安全的客户端(例如 HTTP Only Cookie)。
无感续约的实现
当 Access Token 过期时,客户端使用 Refresh Token 请求新的 Access Token。如果 Refresh Token 仍然有效,服务器会返回新的 Access Token,客户端无需重新登录。
代码实例
1. 生成 JWT
use Firebase\JWT\JWT;
function generateJWT($payload, $secret, $expiry) {
$payload['iat'] = time(); // 签发时间
$payload['exp'] = time() + $expiry; // 过期时间
return JWT::encode($payload, $secret, 'HS256');
}
// 生成 Access Token
$accessToken = generateJWT(['sub' => '123'], 'your_secret_key', 900); // 15 分钟
// 生成 Refresh Token
$refreshToken = generateJWT(['sub' => '123'], 'your_refresh_secret_key', 604800); // 7 天
2. 验证 JWT
use Firebase\JWT\JWT;
useFirebase\JWT\Key;
function validateJWT($token, $secret) {
try {
$decoded = JWT::decode($token, new Key($secret, 'HS256'));
return (array) $decoded;
} catch (\Exception $e) {
returnnull; // Token 无效
}
}
// 验证 Access Token
$accessToken = 'your_access_token';
$decoded = validateJWT($accessToken, 'your_secret_key');
if ($decoded) {
echo"Token 有效: " . print_r($decoded, true);
} else {
echo"Token 无效";
}
3. 无感续约逻辑
function refreshAccessToken($refreshToken) {
$decoded = validateJWT($refreshToken, 'your_refresh_secret_key');
if ($decoded) {
// 生成新的 Access Token
$newAccessToken = generateJWT(['sub' => $decoded['sub']], 'your_secret_key', 900);
return $newAccessToken;
}
returnnull; // Refresh Token 无效
}
// 客户端请求续约
$refreshToken = 'your_refresh_token';
$newAccessToken = refreshAccessToken($refreshToken);
if ($newAccessToken) {
echo"新的 Access Token: " . $newAccessToken;
} else {
echo"Refresh Token 无效,请重新登录";
}
4. 客户端逻辑
// 假设 Access Token 存储在 localStorage 中
let accessToken = localStorage.getItem('accessToken');
asyncfunction fetchWithToken(url, options = {}) {
options.headers = options.headers || {};
options.headers['Authorization'] = `Bearer ${accessToken}`;
let response = await fetch(url, options);
if (response.status === 401) { // Token 过期
let newToken = await refreshToken();
if (newToken) {
accessToken = newToken;
localStorage.setItem('accessToken', newToken);
options.headers['Authorization'] = `Bearer ${newToken}`;
response = await fetch(url, options); // 重试请求
} else {
// 跳转到登录页
window.location.href = '/login';
}
}
return response;
}
asyncfunction refreshToken() {
let response = await fetch('/refresh-token', {
method: 'POST',
credentials: 'include'// 发送 Refresh Token(存储在 Cookie 中)
});
if (response.ok) {
let data = await response.json();
return data.accessToken;
}
returnnull;
}
总结
通过 JWT 双令牌机制,我们可以实现用户无感知的 Token 自动续约,提升用户体验。Access Token 用于短期访问,Refresh Token 用于长期续约,两者结合既保证了安全性,又避免了频繁登录的麻烦。
在实际开发中,需要注意以下几点:
- 安全性
:Refresh Token 应存储在安全的客户端(例如 HTTP Only Cookie)。 - 有效期
:合理设置 Access Token 和 Refresh Token 的有效期。 - 错误处理
:正确处理 Token 过期和无效的情况。
希望本文能帮助你理解并实现 JWT 双令牌认证和无感续约机制。如果有任何问题,欢迎在评论区讨论!



评论 (0)