返回首页

如何实现JWT认证:分步指南

本综合指南解释了如何为现代Web API实现JWT认证。它涵盖了令牌架构、使用.NET和Node.js示例的分步实现、安全最佳实践(包括刷新令牌轮换和非对称签名)以及需要避免的常见陷阱。

JWT认证实现:安全最佳实践
Advertisement 728x90

.NET

JWT 身份验证:完整实现指南

为现代 Web API 和应用程序实现安全身份验证是一项关键挑战。JSON Web 令牌(JWT)已成为无状态身份验证的行业标准解决方案,使服务器无需维护服务器端会话状态即可验证用户身份。通过本指南,你将全面了解基于 JWT 的身份验证架构,能够在自己的应用程序中安全地实现令牌签发和验证,并掌握一套可用于生产环境的令牌生命周期管理策略。你将获得在任何技术栈中安全实现 JWT 身份验证的实用知识。

你将学到什么

你将理解 JWT 的架构工作原理,明白正确实现为何对安全性至关重要,并掌握配置身份验证中间件、签发令牌以及保护 API 端点的实践知识。最终,你将能够自信地在自己的项目中实现 JWT 身份验证,并确保所做的安全决策正确无误。

Google AdInline article slot

理解 JWT 身份验证架构

在深入代码之前,理解 JWT 身份验证的工作原理至关重要。JSON Web 令牌是一种紧凑、URL 安全的表示双方之间声明的方式。令牌由三个 Base64URL 编码的部分组成,各部分之间用点分隔:头部、载荷和签名(header.payload.signature)。

JWT 身份验证流程遵循清晰的模式:客户端进行身份验证(通常通过登录端点),接收签名的令牌,然后在后续请求的 Authorization 头部中包含该令牌。服务器在每个请求上验证令牌的签名和声明,使系统成为无状态系统——无需服务器端会话存储。

JWT 的关键组成部分

组成部分 用途 示例
头部 指定签名算法和令牌类型 {"alg":"RS256","typ":"JWT"}
载荷 包含声明(用户数据、过期时间、签发者) {"sub":"user123","exp":1700000000}
签名 确保令牌完整性和真实性 使用密钥对头部+载荷进行加密哈希

这种设计使 JWT 成为分布式系统和微服务的理想选择,因为在这些场景中维护共享会话状态是不切实际的。

Google AdInline article slot

如何实现 JWT 身份验证:分步指南

以下步骤概述了一种可用于生产环境的 JWT 身份验证实现方法。虽然示例使用 .NET 和 Node.js,但这些原则适用于所有编程语言。

步骤 1:安装所需的身份验证包

现代框架提供了官方的 JWT Bearer 身份验证处理程序。对于 .NET,安装 Microsoft.AspNetCore.Authentication.JwtBearer 包。对于 Node.js,使用 jsonwebtoken

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

# Node.js
npm install jsonwebtoken bcryptjs dotenv

依赖官方且经过实战检验的库至关重要——切勿自行实现加密签名逻辑

Google AdInline article slot

步骤 2:配置身份验证服务

身份验证配置定义了 API 如何验证传入的令牌。关键的验证参数包括:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options => {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,           // 验证令牌来自已知的颁发机构
            ValidateAudience = true,         // 验证令牌是为该 API 签发的
            ValidateLifetime = true,         // 自动拒绝过期令牌
            ValidateIssuerSigningKey = true, // 验证加密签名
            ValidIssuer = "https://your-identity-provider.com",
            ValidAudience = "https://your-api.com",
            IssuerSigningKey = publicKey     // 用于签名验证的密钥
        };
    });

在生产环境中,所有四个验证标志都应设置为 true禁用其中任何一个都会造成安全漏洞IssuerSigningKey 可以从 PEM 文件加载,或者在生产环境中从身份提供者的元数据端点自动获取。

步骤 3:添加身份验证和授权中间件

中间件组件的顺序至关重要——身份验证必须在授权之前运行:

app.UseAuthentication();  // 验证 JWT 并填充 HttpContext.User
app.UseAuthorization();   // 对受保护端点强制执行 [Authorize] 策略

当请求到达 UseAuthentication 时,处理程序会检查 Authorization 头部,验证 JWT,并创建一个代表已认证用户的 ClaimsPrincipal 对象。该对象随后在整个请求管道中可用于授权决策。

步骤 4:使用 [Authorize] 属性保护端点

保护 API 端点需要使用 [Authorize] 属性:

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class SecureController : ControllerBase
{
    [HttpGet]
    public IActionResult GetSecureData()
    {
        var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        return Ok($"已认证用户:{userId}");
    }
}

没有有效令牌的请求将收到 401 Unauthorized 响应。当身份验证成功但缺乏所需权限时,API 返回 403 Forbidden。

步骤 5:实现令牌签发(仅限开发环境)

在生产环境中,令牌签发应归属于专门的 Identity Provider(IdP),实现 OAuth 2.0 或 OpenID Connect。API 的角色仅限于验证。

然而,对于开发和测试,你可能需要一个本地令牌端点:

// Node.js 示例,使用 jsonwebtoken
router.post('/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await User.findOne({ email });
  
  if (!user || !(await bcrypt.compare(password, user.password))) {
    return res.status(401).json({ message: '无效的凭据' });
  }
  
  const token = jwt.sign(
    { id: user._id, email: user.email },
    process.env.JWT_SECRET,
    { expiresIn: '15m' } // 短生命周期的访问令牌
  );
  
  res.json({ token });
});

访问令牌应具有短生命周期——通常为 15 分钟——以限制令牌被盗带来的损害。

生产环境安全最佳实践

切勿将令牌存储在 LocalStorage 中

对于 Web 应用程序,将 JWT 存储在 HttpOnly、Secure cookie 中,而不是 localStorage 或 sessionStorage。这可以防止跨站脚本攻击(XSS)访问令牌。Cookie 配置应包括:

HttpOnly: true       // 阻止 JavaScript 访问
Secure: true         // 仅通过 HTTPS 发送
SameSite: Lax 或 Strict  // CSRF 保护

使用刷新令牌轮换

由于访问令牌过期较快,请实现刷新令牌机制,以便在不要求重新认证的情况下维持用户会话:

  • 刷新令牌:生命周期较长(例如 7 天),安全存储在服务器上
  • 访问令牌:生命周期较短(例如 15 分钟),随每个请求发送
  • 轮换:每次刷新请求都会签发新的访问+刷新令牌对,并使旧的刷新令牌失效

这种方法可以减轻令牌被盗的影响,并实现干净的注销功能。

在生产环境中使用非对称签名(RS256/ES256)

在生产环境中使用非对称算法(RS256 或 ES256),而不是 HMAC(HS256):

算法 密钥类型 使用场景
HS256 对称(共享密钥) 单一服务应用程序
RS256/ES256 非对称(公钥/私钥) 多服务系统、微服务

非对称签名允许多个服务使用公钥验证令牌,而只有签发者持有私钥——遵循最小权限原则。

验证所有标准声明

OAuth 2.0 访问令牌需要以下声明,并且必须进行验证:

  • iss(签发者)—— 与预期的颁发机构匹配
  • aud(受众)—— 与你的 API 标识符匹配
  • exp(过期时间)—— 令牌未过期
  • sub(主题)—— 用户标识符
  • iat(签发时间)—— 令牌是在过去签发的
  • jti(JWT ID)—— 用于令牌跟踪的唯一标识符

使用 Redis 实现无状态身份验证

虽然 JWT 本质上是无状态的,但实现令牌撤销需要一个存储层。Redis 提供了理想的解决方案:

// 存储令牌元数据以用于撤销
func (r *Redis) SetJTI(ctx context.Context, key, userID string, exp time.Time) error {
    return r.Client.Set(ctx, key, userID, time.Until(exp)).Err()
}

// 检查令牌是否被撤销
func (r *Redis) GetUserByJTI(ctx context.Context, key string) (string, error) {
    return r.Client.Get(ctx, key).Result()
}

将令牌的唯一标识符(jti)存储在 Redis 中,并设置与令牌过期时间匹配的 TTL。这可以实现即时注销,并防止被泄露的令牌被重复使用。

应避免的常见 JWT 实现错误

  1. 在生产环境中自行签发令牌:API 应验证令牌,而不是签发令牌。使用专门的 IdP 进行签发。

  2. 弱签名密钥:使用通过 openssl rand -base64 32 生成的加密强度高的密钥(至少 32 字节)。

  3. 禁用声明验证:切勿在生产环境中将 ValidateIssuerValidateAudience 设置为 false

  4. 令牌生命周期过长:访问令牌应在 15 分钟内过期。生命周期越长,攻击面越大。

  5. 缺少撤销机制:使用 Redis 或类似的存储实现注销和令牌撤销。

常见问题解答

JWT 身份验证和 OAuth 2.0 有什么区别? JWT 是一种令牌格式,而 OAuth 2.0 是一个授权框架。OAuth 2.0 定义了如何获取令牌(包括 JWT),而 JWT 定义了令牌本身的结构。它们是互补的——OAuth 2.0 可以使用 JWT 作为访问令牌,但在较小的系统中,你也可以独立使用 JWT 进行身份验证。

如何在客户端安全地存储 JWT? 将 JWT 存储在 HttpOnly、Secure cookie 中,而不是 localStorage 或 sessionStorage。HttpOnly cookie 可阻止 JavaScript 访问,从而防止 XSS 攻击。对于移动应用程序,请使用安全的平台特定存储机制。

我可以在 JWT 过期之前撤销它吗? 可以,通过维护一个令牌标识符(jti 声明)的撤销列表或存储。Redis 是理想的选择——将 jti 与令牌过期时间匹配的 TTL 一起存储。对于每个请求,检查 jti 是否存在于撤销存储中。这可以实现即时注销和令牌失效。

我应该使用 HS256 还是 RS256 来签名 JWT? 在生产环境的多服务场景中,使用 RS256(非对称),因为令牌签发和验证发生在不同的系统上。仅在可以安全共享相同密钥的单一服务应用程序中使用 HS256。非对称签名允许验证服务仅使用公钥,遵循最小权限原则。

如何安全地实现刷新令牌? 在短生命周期的访问令牌(15 分钟)旁边签发一个长生命周期的刷新令牌(7 天)。提供一个 /refresh 端点,该端点接受刷新令牌,验证它,并返回一个新的访问+刷新令牌对。每次使用时轮换刷新令牌,以防止令牌重放攻击。安全地存储刷新令牌(例如在 Redis 中),并在注销时使其失效。

来源

  • Duende Software. "A step by step JWT authentication example." Duende Software. 2025.
  • Microsoft Learn. "Configure JWT bearer authentication in ASP.NET Core." Microsoft. 2025.
  • Duende Software. "Implementing Token Authentication in Controller-Based ASP.NET Core Web APIs." Duende Software. 2026.
  • QuickNode. "How to Implement JSON Web Tokens (JWT) Authorization." QuickNode. 2025.
  • freeCodeCamp. "How to Build a Secure Authentication System with JWT and Refresh Tokens." freeCodeCamp. 2025.
  • Vonage. "JWT Authentication in Go with Gin." Vonage Developer. 2025.

— Editorial Team

Advertisement 728x90

继续阅读