在 Web 应用开发中,越来越多的项目采用分布式架构——比如前端、后端、用户服务、订单服务等各自独立部署,甚至运行在不同的服务器或云环境中。这种架构带来了高可用性、可扩展性等优势,但也带来了一个经典难题:用户登录状态如何在多个服务之间共享?
假设你有一个电商网站,采用如下架构:
- 前端:部署在
https://web.example.com - 用户服务:
https://auth.example.com - 商品服务:
https://product.example.com - 订单服务:
https://order.example.com
这些服务各自独立,可能由不同团队维护,数据库也彼此隔离。
在传统的单体应用中,用户登录后,服务器会在 Session 中保存用户信息,并通过 Cookie 把 Session ID 发给浏览器。后续请求带上这个 Cookie,服务器就能识别用户身份。
但在分布式环境下,问题来了:
- 用户在
auth.example.com登录成功,Session 存在 auth 服务的内存里; - 当用户访问
product.example.com时,这个服务根本不知道用户是谁,因为它没有访问 auth 服务 Session 的权限(也不应该有); - 如果强行让所有服务共享同一个 Session 存储(比如 Redis),虽然可行,但会增加系统耦合度,违背微服务“高内聚、低耦合”的原则。
我们需要一个“自包含”的身份凭证
理想的解决方案应满足以下条件:
- 无状态(Stateless):服务不需要保存用户登录状态;
- 可跨域传递:能通过 HTTP Header 或 URL 参数在不同域名间传递;
- 防篡改:不能被用户伪造或修改;
- 可携带信息:比如用户 ID、角色、过期时间等;
- 轻量高效:解析快,网络传输开销小。
这时候,WT(JSON Web Token)就闪亮登场了!
JWT 是一种开放标准(RFC 7519),它把用户信息编码成一段字符串,由三部分组成:Header(头部)、Payload(载荷)、Signature(签名)。
最关键的是:只要你知道密钥,就能验证这个 Token 是否合法,而无需查询数据库或 Session。
用 PHP + JWT 实现分布式登录
下面我们用 PHP 来实现一个简单的 JWT 登录流程。
用户提交用户名和密码到 POST /login 接口:
// auth.example.com/login.php
require 'vendor/autoload.php'; // 引入 firebase/php-jwt 库
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
// 1. 验证用户名密码(这里简化,实际要查数据库+密码哈希比对)
if ($username === 'alice' && $password === '123456') {
$payload = [
'iss' => 'auth.example.com', // 签发者
'sub' => 'user_login', // 主题
'iat' => time(), // 签发时间
'exp' => time() + 3600, // 过期时间(1小时)
'uid' => 1001, // 用户ID
'role' => 'customer'
];
$secret_key = 'my_super_secret_key_2026'; // 实际项目要用强密钥并妥善保管
$jwt = JWT::encode($payload, $secret_key, 'HS256');
// 返回 Token 给前端
echo json_encode(['token' => $jwt]);
} else {
http_response_code(401);
echo json_encode(['error' => '用户名或密码错误']);
}
}
前端收到 Token 后,通常存到 localStorage 或 sessionStorage 中。
前端在请求商品列表时,在 Header 中带上 Token:
GET /products HTTP/1.1
Host: product.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
商品服务收到请求后,验证 Token:
// product.example.com/products.php
require 'vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? '';
if (preg_match('/Bearer\s+(.*)/i', $authHeader, $matches)) {
$token = $matches[1];
try {
$secret_key = 'my_super_secret_key_2026'; // 所有服务共享同一密钥
$decoded = JWT::decode($token, new Key($secret_key, 'HS256'));
// 验证通过!可以从 $decoded->uid 获取用户ID
echo json_encode([
'message' => '欢迎用户 ' . $decoded->uid,
'products' => ['iPhone', 'MacBook']
]);
} catch (Exception $e) {
http_response_code(401);
echo json_encode(['error' => 'Token 无效或已过期']);
}
} else {
http_response_code(401);
echo json_encode(['error' => '缺少 Token']);
}
✅ 关键点:所有服务都使用同一个密钥(secret_key)来验证 Token。这个密钥必须严格保密,可通过配置中心或环境变量管理。
JWT 到底是什么?
1. JWT 的结构
JWT 看起来像这样:
xxxxx.yyyyy.zzzzz
- Header:描述算法和类型,如**
{"alg":"HS256","typ":"JWT"} - Payload:存放用户数据(不要放敏感信息!),如用户ID、角色、过期时间等
- Signature:用密钥对前两部分进行签名,防止篡改
2. 为什么 JWT 适合分布式系统?
- 无状态:每个服务独立验证,无需共享 Session;
- 自包含:Token 本身携带用户信息,减少数据库查询;
- 跨语言/跨平台:几乎所有语言都有 JWT 库;
- 标准化:遵循 RFC 7519,生态成熟。
3. 注意事项
- 密钥安全:一旦泄露,攻击者可伪造任意用户 Token;
- 无法主动失效:除非引入黑名单机制(如 Redis 存已注销 Token),否则 Token 在过期前一直有效;
- 不要存敏感信息:Payload 是 Base64 编码,不是加密!任何人都能解码看到内容;
- HTTPS 必须启用:防止 Token 在传输中被窃取。
登录虽小,架构事大。选对工具,事半功倍!

评论 (0)