分布式部署项目中的登录问题:用JWT 轻松搞定

在 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),虽然可行,但会增加系统耦合度,违背微服务“高内聚、低耦合”的原则。

我们需要一个“自包含”的身份凭证

理想的解决方案应满足以下条件:

  1. ​无状态(Stateless):服务不需要保存用户登录状态;
  2. ​可跨域传递:能通过 HTTP Header 或 URL 参数在不同域名间传递;
  3. ​防篡改:不能被用户伪造或修改;
  4. ​可携带信息:比如用户 ID、角色、过期时间等;
  5. ​轻量高效:解析快,网络传输开销小。

这时候,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)

暂无评论