共计 2626 个字符,预计需要花费 7 分钟才能阅读完成。
背景与痛点
在构建 Web API 时,认证是确保数据安全的第一道防线。传统的 Cookie/Session 模式虽然简单,但在分布式系统中存在明显短板:

- 扩展性问题:Session 需要服务器存储状态,当服务横向扩展时,需要额外处理 Session 共享
- CSRF 风险:基于 Cookie 的认证容易受到跨站请求伪造攻击
- 移动端适配:原生 App 处理 Cookie 往往不如浏览器方便
而 Token 认证(如 JWT)采用无状态设计,天然适合 RESTful API,特别在前后端分离架构中表现出色。
为什么选择 JWT?
对比常见认证方案:
- OAuth2:适合第三方授权,但实现复杂
- Basic Auth:每次请求携带明文凭证,安全性低
- JWT:自包含令牌、无状态、适合 API 间通信
JWT 的优势在于:
- 客户端存储,减轻服务端压力
- 包含数字签名,防篡改
- 可嵌入用户信息(Claims),减少数据库查询
核心实现
1. 安装必要包
dotnet add package Microsoft.IdentityModel.Tokens
2. 生成 Token 的完整代码
// JwtService.cs
public class JwtService
{
private readonly IConfiguration _config;
public JwtService(IConfiguration config)
{_config = config;}
public string GenerateToken(User user)
{
// 1. 准备密钥(实际项目应从安全配置读取)var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
// 2. 创建 Claims(令牌中的用户信息)var claims = new[]
{new Claim(JwtRegisteredClaimNames.Sub, user.Id),
new Claim(JwtRegisteredClaimNames.Email, user.Email),
new Claim("custom_claim", "example_value")
};
// 3. 生成签名凭证
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
// 4. 创建 Token 描述
var token = new JwtSecurityToken(issuer: _config["Jwt:Issuer"],
audience: _config["Jwt:Audience"],
claims: claims,
expires: DateTime.Now.AddMinutes(30), // 过期时间
signingCredentials: creds);
// 5. 序列化为字符串
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
3. Startup 配置(.NET 6+)
// 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"]))
};
});
安全加固
1. Token 过期与刷新
- 短期有效的 Access Token(如 30 分钟)
- 配合长期有效的 Refresh Token(7 天)
- Refresh Token 应单独存储并绑定设备
// 刷新令牌示例
public RefreshToken GenerateRefreshToken()
{
return new RefreshToken
{Token = Convert.ToBase64String(RandomNumberGenerator.GetBytes(64)),
Expires = DateTime.Now.AddDays(7),
DeviceId = "客户端唯一标识"
};
}
2. 防范重放攻击
- 使用 JWT 的
jti(JWT ID)唯一标识 - 服务端维护短期 Token 黑名单
3. 密钥管理
- 绝对不要 硬编码密钥
- 开发环境使用
dotnet user-secrets - 生产环境使用 Azure Key Vault 或 AWS KMS
避坑指南
常见错误
- 密钥泄漏:将密钥提交到 Git 仓库
- 过度 Claims:Token 体积过大会增加网络开销
- 不验证签名 :
ValidateIssuerSigningKey = false是危险操作
性能优化
- 避免在 Claims 中包含大字段(如用户头像)
- 高频访问数据建议放在 Token 中减少 DB 查询
- 使用
Policy替代角色检查减少 Token 体积
// 基于策略的授权
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdmin", policy =>
policy.RequireClaim("role", "admin"));
});
动手练习
任务:实现基于角色的访问控制(RBAC)
1. 在 Token 中添加role Claim
2. 创建 [Authorize(Policy = "RequireAdmin")] 测试接口
3. 验证不同角色用户的访问权限
完整示例代码已上传 GitHub:C#-JWT-Demo
总结
通过本文,我们完成了:
- JWT 核心组件的集成
- 安全的 Token 生成与验证流程
- 生产级的安全防护措施
实际项目中还需考虑:
- 令牌吊销(如用户修改密码时)
- 多设备管理
- 监控异常登录行为
希望这篇指南能帮你避开我当初踩过的坑。如果遇到问题,欢迎在 GitHub 讨论区提问!
正文完
