18.1 集成模式概览

将 IDaaS 集成到应用架构中有多种模式,选择正确的模式取决于应用类型、安全要求和技术栈:

模式适用场景复杂度安全性
网关模式传统 Web 应用、API
BFF 模式SPA、移动端
Sidecar 模式微服务/服务网格
SDK/库模式任何应用取决于实现
直接集成自定义应用取决于实现

18.2 网关模式

API 网关认证

在 API 网关层统一处理认证,后端服务不感知身份:

客户端 → [API 网关] → [后端服务]
              │
              │ Token 验证
              ▼
        [IDaaS Server]

优点

  • 后端服务零改动
  • 集中管理认证策略
  • 网关层统一的限流、日志、监控

缺点

  • 网关层成为性能瓶颈
  • 细粒度授权在网关层实现困难
  • 后端服务不知道"谁"在访问

Nginx + OAuth2 代理

使用 oauth2-proxy 为任何 HTTP 应用添加 OAuth 2.0 保护。完整配置指南、生产排错与回滚方案见 Keycloak + oauth2-proxy 集成实战指南,本节给出快速参考。

浏览器 → Nginx → oauth2-proxy → 应用
                │
                └── 未认证 → 重定向到 IDaaS
# docker-compose.yaml
services:
  oauth2-proxy:
    image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.3
    command:
      - --provider=oidc
      - --oidc-issuer-url=https://idp.example.com/realms/myrealm
      - --client-id=my-app
      - --client-secret=xxx
      - --cookie-secret=generate-a-random-secret
      - --upstream=http://my-app:8080
      - --http-address=0.0.0.0:4180
    ports:
      - "4180:4180"

18.3 BFF 模式(Backend For Frontend)

BFF 模式是 SPA 和移动应用的推荐模式:

┌────────┐     ┌─────────────┐     ┌──────────────┐
│  SPA   │────→│  BFF Server │────→│  后端微服务   │
│ (浏览器)│     │ (机密客户端) │     │              │
└────────┘     └──────┬──────┘     └──────────────┘
  Token   ←→          │ ←→ Access Token
  in                  │ Token 交换
  HttpOnly            │ Session 管理
  Cookie              ▼
                ┌──────────┐
                │  IDaaS   │
                └──────────┘

为什么 BFF 模式更安全

SPA 不能安全存储 Token(localStorage 易受 XSS 攻击,浏览器无法保护)。BFF 将 Token 存储在服务端,使用 HttpOnly、Secure、SameSite Cookie 与前端通信。

BFF 的实现

// BFF 服务示例 (Node.js + Express)
app.get('/api/proxy/*', async (req, res) => {
  const accessToken = req.session.accessToken;
  
  if (!accessToken || isExpired(accessToken)) {
    const newToken = await refreshAccessToken(req.session.refreshToken);
    req.session.accessToken = newToken.access_token;
    req.session.refreshToken = newToken.refresh_token;
  }
  
  const backendPath = req.path.replace('/api/proxy', '');
  const response = await fetch(`https://backend-api${backendPath}`, {
    headers: { Authorization: `Bearer ${req.session.accessToken}` }
  });
  
  res.json(await response.json());
});

app.get('/callback', async (req, res) => {
  const { code } = req.query;
  const tokens = await exchangeCode(code, codeVerifier);
  req.session.accessToken = tokens.access_token;
  req.session.refreshToken = tokens.refresh_token;
  res.redirect('/');
});

18.4 Sidecar 模式(服务网格)

在服务网格(如 Istio)中处理认证:

[Pod]
 ┌──────────────────┐
 │  Application     │──→ 业务逻辑(不关心认证)
 │  Container       │
 ├──────────────────┤
 │  Sidecar Proxy   │──→ 拦截所有流量,处理认证
 │  (Envoy)         │
 └──────────────────┘
       │
       ▼
  [Istio Control Plane]
  ├── RequestAuthentication(从 JWT 提取身份)
  └── AuthorizationPolicy(基于身份的授权)
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
  name: jwt-auth
spec:
  selector:
    matchLabels:
      app: my-service
  jwtRules:
  - issuer: https://idp.example.com/realms/myrealm
    jwksUri: https://idp.example.com/realms/myrealm/protocol/openid-connect/certs
    forwardOriginalToken: true
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: require-jwt
spec:
  selector:
    matchLabels:
      app: my-service
  action: ALLOW
  rules:
  - from:
    - source:
        requestPrincipals: ["*"]
    to:
    - operation:
        methods: ["GET", "POST"]

18.5 SDK 模式

直接使用各生态的 OIDC/OAuth 2.0 库集成。

自 Keycloak 24+ 起,官方已弃用各语言的专用 Keycloak Adapter,推荐改用框架自带的标准 OIDC 客户端。已有 Adapter 项目的迁移步骤参见 Keycloak Adapter 弃用迁移指南

Spring Security OIDC 集成

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()
                .requestMatchers("/admin/**").hasRole("admin")
                .anyRequest().authenticated()
            )
            .oauth2Login()
            .logout()
                .logoutSuccessUrl("/");
        return http.build();
    }
}

通用 OIDC 库

  • Java:Spring Security OAuth 2.0 Client、Nimbus JOSE + JWT
  • Gocoreos/go-oidczitadel/oidcory/fosite
  • Pythonflask-oidcdjango-oidc-provider
  • Node.jsopenid-client
  • Rustopenidconnect-rs

18.6 协议选择指南

应用类型推荐协议原因
现代 Web 应用OIDC + PKCE标准化、安全、SPA 友好
SPA(无 BFF)OIDC + PKCE安全处理 Token
移动 AppOIDC 授权码 + PKCE,经系统浏览器/Custom Tab安全浏览器(RFC 8252)
服务间 APIOAuth 2.0 Client CredentialsM2M 认证
遗留企业应用SAML 2.0兼容性
物联网设备OAuth 2.0 Device Flow输入受限

18.7 常见集成陷阱

  1. 在 SPA 中将 Token 存在 localStorage:易受 XSS,应使用 BFF 模式。
  2. 不验证 Token 的 audience:一个应用的 Token 被另一个应用接受。
  3. 使用隐式模式(Implicit Flow):已不安全,迁移到 PKCE。
  4. 硬编码 OIDC 配置:应使用发现文档动态获取。
  5. 忽略 Token 刷新:Access Token 过期后不处理,用户突然退出。
  6. 每个应用自己实现认证:违反了 SSO 的初衷。

18.8 小结

集成 IDaaS 的核心不在于"对接 API",而在于选择正确的架构模式。SPA 应用首选 BFF 模式,微服务环境首选 Sidecar/网格模式,传统 Web 应用首选网关模式或 SDK 模式。无论选择哪种模式,确保 Token 安全存储、使用 PKCE、避免业界已知的反模式。