身份认证与会话安全

深入讲解现代身份认证体系:OAuth 2.0 / OpenID Connect 协议流程、JWT 安全设计、单点登录(SSO)架构、Cookie/Session 安全与会话管理、多因素认证(MFA)实现方案。

身份认证是安全体系的第一道关卡。从传统的用户名密码到现代 OAuth 2.0 / OIDC 协议,再到多因素认证和零信任架构,身份认证技术经历了深刻的演进。本文系统讲解现代身份认证的协议原理与安全实践。


1. 认证 vs 授权

概念定义类比
认证(Authentication)验证"你是谁"出示身份证
授权(Authorization)决定"你能做什么"门禁卡权限
认证 ──► 登录成功 ──► 获得身份凭证(Token/Session)
                           │
授权 ◄── 访问资源 ◄── 校验凭证权限

2. OAuth 2.0 授权框架

2.1 为什么需要 OAuth

场景:美团 App 需要访问你的微信头像

❌ 危险方案:
用户把微信密码给美团 → 美团登录微信获取头像
问题:美团可以删好友、发朋友圈、转账...

✅ OAuth 方案:
用户同意 → 微信给美团一枚"只能读头像"的令牌
美团用令牌获取头像,但无法做其他操作

2.2 四种授权模式

模式适用场景安全等级
Authorization Code服务端 Web 应用★★★★★ 最高
PKCE移动/原生应用(Code 的扩展)★★★★★
Implicit纯前端应用(已废弃不推荐)★★
Password受信任的第一方应用(即将废弃)★★★
Client Credentials服务间调用★★★★

2.3 Authorization Code 流程

1. 用户浏览器 ──► 授权服务器
   GET /authorize?response_type=code
             &client_id=meituan_app
             &redirect_uri=https://meituan.com/callback
             &scope=profile
             &state=xyz123
             
2. 用户在微信登录、授权

3. 微信 ──► 回调美团
   HTTP/1.1 302 Found
   Location: https://meituan.com/callback?code=abc123&state=xyz123
   
4. 美团后端 ──► 微信令牌端点
   POST /token
   grant_type=authorization_code
   &code=abc123
   &redirect_uri=https://meituan.com/callback
   &client_id=meituan_app
   &client_secret=xxx
   
5. 微信返回:
   {
     "access_token": "2YotnFZFEjr1zCsicMWpAA",
     "token_type": "Bearer",
     "expires_in": 3600,
     "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA",
     "scope": "profile"
   }

2.4 PKCE(Proof Key for Code Exchange)

防止授权码拦截攻击(移动应用):

客户端生成:
  code_verifier = 随机字符串(43-128字符)
  code_challenge = BASE64URL(SHA256(code_verifier))

步骤1 授权请求:
  GET /authorize?...&code_challenge=xxx&code_challenge_method=S256

步骤4 令牌请求:
  POST /token
  &code=abc123
  &code_verifier=原始随机字符串  ← 证明是同一客户端

3. OpenID Connect(OIDC)

3.1 OAuth 2.0 + 身份层

OAuth 2.0:授权框架(Access Token 访问资源)
    +
OpenID Connect:身份认证层(ID Token 传递身份信息)
    =
现代身份认证标准

3.2 ID Token(JWT 格式)

{
  "iss": "https://auth.example.com",
  "sub": "user_12345",
  "aud": "my_client_app",
  "exp": 1704067200,
  "iat": 1704063600,
  "auth_time": 1704063000,
  "nonce": "n-0S6_WzA2Mj",
  "name": "张三",
  "email": "zhangsan@example.com",
  "picture": "https://example.com/avatar.jpg"
}

3.3 用户信息端点

GET /userinfo
Authorization: Bearer <access_token>

Response:
{
  "sub": "user_12345",
  "name": "张三",
  "email": "zhangsan@example.com",
  "email_verified": true
}

4. JWT 安全设计

4.1 JWT 结构

base64(header).base64(payload).signature

Header: {"alg":"RS256","typ":"JWT"}
Payload: {"sub":"user123","exp":1704067200,...}
Signature: HMAC_SHA256(base64url(header) + "." + base64url(payload), secret)

4.2 安全要点

要点建议错误示例
算法使用 RS256/ES256(非对称)HS256 共享密钥泄露风险
密钥256+ bit 随机密钥弱密钥 secret123
过期Access Token 15分钟永不过期
敏感信息Token 中不存敏感数据Token 中包含密码
传输HTTPS onlyHTTP 传输
存储内存或 httpOnly CookielocalStorage(XSS 风险)
验证服务端严格验证签名信任客户端传来的 Token

4.3 JWT vs Session

维度JWTSession
存储客户端(Cookie/Header)服务端(Redis/File/DB)
扩展性无状态,天然分布式需要 Session 共享
撤销困难(需黑名单/短过期)随时删除
大小较大(含签名)小(仅 Session ID)
信息量可含用户数据仅 ID,数据服务端查
适用微服务/API传统 Web

推荐混合方案:短过期 JWT + Redis 黑名单(用于撤销)


5. 会话管理安全

Set-Cookie: sessionid=xxx; 
    HttpOnly;      // JavaScript 无法读取,防 XSS
    Secure;        // 仅 HTTPS 传输
    SameSite=Lax;  // CSRF 防护
    Path=/;        // 限制路径
    Domain=.example.com;  // 限制域名
    Max-Age=3600;  // 过期时间

5.2 Session 安全策略

// Spring Session 安全配置
@Configuration
public class SessionConfig {
    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }
}

// session 固定攻击防护
http.sessionManagement(session -> session
    .sessionFixation().migrateSession()  // 登录后更换 Session ID
    .maximumSessions(1)                   // 单设备登录
    .maxSessionsPreventsLogin(false)      // 踢掉旧会话
    .expiredUrl("/login?expired")
);

// 会话超时
server.servlet.session.timeout = 30m

5.3 并发会话控制

// 实现 SessionRegistry 控制并发
@Autowired
private SessionRegistry sessionRegistry;

public void listActiveSessions(String username) {
    List<Object> principals = sessionRegistry.getAllPrincipals();
    for (Object principal : principals) {
        List<SessionInformation> sessions = 
            sessionRegistry.getAllSessions(principal, false);
        // 可强制过期指定会话
        sessions.get(0).expireNow();
    }
}

6. 单点登录(SSO)

6.1 基于 OIDC 的 SSO

┌──────────┐         ┌──────────┐         ┌──────────┐
│  App A   │         │  App B   │         │  IDP     │
│  (SP)    │         │  (SP)   │         │(Keycloak)│
└────┬─────┘         └────┬─────┘         └────┬─────┘
     │                    │                    │
登录   │──► 重定向到 IDP    │                    │
     │    携带 redirect_uri│                    │
     │────────────────────│───────────────────►│
     │                    │                    │
     │◄───────────────────│────────────────────│
     │   返回 code,App A   │                    │
     │   换取 token        │                    │
     │                    │                    │
访问 App B                │──► 重定向到 IDP    │
     │                    │    (已登录,自动)  │
     │                    │◄───────────────────│
     │                    │   直接返回 code     │

6.2 基于 SAML 2.0 的企业 SSO

SAML 流程(企业常用,如 AD/Azure AD):

1. 用户访问企业应用(SP)
2. SP 生成 SAML Request,重定向到 IdP
3. 用户在 IdP(如 Azure AD)认证
4. IdP 生成 SAML Assertion(包含用户属性和签名)
5. POST 回 SP
6. SP 验证签名,创建本地会话

7. 多因素认证(MFA)

7.1 因素类型

类型示例安全性
所知密码、PIN
所有手机、硬件密钥
所是指纹、面容

7.2 TOTP(Time-based One-Time Password)

// Google Authenticator 标准
// 基于 HOTP(RFC 4226) 扩展

// 密钥:base32 编码的共享密钥
String secret = Base32.encode(randomBytes(20));

// 生成二维码供用户扫描
String otpAuth = String.format(
    "otpauth://totp/Example:user@example.com?secret=%s&issuer=Example",
    secret
);

// 验证用户输入
long counter = System.currentTimeMillis() / 1000 / 30;  // 30 秒窗口
int expectedCode = generateTOTP(secret, counter);
boolean valid = expectedCode == userInputCode;

// 考虑时间漂移,通常校验 counter-1, counter, counter+1

7.3 FIDO2 / WebAuthn(passkeys)

// 注册安全密钥
const credential = await navigator.credentials.create({
    publicKey: {
        challenge: Uint8Array.from(serverChallenge, c => c.charCodeAt(0)),
        rp: { name: "Example Corp", id: "example.com" },
        user: { id: userId, name: "user@example.com", displayName: "User" },
        pubKeyCredParams: [{ alg: -7, type: "public-key" }], // ES256
        authenticatorSelection: { authenticatorAttachment: "platform" }
    }
});

// 支持生物识别(指纹/面容)、硬件密钥(YubiKey)、移动设备
// 无密码登录,抗钓鱼

8. 密码管理

8.1 密码存储

// ✅ 现代推荐:Argon2id
import de.mkammerer.argon2.Argon2;
import de.mkammerer.argon2.Argon2Factory;

Argon2 argon2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id);
String hash = argon2.hash(3, 65536, 1, password.toCharArray());

boolean valid = argon2.verify(hash, password.toCharArray());

// 参数:iterations=3, memory=64MB, parallelism=1

// ✅ 备选:BCrypt(Java 内置)
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12);
String hash = encoder.encode(password);

8.2 密码策略

# 现代密码策略(NIST 800-63B 建议)
password-policy:
  min-length: 8              # 最小长度 8
  max-length: 64             # 最大长度 64(防止 DoS)
  blocklist: true            # 检查常见密码字典
  complexity: false          # 不要求大小写+数字+符号(增加用户痛苦,不增加安全)
  rotation: false            # 不强制定期更换(除非泄露)
  mfa-required: true         # 敏感操作要求 MFA
  breach-check: true         # 检查 Have I Been Pwned

9. 总结

现代身份认证体系的分层设计:

┌─────────────────────────────────────────────────────────┐
│                    认证方式选择                          │
├─────────────────────────────────────────────────────────┤
│  B2C 应用    │ OAuth 2.0 + OIDC                        │
│  B2B 应用    │ SAML 2.0                                │
│  内部系统    │ LDAP/AD + Kerberos                      │
│  微服务      │ mTLS + JWT                              │
├─────────────────────────────────────────────────────────┤
│                    安全等级                              │
├─────────────────────────────────────────────────────────┤
│  基础        │ 密码 + Session/Cookie                   │
│  增强        │ 密码 + TOTP(手机验证码)                 │
│  高安全      │ 密码 + FIDO2/WebAuthn(无密码)           │
│  零信任      │ 持续认证 + 上下文感知 + 动态授权            │
└─────────────────────────────────────────────────────────┘

身份认证领域正从"密码为中心"向"无密码(passwordless)“演进,而 OAuth 2.0 / OIDC 已经成为互联网身份互联的事实标准。正确实现这些协议,是构建安全、可扩展认证系统的基础。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「安全」更多文章

  1. Kubernetes安全体系:RBAC、PodSecurity与NetworkPolicy实战
  2. 安全合规与数据保护
  3. 渗透测试与红蓝对抗