共计 3258 个字符,预计需要花费 9 分钟才能阅读完成。
背景痛点
在传统的 Web API 开发中,Session 认证方式存在一些明显的局限性:

- 服务器资源占用:每个用户会话都需要在服务器端存储状态信息,当用户量增大时,内存消耗会显著增加
- 扩展性差:在分布式系统或微服务架构中,Session 需要在各服务间同步,增加了系统复杂性
- 跨域问题:难以支持跨域 API 调用,限制了前后端分离架构的应用
Token 认证(特别是 JWT)则很好地解决了这些问题:
- 无状态:服务器不需要存储会话信息,所有必要数据都包含在 Token 中
- 跨域友好:可以轻松支持跨域 API 调用
- 扩展性强:特别适合微服务架构,各服务可以独立验证 Token
技术选型
在实现 Token 认证时,我们通常面临两个主要选择:JWT 和 OAuth2.0。
- OAuth2.0:更适合第三方授权场景,如使用 Google 或 Facebook 账号登录
- JWT(JSON Web Token):更适合 API 自身的认证需求
选择 JWT 的核心原因:
- 自包含:所有必要信息都包含在 Token 中,减少数据库查询
- 标准化:有明确的 RFC 标准(RFC 7519)
- 灵活性:可以自定义 Claims 来携带各种业务信息
- 广泛支持:几乎所有语言和框架都有成熟的 JWT 库
核心实现
1. 配置 JWT 认证
首先需要在 ASP.NET Core 中配置 JWT 认证服务:
// Startup.cs 或 Program.cs 中配置
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
};
});
2. 生成 JWT Token
创建 Token 生成服务:
public class TokenService
{
private readonly IConfiguration _configuration;
public TokenService(IConfiguration configuration)
{_configuration = configuration;}
public string GenerateToken(User user)
{
// 创建 Claims(声明)var claims = new[]
{new Claim(JwtRegisteredClaimNames.Sub, user.Id),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64),
new Claim(ClaimTypes.Name, user.Username),
new Claim(ClaimTypes.Role, user.Role)
};
// 创建签名密钥
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
// 创建 Token
var token = new JwtSecurityToken(issuer: _configuration["Jwt:Issuer"],
audience: _configuration["Jwt:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddMinutes(30), // 30 分钟有效期
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
3. 保护 API 端点
使用 [Authorize] 特性保护需要认证的 API:
[ApiController]
[Route("api/[controller]")]
public class SecureController : ControllerBase
{[HttpGet]
[Authorize] // 需要有效 Token 才能访问
public IActionResult GetSecureData()
{
// 可以从 HttpContext.User 获取 Claims 信息
var userName = User.Identity.Name;
var role = User.FindFirst(ClaimTypes.Role)?.Value;
return Ok(new { Message = $"Hello {userName}, your role is {role}" });
}
}
安全实践
1. 防范中间人攻击
- 必须使用 HTTPS:JWT 在传输过程中可能被截获,必须通过 HTTPS 加密传输
- 设置合理的过期时间:通常建议 15-30 分钟,敏感操作可使用更短时间
2. 密钥管理
- 不要硬编码密钥:应从安全配置源获取(如 Azure Key Vault、AWS Secrets Manager)
- 定期轮换密钥:建议每 3 - 6 个月更换一次签名密钥
3. Token 刷新机制
实现 Token 刷新可以改善用户体验,同时不降低安全性:
public class RefreshTokenService
{public string GenerateRefreshToken()
{var randomNumber = new byte[32];
using var rng = RandomNumberGenerator.Create();
rng.GetBytes(randomNumber);
return Convert.ToBase64String(randomNumber);
}
public DateTime GetRefreshTokenExpiryTime()
{return DateTime.UtcNow.AddDays(7); // 刷新 Token 有效期通常较长
}
}
性能考量
- 加密算法选择:
- HS256:对称加密,性能好但需要安全存储密钥
-
RS256:非对称加密,更安全但性能略差
-
Claims 大小:
- 每个 Claim 都会增加 Token 体积
- 建议只包含必要信息,避免存储大量数据
避坑指南
- 时钟偏移问题:
- 不同服务器时间可能不一致,导致 Token 验证失败
-
解决方案:设置合理的 ClockSkew(默认为 5 分钟)
-
跨服务共享密钥:
- 多个服务使用相同密钥会增加风险
-
建议:每个服务使用独立密钥
-
敏感信息存储:
- 不要在 Token 中存储密码等敏感信息
- JWT 内容只是 Base64 编码,不是加密
总结与思考
通过本文,我们系统地了解了如何在 C# Web API 中实现 JWT 认证。从基础配置到安全实践,再到性能优化和常见问题规避,形成了一个完整的解决方案。
留给读者的思考题:
- 在分布式系统中,如何实现 Token 的吊销(Revoke)功能?
- 当检测到异常登录时,如何强制用户重新认证?
建议读者使用 Postman 等工具测试上述代码,实践中遇到问题可以查阅 JWT 官方文档或相关社区讨论。
正文完
