name: authentication-patterns description: 全面的身份验证实施指导,包括JWT最佳实践、OAuth 2.0/OIDC流程、Passkeys/FIDO2/WebAuthn、MFA模式和安全会话管理。在实施登录系统、基于令牌的身份验证、SSO、无密码身份验证或审查身份验证安全时使用。 allowed-tools: Read, Glob, Grep, Task
身份验证模式
实施安全身份验证系统的全面指导,涵盖JWT、OAuth 2.0、OIDC、Passkeys、MFA和会话管理。
何时使用此技能
在以下情况使用此技能:
- 实施基于JWT的身份验证
- 设置OAuth 2.0或OpenID Connect流程
- 实施无密码身份验证(Passkeys/FIDO2)
- 添加多因素身份验证(MFA/2FA)
- 设计会话管理和安全Cookie
- 实施单点登录(SSO)
- 审查身份验证安全
- 选择身份验证方法
身份验证方法选择
| 方法 | 最适用 | 安全等级 | 用户体验 |
|---|---|---|---|
| Passkeys/WebAuthn | 主要身份验证、无密码 | ★★★★★ | 优秀 |
| OAuth 2.0 + PKCE | 第三方登录、单页应用 | ★★★★☆ | 良好 |
| JWT + 刷新令牌 | API、微服务 | ★★★★☆ | 良好 |
| 会话Cookie | 传统Web应用 | ★★★☆☆ | 优秀 |
| 密码 + MFA | 遗留系统升级 | ★★★★☆ | 中等 |
推荐: 新应用首选Passkeys。SPA使用OAuth 2.0 + PKCE。始终添加MFA作为第二因素。
JWT最佳实践快速参考
算法选择
| 算法 | 使用场景 | 推荐 |
|---|---|---|
| RS256 | 公钥验证、分布式系统 | ✅ 推荐 |
| ES256 | 较小令牌、基于ECDSA | ✅ 推荐 |
| HS256 | 简单系统、同方验证 | ⚠️ 谨慎使用 |
| None | 永不使用 | ❌ 禁止 |
令牌结构
// 头部
{
"alg": "RS256",
"typ": "JWT",
"kid": "key-id-for-rotation" // 密钥ID用于密钥轮换
}
// 负载(声明)
{
"iss": "https://auth.example.com", // 签发者
"sub": "user-123", // 主体(用户ID)
"aud": "https://api.example.com", // 受众
"exp": 1735300000, // 过期时间(短期)
"iat": 1735296400, // 签发时间
"jti": "unique-token-id", // JWT ID(用于撤销)
"scope": "read write" // 权限
}
令牌生命周期
| 令牌类型 | 推荐生命周期 | 存储 |
|---|---|---|
| 访问令牌 | 5-15分钟 | 仅内存 |
| 刷新令牌 | 7-30天 | 安全HttpOnly Cookie或加密存储 |
| ID令牌 | 匹配访问令牌 | 仅内存 |
详细JWT安全: 参见 JWT安全参考
OAuth 2.0 流程选择
| 流程 | 使用场景 | 需PKCE |
|---|---|---|
| 授权码 + PKCE | SPA、移动应用、Web应用 | ✅ 是 |
| 客户端凭证 | 服务到服务 | N/A |
| 设备授权 | 智能电视、CLI工具 | N/A |
| 已弃用 - 勿用 | N/A | |
| 已弃用 - 勿用 | N/A |
授权码 + PKCE 流程
┌──────────┐ ┌───────────────┐
│ 客户端 │ │ 认证服务器 │
└────┬─────┘ └───────┬───────┘
│ │
│ 1. 生成code_verifier(随机) │
│ code_challenge = SHA256(code_verifier) │
│ │
│ 2. 授权请求 ───────────────────────────────>│
│ (response_type=code, code_challenge) │
│ │
│ 3. 用户认证和同意 │
│ │
│ 4. <────────── 授权码 ──────────────────────│
│ │
│ 5. 令牌请求 ───────────────────────────────>│
│ (code, code_verifier) │
│ │
│ 6. <────────── 访问 + 刷新令牌 ─────────────│
└────────────────────────────────────────────┘
详细OAuth流程: 参见 OAuth流程参考
Passkeys/WebAuthn 快速开始
Passkeys使用公钥密码学提供抗钓鱼、无密码身份验证。
注册流程
// 1. 从服务器获取挑战
const options = await fetch('/api/webauthn/register/options').then(r => r.json());
// 2. 创建凭证
const credential = await navigator.credentials.create({
publicKey: {
challenge: base64ToBuffer(options.challenge),
rp: { name: "示例应用", id: "example.com" },
user: {
id: base64ToBuffer(options.userId),
name: options.username,
displayName: options.displayName
},
pubKeyCredParams: [
{ type: "public-key", alg: -7 }, // ES256
{ type: "public-key", alg: -257 } // RS256
],
authenticatorSelection: {
authenticatorAttachment: "platform", // 或 "cross-platform"
residentKey: "required", // 可发现凭证
userVerification: "required" // 需要生物识别/PIN
},
timeout: 60000
}
});
// 3. 发送凭证到服务器存储
await fetch('/api/webauthn/register/verify', {
method: 'POST',
body: JSON.stringify({
id: credential.id,
rawId: bufferToBase64(credential.rawId),
response: {
clientDataJSON: bufferToBase64(credential.response.clientDataJSON),
attestationObject: bufferToBase64(credential.response.attestationObject)
}
})
});
完整Passkeys实现: 参见 Passkeys实现指南
MFA 实现模式
MFA 方法(按安全)
| 方法 | 抗钓鱼 | 安全 | 用户体验 |
|---|---|---|---|
| Passkeys/安全密钥 | ✅ 是 | ★★★★★ | 良好 |
| 认证器应用(TOTP) | ❌ 否 | ★★★★☆ | 良好 |
| 推送通知 | ⚠️ 部分 | ★★★★☆ | 优秀 |
| SMS OTP | ❌ 否 | ★★☆☆☆ | 中等 |
| 邮件 OTP | ❌ 否 | ★★☆☆☆ | 中等 |
TOTP 实现
using System.Security.Cryptography;
using OtpNet; // 安装:Otp.NET包
/// <summary>
/// TOTP(基于时间的一次性密码)服务,用于MFA。
/// </summary>
public sealed class TotpService
{
private const int SecretLength = 20; // 160位
/// <summary>
/// 为用户注册生成新的TOTP密钥。
/// </summary>
public static string GenerateSecret()
{
var secretBytes = RandomNumberGenerator.GetBytes(SecretLength);
return Base32Encoding.ToString(secretBytes);
}
/// <summary>
/// 生成认证器应用(如Google Authenticator)的配置URI。
/// </summary>
public static string GetProvisioningUri(string secret, string email, string issuer)
{
return $"otpauth://totp/{Uri.EscapeDataString(issuer)}:{Uri.EscapeDataString(email)}" +
$"?secret={secret}&issuer={Uri.EscapeDataString(issuer)}&algorithm=SHA1&digits=6&period=30";
}
/// <summary>
/// 登录时验证TOTP代码。允许1步时间漂移。
/// </summary>
public static bool VerifyTotp(string secret, string otp)
{
var secretBytes = Base32Encoding.ToBytes(secret);
var totp = new Totp(secretBytes, step: 30, totpSize: 6);
// VerificationWindow允许时钟漂移(1步 = 每方向30秒)
return totp.VerifyTotp(otp, out _, VerificationWindow.RfcSpecifiedNetworkDelay);
}
}
会话管理
安全Cookie配置
// ASP.NET Core Cookie配置
app.UseCookiePolicy(new CookiePolicyOptions
{
HttpOnly = HttpOnlyPolicy.Always, // 防止JavaScript访问(XSS防护)
Secure = CookieSecurePolicy.Always, // 仅HTTPS
MinimumSameSitePolicy = SameSiteMode.Lax // CSRF防护(或Strict更高安全)
});
// 每个Cookie配置
Response.Cookies.Append("session_id", sessionId, new CookieOptions
{
HttpOnly = true, // 防止JavaScript访问
Secure = true, // 仅HTTPS
SameSite = SameSiteMode.Lax, // CSRF防护
MaxAge = TimeSpan.FromHours(1),
Domain = ".example.com",
Path = "/",
IsEssential = true // GDPR必需Cookie
});
会话安全检查清单
- [ ] 生成加密随机会话ID(128+位)
- [ ] 认证后重新生成会话ID
- [ ] 设置HttpOnly标志在会话Cookie上
- [ ] 设置Secure标志(仅HTTPS)
- [ ] 设置SameSite属性(Lax或Strict)
- [ ] 实现会话超时(空闲和绝对)
- [ ] 注销时使会话无效(服务器端)
- [ ] 绑定会话到用户指纹(可选,考虑隐私)
密码安全(当需要时)
密码哈希
using System.Security.Cryptography;
using Konscious.Security.Cryptography; // 安装:Konscious.Security.Cryptography.Argon2
/// <summary>
/// Argon2id密码哈希服务(OWASP推荐)。
/// </summary>
public sealed class PasswordHasher
{
private const int SaltSize = 16;
private const int HashSize = 32;
private const int Iterations = 3; // 时间成本
private const int MemorySize = 65536; // 64 MB
private const int Parallelism = 4; // 线程数
/// <summary>
/// 使用Argon2id哈希密码。
/// </summary>
public static string HashPassword(string password)
{
var salt = RandomNumberGenerator.GetBytes(SaltSize);
using var argon2 = new Argon2id(System.Text.Encoding.UTF8.GetBytes(password))
{
Salt = salt,
DegreeOfParallelism = Parallelism,
MemorySize = MemorySize,
Iterations = Iterations
};
var hash = argon2.GetBytes(HashSize);
// 组合盐 + 哈希用于存储
var combined = new byte[SaltSize + HashSize];
Buffer.BlockCopy(salt, 0, combined, 0, SaltSize);
Buffer.BlockCopy(hash, 0, combined, SaltSize, HashSize);
return Convert.ToBase64String(combined);
}
/// <summary>
/// 验证密码与存储哈希。
/// </summary>
public static bool VerifyPassword(string password, string storedHash)
{
var combined = Convert.FromBase64String(storedHash);
if (combined.Length != SaltSize + HashSize) return false;
var salt = combined[..SaltSize];
var expectedHash = combined[SaltSize..];
using var argon2 = new Argon2id(System.Text.Encoding.UTF8.GetBytes(password))
{
Salt = salt,
DegreeOfParallelism = Parallelism,
MemorySize = MemorySize,
Iterations = Iterations
};
var actualHash = argon2.GetBytes(HashSize);
// 时间安全比较
return CryptographicOperations.FixedTimeEquals(actualHash, expectedHash);
}
}
密码策略
| 要求 | 推荐 |
|---|---|
| 最小长度 | 12+ 字符 |
| 最大长度 | 128+ 字符(防止DoS) |
| 复杂性 | 无任意规则(允许所有字符) |
| 泄露检查 | 检查已知泄露密码 |
| 速率限制 | 5次尝试,然后指数退避 |
| 账户锁定 | 失败后临时锁定(15-30分钟) |
快速决策树
您正在实施什么身份验证?
- 新Web/移动应用 → Passkeys + OAuth 2.0 后备
- SPA带API后端 → OAuth 2.0 + PKCE 带JWT访问令牌
- 服务到服务 → 客户端凭证流程或mTLS
- 向现有添加MFA → TOTP认证器应用(最小),Passkeys(理想)
- 传统Web应用 → 会话Cookie + CSRF令牌
- CLI/设备无浏览器 → 设备授权流程
安全检查清单
令牌安全
- [ ] 短期访问令牌(5-15分钟)
- [ ] 安全刷新令牌存储
- [ ] 令牌撤销机制
- [ ] 适当令牌验证(签名、声明、过期)
OAuth/OIDC安全
- [ ] 所有公共客户端使用PKCE
- [ ] 严格验证重定向URI
- [ ] 验证state参数
- [ ] 为OIDC验证nonce
- [ ] 使用精确重定向URI匹配
会话安全
- [ ] HttpOnly、Secure、SameSite Cookie
- [ ] 认证后会话重新生成
- [ ] 适当会话无效化
- [ ] 空闲和绝对超时
MFA安全
- [ ] 所有账户MFA(强制执行或鼓励)
- [ ] 安全恢复代码
- [ ] 速率限制MFA尝试
- [ ] 优先抗钓鱼方法
参考
- JWT安全深入 - 完整JWT实现指南
- OAuth 2.0流程 - OAuth/OIDC流程图和实现
- Passkeys实现 - WebAuthn/FIDO2完整指南
相关技能
| 技能 | 关系 |
|---|---|
authorization-models |
认证后,应用授权(RBAC、ABAC) |
cryptography |
令牌和密码的基础密码学 |
api-security |
用认证保护API端点 |
secure-coding |
通用安全模式 |
版本历史
- v1.0.0 (2025-12-26): 初始发布,含JWT、OAuth 2.0、Passkeys、MFA、会话
最后更新: 2025-12-26