OAuth 2.0 认证中心落地指南:Keycloak 26 打通 SSO、角色鉴权与令牌校验

7 个业务系统、7 套用户表、7 份密码哈希,改一次密码要通知 7 个团队------这是自建登录最常见的结局。本文先讲清 OAuth 2.0 的四角色模型与授权码 + PKCE 流程(含 5 个最容易搞错的点),再用 Keycloak 26 从零搭一个认证中心:Realm/Client/Role 建模、Spring Boot 资源服务器验签 JWKS、SPA 走 PKCE 免密钥、kcadm 脚本化配置,全部给可跑的命令与代码。读完你能判断自己的系统该不该上认证中心,以及第一步怎么迈。


一、你到底需不需要认证中心?

别急着选型。认证中心不是"更高级的登录页",它引入一个必须 7×24 可用的登录单点。中两条以上再上:

信号 说明
系统数 ≥ 2,用户要求"一次登录到处能用" SSO 的真实需求,通常也是老板先提的
出现第二套用户表 / 第二份密码哈希 数据已经分裂,再拖就是迁移成本翻倍
要接外部身份(企业微信、钉钉、AD/LDAP、客户 IdP) 每个系统各接一遍 OAuth 是重复劳动
要对客户开放 API,需要给第三方发凭证 需要标准的 scope / token 生命周期管理
等保、审计要求"谁在何时访问了什么" 授权日志集中化才有意义

结论先行------认证中心的价值不在登录页,而在三件事:

  1. 把"你是谁"抽出来:一次认证,多系统消费(SSO)
  2. 把"你能干什么"抽出来 :从散落各处的 if (user.isAdmin()) 变成可审计的授权数据
  3. 把"接一个新系统"降级:从改代码变成配一个 Client

二、概念:OAuth 2.0 到底解决什么问题

先破一个最大误解

OAuth 2.0 是授权(Authorization)框架,不是认证(Authentication)协议。

它回答的是"这个应用能不能访问那份资源 ",而不是"这个人是谁 "。后者由 OpenID Connect(OIDC) 补上------OIDC = OAuth 2.0 + 一层身份信息(id_token + /userinfo)。

搞混这一点,就会写出"拿 access_token 当用户身份库用"的代码,这是后面第 6 个坑的根源。

2.1 四个角色

角色 职责 落到你的系统里是什么
Resource Owner 资源所有者,即用户 张三
Client 想访问资源的应用 前端门户、移动 App、订单服务
Authorization Server 发令牌的(认证中心本体) Keycloak
Resource Server 持有资源、校验令牌的 订单服务的 /api/**

关键:Client 不是用户,它是"被保护资源的注册记录"。很多生产事故源于把 Client 当账号管理。

2.2 一张图看懂授权码 + PKCE 流程

八步拆解:

  1. 用户点"登录" → Client 生成 code_verifier(随机串)并算出 code_challenge = BASE64URL(SHA256(verifier))
  2. Client 把用户浏览器重定向 到授权端点,带上 client_idredirect_uriscopecode_challenge
  3. 授权服务器展示登录页(密码只在这里输入,绝不给 Client
  4. 用户认证通过 → 重定向回 redirect_uri,带上一次性 code
  5. Client 用 code + code_verifier 在后端通道请求令牌端点
  6. 授权服务器用 code_challenge 校验 code_verifier,通过才发令牌
  7. Client 拿到 access_token / refresh_token / id_token
  8. Client 带 Authorization: Bearer <access_token> 访问资源服务器

PKCE 解决什么? 前端 SPA 和移动 App 无法安全保管 client_secret(代码在用户机器上,反编译就有)。PKCE 用"每次登录现生成的验证串"替代固定密钥:即使 code 被拦截,没有 verifier 也换不到令牌。

是否要 PKCE 取决于 Client 类型:public client(无法保密)必须用 ,confidential client(服务端有密钥)建议用------因为 code 本身也可能被日志、代理、Referer 泄露。

2.3 令牌三兄弟,别用错

令牌 给谁看 形态 典型有效期 常见误用
access_token 资源服务器 默认 JWT(可配不透明) 5--15 分钟 ---
refresh_token 授权服务器 不透明串 数小时--数天 当 access_token 去调业务接口
id_token Client(证明"用户是谁") JWT 与 access_token 同 ❌ 拿它去调资源服务器

最容易犯的错 :用 id_token 调后端 API。它不是为资源服务器设计的,audience 是 Client 而非你的服务,正确的做法永远是 access_token

2.4 五种流程怎么选

场景 流程(grant_type) 需要 secret PKCE 说明
前端 SPA、移动 App authorization_code 否(public) 必须 S256 唯一推荐的前端方案
服务端 Web(有会话) authorization_code 是(confidential) 建议 传统 Web 应用
服务间调用(无用户上下文) client_credentials 不适用 后端定时任务、内部调度
电视 / CLI / IoT device_code 视情况 --- 用户另行扫码确认
用户名密码直换令牌 password --- 不要用,OAuth 2.1 草案已移除该流程

同理,implicit 流程也已废弃------不要在 2026 年再写它。

2.5 OIDC 补上最后一块拼图

OIDC 要求授权服务器暴露一个发现端点

复制代码
GET https://sso.example.com/realms/acme/.well-known/openid-configuration

它返回授权端点、令牌端点、JWKS 地址(jwks_uri)、支持的算法等。这就是为什么后面的后端配置只要写一行 issuer-uri 就够了------Spring 会自己去发现并拉取公钥。


三、为什么选 Keycloak

维度 Keycloak Spring Authorization Server 商业 SaaS(Auth0 等)
协议覆盖 OIDC / OAuth 2.0 / SAML / SCIM / UMA 以 OIDC 为主
用户、组织、身份联邦 开箱即用 全部自己写 开箱即用
运维成本 中(要跑服务 + DB) 低(就是你的应用) 最低
数据主权 / 可控性 高(自托管)
二次开发 SPI 扩展点丰富 完全可控 基本没有

选型一句话:要接 AD/LDAP/钉钉/企微、要同时支持 OIDC 和 SAML、不想自己写用户中心 → Keycloak;只有 OIDC、团队 Java 底子厚、想少一个进程 → Spring Authorization Server。

Keycloak 26 现状(写于 2026-09)

  • 最新稳定版 26.7.x(26.7.0 于 2026-07 发布,26.7.2 于 2026-08 发布)
  • 26.5 引入 Workflows (管理任务自动化)与 JWT Authorization Grant(取代 external-to-internal token exchange)
  • 26.7 增强了 MCP 授权服务器 能力:Claude Code、VS Code 可作为 MCP 客户端接入,走 CIMD(OAuth Client ID Metadata Document)做动态客户端注册 + PKCE。如果你在做 Agent 平台,这条值得单独看------它把 Keycloak 变成了 MCP 生态的标准鉴权层
  • ⚠️ 两个已失效写法 (大量中文教程仍停留在此,照抄会失败):
    • KEYCLOAK_ADMIN / KEYCLOAK_ADMIN_PASSWORD → 现为 KC_BOOTSTRAP_ADMIN_USERNAME / KC_BOOTSTRAP_ADMIN_PASSWORD
    • --hostname-strict / --hostname-url / --hostname-port → 已被 hostname v2 取代,统一用 --hostname

3.1 Keycloak 核心概念模型

概念 是什么 关键点
Realm 租户 / 命名空间 用户、Client、角色都在 Realm 内隔离。master 只用来管 Keycloak 自己,绝不放业务用户
Client 被保护应用 / 服务的注册 决定用哪种流程、redirect_uri 白名单、是否需要 secret
User 用户 可用 Federation 映射到 LDAP/AD,不必在 Keycloak 里重存
Group 用户的组织结构 组可以批量挂角色,适合按部门授权
Realm Role 全局角色 落在令牌的 realm_access.roles
Client Role 某 Client 专属角色 落在令牌的 resource_access.<clientId>.roles
Client Scope 一组 claim / 角色的可选集合 决定"这个 Client 的令牌里包含什么"
Protocol Mapper 往令牌里写数据的规则 Keycloak 最该掌握的能力:把角色、属性、组映射成自定义 claim
Identity Provider 外部身份源(钉钉/企微/AD/其他 OIDC) 联邦登录,账号仍在本地映射

从这张模型能推出一个高频问题的答案:为什么后端拿不到角色? 因为 realm role 和 client role 落在两个不同的 claim 位置 ,而 Spring Security 默认只读 scope


四、落地:五步走

Step 0 · 部署(开发环境)

yaml 复制代码
# docker-compose.yml ------ 开发环境,生产别用 start-dev
services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: keycloak_dev_pwd
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U keycloak -d keycloak"]
      interval: 5s
      retries: 10

  keycloak:
    image: quay.io/keycloak/keycloak:26.7.2      # 固定补丁号,不要用 latest
    command: ["start-dev", "--import-realm"]
    environment:
      KC_BOOTSTRAP_ADMIN_USERNAME: admin          # 26.x 新变量名
      KC_BOOTSTRAP_ADMIN_PASSWORD: ChangeMe_2026
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: keycloak_dev_pwd
    ports:
      - "8080:8080"
    volumes:
      - ./realm:/opt/keycloak/data/import:ro      # 启动时导入 realm,配 --import-realm
    mem_limit: 2g                                 # ⚠️ 必设,见 6.9
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  pgdata:

起服务后访问 http://localhost:8080/admin,用 admin / ChangeMe_2026 登录。

注意 --import-realm :它让 Keycloak 启动时读取 /opt/keycloak/data/import 下的 realm JSON。这是把认证配置纳入 Git 的第一步------比控制台点完再导出靠谱得多。

Step 1 · 建模:把配置写成脚本,而不是点出来

控制台点点点的配置有三个致命问题:进不了 Git、预发环境无法复现、出事无法重建。生产环境必须做到 "Realm as Code"。

用自带的 kcadm.sh 把建模过程脚本化:

bash 复制代码
KCADM="/opt/keycloak/bin/kcadm.sh"
$KCADM config credentials --server http://localhost:8080 \
  --realm master --user admin --password ChangeMe_2026

# 1) 建业务 realm
$KCADM create realms -s realm=acme -s enabled=true -s 'displayName=ACME 统一认证'

# 2) 后端资源服务器(confidential client,走 service account)
$KCADM create clients -r acme \
  -s clientId=order-service \
  -s protocol=openid-connect \
  -s publicClient=false \
  -s serviceAccountsEnabled=true \
  -s standardFlowEnabled=false \
  -s directAccessGrantsEnabled=false

# 3) 前端 SPA(public client,强制 PKCE S256)
$KCADM create clients -r acme \
  -s clientId=web-portal \
  -s protocol=openid-connect \
  -s publicClient=true \
  -s standardFlowEnabled=true \
  -s 'redirectUris=["https://portal.example.com/*"]' \
  -s 'webOrigins=["https://portal.example.com"]' \
  -s attributes.'pkce.code.challenge.method'=S256

# 4) 角色(realm 级)
$KCADM create roles -r acme -s name=order.reader -s 'description=只读订单'

# 5) 用户 + 密码 + 授角色
$KCADM create users -r acme -s username=alice -s enabled=true -s email=alice@example.com
$KCADM set-password -r acme --username alice --new-password 'Alice@2026'
$KCADM add-roles -r acme --uusername alice --rolename order.reader

三个必须记住的配置要点:

  • redirectUris 要写到最具体 ,不要用 *。它是防开放重定向的第一道门
  • public client 必须带 attributes.'pkce.code.challenge.method'=S256
  • 禁止为了调试开 directAccessGrantsEnabled(即 password 流程),它会绕过 PKCE 与登录页,直接变成"用 Keycloak 存密码"的退化设计

Step 2 · 后端资源服务器(Spring Boot 3)

依赖:

xml 复制代码
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

配置只要一行:

yaml 复制代码
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://sso.example.com/realms/acme

issuer-uri 一行会做三件事:

  1. 请求 /.well-known/openid-configuration端点发现
  2. 从返回的 jwks_uri 拉取公钥并缓存,按 kid 自动轮换
  3. 校验令牌的 iss(签发者)必须完全等于该地址

第 3 点是生产环境 401 的头号原因------Keycloak 部署在反代后面时,如果 --hostname 配错,它签发的令牌里 iss 是内网地址,而你的服务期待公网地址,签名合法但校验失败 。排查方法:把令牌粘到 jwt.ioiss,和配置逐字符比对(含结尾斜杠与端口)。详见 6.1。

安全配置(含角色映射):

java 复制代码
@Configuration
@EnableWebSecurity
public class ResourceServerConfig {

    @Bean
    SecurityFilterChain api(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())                       // 纯 API,用 Bearer 而非 Cookie
            .sessionManagement(s -> s
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/actuator/health/**").permitAll()
                .requestMatchers(HttpMethod.GET, "/api/orders/**")
                    .hasAuthority("ROLE_order.reader")
                .anyRequest().authenticated())
            .oauth2ResourceServer(o -> o.jwt(jwt -> jwt
                .jwtAuthenticationConverter(keycloakJwtConverter())));
        return http.build();
    }

    /**
     * Spring 默认只把 scope 映射成 SCOPE_xxx 权限,
     * 读不到 Keycloak 的 realm_access / resource_access,必须自己补。
     */
    @Bean
    JwtAuthenticationConverter keycloakJwtConverter() {
        JwtGrantedAuthoritiesConverter scopes = new JwtGrantedAuthoritiesConverter();

        JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
        converter.setJwtGrantedAuthoritiesConverter(jwt -> {
            Collection<GrantedAuthority> authorities = new ArrayList<>(scopes.convert(jwt));

            // realm_access.roles -> ROLE_xxx
            if (jwt.getClaim("realm_access") instanceof Map<?, ?> ra
                    && ra.get("roles") instanceof Collection<?> roles) {
                roles.forEach(r -> authorities.add(
                    new SimpleGrantedAuthority("ROLE_" + r)));
            }

            // resource_access.<clientId>.roles -> ROLE_xxx
            if (jwt.getClaim("resource_access") instanceof Map<?, ?> resourceAccess) {
                resourceAccess.values().stream()
                    .filter(Map.class::isInstance)
                    .map(m -> ((Map<?, ?>) m).get("roles"))
                    .filter(Collection.class::isInstance)
                    .flatMap(c -> ((Collection<?>) c).stream())
                    .forEach(r -> authorities.add(
                        new SimpleGrantedAuthority("ROLE_" + r)));
            }
            return authorities;
        });
        return converter;
    }
}

Step 3 · 让令牌带上角色:两条路

方案 做法 优点 缺点
A. 后端解析(上面的代码) realm_access / resource_access 不改 Keycloak,立刻可用 每个服务都要复制这段逻辑
B. Protocol Mapper(推荐) 在 Client 上加 Mapper,把角色写成顶层 roles claim 令牌结构干净,后端零自定义代码 需要改 Keycloak 配置

方案 B 的后端代码可以简化成:

java 复制代码
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
converter.setAuthoritiesClaimName("roles");   // 对应 Mapper 写出的 claim
converter.setAuthorityPrefix("ROLE_");

JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(converter);

架构建议 :Mapper 做减法------只把当前服务需要的角色写进令牌,不要把所有角色都塞进去。否则令牌体积膨胀(HTTP 头变大、网关日志变长、每次请求多传几 KB),而且违反最小权限原则。

Step 4 · 前端 SPA:PKCE 免密钥

js 复制代码
const ISSUER = 'https://sso.example.com/realms/acme';
const CLIENT_ID = 'web-portal';
const REDIRECT_URI = 'https://portal.example.com/callback';

// base64url 编码(PKCE 要求)
const b64url = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf)))
  .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');

// 1) 生成 code_verifier 与 S256 challenge
const verifier  = b64url(crypto.getRandomValues(new Uint8Array(32)));
const challenge = b64url(await crypto.subtle.digest('SHA-256',
                    new TextEncoder().encode(verifier)));
const state     = crypto.randomUUID();                       // 防 CSRF,回调必须比对

sessionStorage.setItem('pkce_verifier', verifier);          // 只存本次登录
sessionStorage.setItem('oauth_state', state);

// 2) 跳转授权端点(public client,URL 里绝不带 secret)
location.href = `${ISSUER}/protocol/openid-connect/auth?` + new URLSearchParams({
  client_id: CLIENT_ID,
  response_type: 'code',
  scope: 'openid profile email',
  redirect_uri: REDIRECT_URI,
  code_challenge: challenge,
  code_challenge_method: 'S256',
  state,
});

// 3) 回调页:校验 state 后用 code + verifier 换令牌
const params = new URLSearchParams(location.search);
if (params.get('state') !== sessionStorage.getItem('oauth_state')) {
  throw new Error('state 不匹配,疑似 CSRF,终止');
}
const res = await fetch(`${ISSUER}/protocol/openid-connect/token`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'authorization_code',
    client_id: CLIENT_ID,
    code: params.get('code'),
    redirect_uri: REDIRECT_URI,
    code_verifier: sessionStorage.getItem('pkce_verifier'),
  }),
});

关于前端库的选型,纠正一个流传很广的说法

Keycloak 在 2022 年确实弃用了 Java 适配器和 Node.js 适配器 (Java 侧改用 Spring Security / Quarkus OIDC;Node 侧改用 openid-client),但那份官方公告明确保留 了客户端 JavaScript 适配器 keycloak-js("What is not being deprecated: OpenID Connect client-side JavaScript adapter"),Red Hat build 26.0 文档仍为它单独成章。

所以现状是:keycloak-js 可以用,但它不是唯一选择。选型建议:

需求 推荐
只想最快跑通,团队不熟 OIDC keycloak-js(内置 PKCE、静默续期、登出)
不想绑定厂商,未来可能换 IdP oidc-client-ts(OIDC 标准库)
用 React/Vue 且有服务端渲染 Auth.js / 框架原生 OIDC 支持

Step 5 · 网关统一鉴权(推荐)

在多服务环境里,把校验收到网关做一次,下游只信任网关注明的身份:

yaml 复制代码
# Spring Cloud Gateway
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://sso.example.com/realms/acme
java 复制代码
@Bean
SecurityWebFilterChain gateway(HttpSecurity http) {
    return http
        .authorizeExchange(ex -> ex
            .pathMatchers("/api/public/**").permitAll()
            .pathMatchers("/api/orders/**").hasAuthority("ROLE_order.reader")
            .anyExchange().authenticated())
        .oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults()))
        .build();
}

⚠️ 安全前提 :网关必须剥掉所有外部传入的内部身份头 (如 X-User-IdX-Roles),只注入自己解析出的值。否则攻击者直接伪造请求头即可越权------这是网关方案最常见的漏洞,比 JWT 本身的问题多得多。


五、生产化清单

要求 说明
数据库 必须切 PostgreSQL/MySQL 生产用默认的 dev-file 会被标记弃用,且无法横向扩展
主机名 显式配置 --hostname hostname v2 统一入口,决定 iss 与各类 URL 的生成;配错 = 全站 401
TLS 必须 HTTPS 令牌是 Bearer,明文 HTTP 等于把钥匙贴在门上
高可用 ≥2 节点 + 负载均衡 + 共享 DB 认证中心挂了全站登不上,按核心系统对待
内存上限 容器必须-m 镜像用 MaxRAMPercentage=70 按容器总内存算堆,不限内存会持续吃满宿主机
密钥轮换 定期轮换 realm 签名密钥并保留旧公钥 轮换期间新旧令牌并存,直接删旧密钥会让在途令牌全部失效
登出 明确"登出"是清本地还是全端 OIDC end_session_endpoint 才行,本地删 token 不算登出
审计 开启 user events / admin events 等保与事后追溯刚需,默认不全开
备份 定期备份 DB 它同时是用户库和授权数据,丢了业务登录全断
容量 按"峰值登录 QPS × 3"估 令牌签发与 JWKS 拉取都消耗 CPU;先压测再定规格

生产镜像建议(在构建阶段固化配置,避免每次启动重复优化):

dockerfile 复制代码
FROM quay.io/keycloak/keycloak:26.7.2 AS builder

ENV KC_HEALTH_ENABLED=true
ENV KC_METRICS_ENABLED=true
ENV KC_DB=postgres

WORKDIR /opt/keycloak
RUN /opt/keycloak/bin/kc.sh build          # 预构建,显著缩短启动时间

FROM quay.io/keycloak/keycloak:26.7.2
COPY --from=builder /opt/keycloak/ /opt/keycloak/

ENV KC_DB=postgres
ENV KC_DB_URL=<DBURL>
ENV KC_DB_USERNAME=<DBUSERNAME>
ENV KC_DB_PASSWORD=<DBPASSWORD>
ENV KC_HOSTNAME=sso.example.com

ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]

启动并启用健康检查:

bash 复制代码
docker run --name keycloak -p 8443:8443 -p 9000:9000 -m 2g \
  -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
  -e KC_BOOTSTRAP_ADMIN_PASSWORD=change_me \
  mykeycloak \
  start --optimized --hostname=sso.example.com
# 健康检查:https://sso.example.com:9000/health/ready | /health/live
# 指标:    /metrics

自定义 ENTRYPOINT 脚本时,最后一步必须用 exec 启动,否则 shell 占着 PID 1 会挡住 SIGTERM,容器停不干净 → 缓存不一致甚至数据丢失。这件事在滚动升级时会集中爆发。


六、十个最容易踩的坑

6.1 iss 不一致 → 全站 401 (最高频)

反代后面没配 --hostname,Keycloak 签发的令牌 iss 是内网地址,服务端期待公网地址。签名是对的,校验照样失败 。排查:jwt.ioiss,与 issuer-uri 逐字符比对(含结尾斜杠、端口、http/https)。

6.2 后端拿不到角色

realm role 在 realm_access.roles,client role 在 resource_access.<clientId>.roles,而 Spring 默认只读 scope。用 Step 2 的 Converter,或 Step 3 的 Mapper。

6.3 redirect_uri 配太宽

配成 * 等于把授权码送到任意站点。必须精确到 host + path 前缀。

6.4 public client 没开 PKCE

SPA 必须 S256。不带 PKCE 的授权码流程在公开客户端上等于没防。

6.5 把 access_token 当会话用

它只有 5--15 分钟。要么用 refresh_token 静默续期,要么在网关做会话映射。不要把有效期调到几天来"解决"这个问题

6.6 用 id_token 调业务接口

id_token 的 audience 是 Client,不是资源服务器。调后端永远用 access_token

6.7 token 存 localStorage

XSS 一旦命中就是账号接管。前端优先内存 + 静默续期,或让 BFF(后端即前端)帮你持有令牌------这才是当前主流做法。

6.8 服务器时间漂移

令牌的 exp / nbf / iat 全靠时钟。集群机器时间不同步会导致随机 401,且极难复现。上线前确认 NTP 全量同步。

6.9 容器没设内存上限

Keycloak 镜像用 -XX:MaxRAMPercentage=70 动态算堆。不设 -m,堆会涨到宿主机的 70%,然后即使不忙也几乎不还给 OS。这是"认证服务把宿主机拖垮"的典型原因。

6.10 在 master realm 里建业务用户

master 是管理域,放业务数据意味着:一旦管理员账号体系出问题,业务用户一起遭殃;而且 realm 级的隔离、主题、令牌策略全部无法按业务定制。


七、小结

认证中心这件事,概念比工具重要,工具比配置重要,配置比界面重要

  • 概念 :OAuth 2.0 管授权,OIDC 管身份;access_token 给资源服务器,id_token 给客户端
  • 工具:要接多源身份 + 同时支持 OIDC/SAML → Keycloak
  • 配置 :Realm as Code(kcadm / --import-realm),不要点出来
  • 界面 :控制台只是给人看的,任何进生产的配置都必须能从 Git 重建

判断自己该不该上的最终标准其实很简单:如果你现在还在自己写"忘记密码"的邮件模板,那大概率该上;如果你的用户在 3 个以内且不打算接第三方登录,那大概率不该上。


你的系统现在是自建登录还是接了认证中心?踩过 iss 不一致或角色取不到的坑吗?评论区聊聊,我看到都会回。

相关推荐
Dontla3 个月前
在Keycloak创建测试用户流程记录(添加属性attribute)
keycloak
曲幽3 个月前
FastAPI 身份验证总踩坑?这份 FastAPI Users “避坑指南”请收好
python·fastapi·web·jwt·oauth2·user·authentication
海市公约4 个月前
OAuth2授权码模式与密码模式的底层流转及安全边界分析
oauth2·认证授权·授权码模式·密码模式·access token·双信道安全
梵得儿SHI4 个月前
SpringCloud 进阶拓展:Spring Security OAuth2+JWT 微服务统一认证授权全实战|生产级方案 + 源码解析 + 踩坑实录
spring·spring cloud·微服务·spring security·jwt·oauth2·统一认证授权
曲幽4 个月前
你的Agent API还在裸奔?从认证到沙箱,我用FastAPI搭了几道防线
python·fastapi·web·security·jwt·oauth2·limit·sandbox·ai agent
炸裂狸花猫4 个月前
开源身份认证与访问管理平台 - Keycloak(三)公有云Console集成实践(AWS / 阿里云 / OCI)
阿里云·云原生·keycloak·aws·oci·sso
炸裂狸花猫5 个月前
开源身份认证与访问管理平台 - Keycloak(二)
docker·云原生·容器·kubernetes·开源·keycloak·sso
摇曳的精灵5 个月前
Keycloak开源企业级IAM
开源·keycloak·iam·sso
indexsunny5 个月前
互联网大厂Java求职面试实战:Spring Boot微服务在电商场景中的应用与挑战
java·spring boot·redis·面试·kafka·oauth2·microservices