mcpField
K
Keycloak

Keycloak

github.com/oriolrius安全2493 次安装5 天前更新
代码仓库

关于 Keycloak

Keycloak 是面向应用和服务的开源身份与访问管理平台,适用于需要管理认证与授权的团队。它集成了单点登录、身份联邦、标准协议支持和用户管理。

主要功能

  1. 01单点登录与社交登录
  2. 02LDAP 和 Active Directory 用户联邦
  3. 03支持 OAuth 2.0、OpenID Connect 和 SAML
  4. 04细粒度授权与角色管理
  5. 05管理控制台和用户管理
  6. 06自定义认证流程与 OTP

适用场景

  1. 01为应用提供单点登录
  2. 02接入 LDAP 或社交身份提供商的用户
  3. 03配置应用角色和令牌声明
  4. 04在浏览器认证流程中加入 OTP

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/oriolrius/pki-manager-web --skill keycloak

安装后在 Claude Code 中输入/keycloak确认已加载。

详细介绍

Keycloak

提供有关 Keycloak 身份与访问管理平台的专家协助。

概述

Keycloak 是一个开源的身份与访问管理(IAM)解决方案,提供:

  • 单点登录(SSO)
  • 身份代理和社交登录
  • 用户联合(LDAP/Active Directory)
  • 标准协议(OAuth 2.0、OpenID Connect、SAML 2.0)
  • 精细化授权
  • 管理控制台和账户管理

安装与设置

Docker(快速开始)
# Run Keycloak
docker run -d \
  --name keycloak \
  -p 8080:8080 \
  -e KEYCLOAK_ADMIN=admin \
  -e KEYCLOAK_ADMIN_PASSWORD=admin \
  quay.io/keycloak/keycloak:latest start-dev

# With PostgreSQL
docker run -d \
  --name keycloak \
  -p 8080:8080 \
  -e KC_DB=postgres \
  -e KC_DB_URL=jdbc:postgresql://localhost/keycloak \
  -e KC_DB_USERNAME=keycloak \
  -e KC_DB_PASSWORD=password \
  -e KEYCLOAK_ADMIN=admin \
  -e KEYCLOAK_ADMIN_PASSWORD=admin \
  quay.io/keycloak/keycloak:latest start
Docker Compose
version: '3'

services:
  postgres:
    image: postgres:15
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: password
    volumes:
      - postgres_data:/var/lib/postgresql/data

  keycloak:
    image: quay.io/keycloak/keycloak:latest
    command: start
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://postgres/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: password
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin
      KC_HOSTNAME: localhost
      KC_HOSTNAME_PORT: 8080
      KC_HOSTNAME_STRICT_HTTPS: false
      KC_HTTP_ENABLED: true
    ports:
      - "8080:8080"
    depends_on:
      - postgres

volumes:
  postgres_data:
生产环境设置
# Build with PostgreSQL support
docker run \
  -e KC_DB=postgres \
  -e KC_FEATURES=token-exchange,admin-fine-grained-authz \
  -e KC_HTTP_ENABLED=true \
  -e KC_HOSTNAME_STRICT_HTTPS=false \
  quay.io/keycloak/keycloak:latest build

# Run in production mode
docker run \
  -p 8443:8443 \
  -e KC_DB=postgres \
  -e KC_DB_URL=jdbc:postgresql://postgres/keycloak \
  -e KC_DB_USERNAME=keycloak \
  -e KC_DB_PASSWORD=password \
  -e KEYCLOAK_ADMIN=admin \
  -e KEYCLOAK_ADMIN_PASSWORD=admin \
  -e KC_HOSTNAME=auth.example.com \
  -e KC_HTTPS_CERTIFICATE_FILE=/opt/keycloak/conf/server.crt \
  -e KC_HTTPS_CERTIFICATE_KEY_FILE=/opt/keycloak/conf/server.key \
  quay.io/keycloak/keycloak:latest start

Realm 配置

创建 Realm
  1. 登录管理控制台:http://localhost:8080/admin
  2. 点击“Create Realm”(左上角下拉菜单)
  3. 输入 Realm 名称(例如,“myapp”)
  4. 点击“Create”
Realm 设置
Realm Settings:
- General
  - Display name: My Application
  - HTML Display name: <b>My App</b>
  - Frontend URL: https://auth.example.com (optional)

- Login
  - User registration: Enable to allow self-registration
  - Edit username: Allow users to edit username
  - Forgot password: Enable password reset
  - Remember me: Allow remember me checkbox
  - Login with email: Allow email as username

- Keys
  - Active keys for signing tokens
  - Configure providers (RSA, ECDSA, HMAC)

- Email
  - From: [email protected]
  - Host: smtp.example.com
  - Port: 587
  - Enable authentication
  - Username/Password for SMTP

- Themes
  - Login theme: keycloak (or custom)
  - Account theme: keycloak
  - Admin console theme: keycloak
  - Email theme: keycloak

- Tokens
  - Access Token Lifespan: 5 minutes
  - Refresh Token Max Reuse: 0
  - SSO Session Idle: 30 minutes
  - SSO Session Max: 10 hours

客户端配置

创建客户端

OpenID Connect 客户端:

Client ID: my-app
Client Protocol: openid-connect
Root URL: http://localhost:3000

Settings:
- Client authentication: ON (for confidential clients)
- Authorization: OFF (unless using fine-grained authz)
- Valid redirect URIs:
  - http://localhost:3000/*
  - http://localhost:3000/api/auth/callback/keycloak
- Valid post logout redirect URIs:
  - http://localhost:3000
- Web origins: http://localhost:3000

Capability config:
- Client authentication: ON
- Authorization: OFF
- Standard flow: ON (Authorization Code Flow)
- Direct access grants: ON (Resource Owner Password Credentials)
- Implicit flow: OFF (deprecated)
- Service accounts roles: ON (for client credentials)
客户端凭据

创建客户端并启用身份验证后:

  1. 前往“Credentials”选项卡
  2. 复制“Client secret”
  3. 在应用配置中使用
客户端范围
Create custom scope:
1. Clients > Client scopes > Create
2. Name: custom-scope
3. Protocol: openid-connect
4. Display on consent: OFF
5. Include in token scope: ON

Add mappers:
1. Mappers tab > Create
2. Mapper type: User Property
3. Property: email
4. Token Claim Name: email
5. Claim JSON Type: String

Assign to client:
1. Clients > [your-client] > Client scopes
2. Add available scope to Assigned default scopes

用户管理

创建用户
Admin Console > Users > Create user

Username: john.doe
Email: [email protected]
Email verified: ON
First name: John
Last name: Doe
Enabled: ON

Credentials:
- Set password
- Temporary: OFF (user won't be forced to change)
用户属性
Users > [user] > Attributes

Key: department
Value: engineering

Key: employee_id
Value: EMP-12345
用户角色
1. Create roles:
   Realm roles > Create role
   - Name: admin
   - Name: user
   - Name: viewer

2. Assign to user:
   Users > [user] > Role mapping
   - Assign role: admin
用户组
1. Create group:
   Groups > Create group
   - Name: Developers

2. Add attributes to group:
   Groups > Developers > Attributes
   - team: backend

3. Assign roles to group:
   Groups > Developers > Role mapping
   - Assign: developer role

4. Add users to group:
   Users > [user] > Groups
   - Join: Developers

角色与权限

Realm 角色
Realm roles > Create role

Name: super-admin
Description: Full system access

Composite roles:
- Add child roles (admin, user, viewer)
客户端角色
Clients > [client] > Roles > Create role

Name: app-admin
Description: Application administrator

Use case: Application-specific roles
角色映射器
Client scopes > roles > Mappers > realm roles

Add to token:
- Token Claim Name: realm_access.roles
- Claim JSON Type: String
- Add to ID token: ON
- Add to access token: ON
- Add to userinfo: ON

身份验证流程

浏览器流程(默认)
Authentication > Flows > Browser

Steps:
1. Cookie (SSO check)
2. Kerberos (optional)
3. Forms (username/password)
   - Username password form
   - OTP form (if enabled)
自定义身份验证流程
1. Duplicate existing flow:
   Flows > Browser > Duplicate

2. Customize:
   - Add execution
   - Set requirement (REQUIRED, ALTERNATIVE, DISABLED)

3. Bind to client:
   Clients > [client] > Advanced > Authentication flow overrides
   - Browser flow: [custom-flow]
双重身份验证
1. Enable OTP:
   Authentication > Flows > Browser
   - Add execution: OTP Form
   - Requirement: CONDITIONAL

2. Configure OTP:
   Authentication > OTP Policy
   - Type: Time-based or Counter-based
   - Algorithm: SHA1, SHA256, SHA512
   - Digits: 6
   - Period: 30 seconds

3. Users enable OTP:
   Account console > Account security > Signing in
   - Set up Authenticator Application

用户联合

LDAP 集成
User Federation > Add provider > LDAP

Connection:
- Console display name: LDAP
- Edit mode: READ_ONLY or WRITEABLE
- Sync registrations: ON
- Vendor: Active Directory, Red Hat Directory Server, etc.
- Connection URL: ldap://ldap.example.com:389
- Users DN: ou=users,dc=example,dc=com
- Bind DN: cn=admin,dc=example,dc=com
- Bind credential: password

LDAP searching and updating:
- Custom user search filter: (objectClass=person)
- Search scope: Subtree

Synchronization:
- Batch size: 1000
- Full sync period: 604800 (weekly)
- Changed users sync period: 86400 (daily)

Test connection and authentication
自定义用户存储 SPI
public class CustomUserStorageProvider implements UserStorageProvider {
    @Override
    public UserModel getUserById(String id, RealmModel realm) {
        // Fetch user from custom storage
    }

    @Override
    public UserModel getUserByUsername(String username, RealmModel realm) {
        // Lookup by username
    }

    @Override
    public UserModel getUserByEmail(String email, RealmModel realm) {
        // Lookup by email
    }
}

身份提供商

社交登录(Google)
Identity Providers > Add provider > Google

Settings:
- Client ID: [from Google Console]
- Client secret: [from Google Console]
- Default scopes: openid profile email
- Store tokens: ON
- Stored tokens readable: ON

Mappers:
- Create mapper: Import from provider
- Sync mode: Import or Force
SAML 提供商
Identity Providers > Add provider > SAML

Settings:
- Service provider entity ID: my-app
- Single sign-on service URL: [from SAML provider]
- Name ID policy format: Email
- Principal type: Subject NameID
- Want AuthnRequests signed: ON

Import from URL or file for metadata

令牌配置

访问令牌
Clients > [client] > Settings > Advanced

Access Token Lifespan: 5 minutes
Client Session Idle: 30 minutes
Client Session Max: 10 hours

Include in token:
- Standard claims (sub, aud, iss, exp, iat)
- Custom claims via mappers
刷新令牌
Realm Settings > Tokens

Refresh Token Max Reuse: 0
Revoke Refresh Token: ON
SSO Session Idle: 30 minutes
SSO Session Max: 10 hours
Offline Session Idle: 30 days
自定义声明
Client scopes > [scope] > Mappers > Create

Mapper type: User Attribute
User attribute: department
Token claim name: department
Claim JSON Type: String
Add to ID token: ON
Add to access token: ON
Add to userinfo: ON

管理 API

获取管理令牌
# Password grant
curl -X POST http://localhost:8080/realms/master/protocol/openid-connect/token \
  -d "client_id=admin-cli" \
  -d "username=admin" \
  -d "password=admin" \
  -d "grant_type=password"
API 示例
# Get realm
curl -X GET http://localhost:8080/admin/realms/myapp \
  -H "Authorization: Bearer $TOKEN"

# Create user
curl -X POST http://localhost:8080/admin/realms/myapp/users \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "john",
    "email": "[email protected]",
    "enabled": true,
    "firstName": "John",
    "lastName": "Doe"
  }'

# Get users
curl -X GET http://localhost:8080/admin/realms/myapp/users \
  -H "Authorization: Bearer $TOKEN"

# Assign role
curl -X POST http://localhost:8080/admin/realms/myapp/users/{userId}/role-mappings/realm \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '[{"id": "{roleId}", "name": "admin"}]'

应用集成

使用 NextAuth 的 Next.js
// pages/api/auth/[...nextauth].ts
import NextAuth from "next-auth"
import KeycloakProvider from "next-auth/providers/keycloak"

export default NextAuth({
  providers: [
    KeycloakProvider({
      clientId: process.env.KEYCLOAK_CLIENT_ID!,
      clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!,
      issuer: process.env.KEYCLOAK_ISSUER, // http://localhost:8080/realms/myapp
    }),
  ],
  callbacks: {
    async jwt({ token, account }) {
      if (account) {
        token.accessToken = account.access_token
        token.refreshToken = account.refresh_token
      }
      return token
    },
    async session({ session, token }) {
      session.accessToken = token.accessToken
      return session
    },
  },
})

// .env.local
KEYCLOAK_CLIENT_ID=my-app
KEYCLOAK_CLIENT_SECRET=secret
KEYCLOAK_ISSUER=http://localhost:8080/realms/myapp
使用 keycloak-connect 的 Node.js
const session = require('express-session')
const Keycloak = require('keycloak-connect')

const memoryStore = new session.MemoryStore()
const keycloak = new Keycloak({ store: memoryStore }, {
  'realm': 'myapp',
  'auth-server-url': 'http://localhost:8080',
  'ssl-required': 'external',
  'resource': 'my-app',
  'credentials': {
    'secret': 'client-secret'
  }
})

app.use(session({
  secret: 'session-secret',
  resave: false,
  saveUninitialized: true,
  store: memoryStore
}))

app.use(keycloak.middleware())

// Protected route
app.get('/protected', keycloak.protect(), (req, res) => {
  res.json({ message: 'Protected resource' })
})

// Role-based protection
app.get('/admin', keycloak.protect('admin'), (req, res) => {
  res.json({ message: 'Admin resource' })
})
React SPA
import Keycloak from 'keycloak-js'

const keycloak = new Keycloak({
  url: 'http://localhost:8080',
  realm: 'myapp',
  clientId: 'my-app',
})

// Initialize
keycloak.init({
  onLoad: 'login-required',
  checkLoginIframe: false,
}).then((authenticated) => {
  if (authenticated) {
    console.log('User is authenticated')
    console.log('Token:', keycloak.token)
    console.log('Roles:', keycloak.realmAccess?.roles)
  }
})

// Auto-refresh token
keycloak.onTokenExpired = () => {
  keycloak.updateToken(30)
}

// API call with token
fetch('/api/data', {
  headers: {
    'Authorization': `Bearer ${keycloak.token}`
  }
})

// Logout
keycloak.logout({ redirectUri: 'http://localhost:3000' })

// Check role
if (keycloak.hasRealmRole('admin')) {
  // Show admin features
}

安全最佳实践

  1. 在生产环境中使用 HTTPS - 始终启用 SSL/TLS
  2. 使用强客户端密钥 - 使用经过加密学随机生成的密钥
  3. 限制令牌有效期 - 使用短期访问令牌(5-15 分钟)
  4. 刷新令牌轮换 - 启用刷新令牌重复使用检测
  5. 为 SPA 使用 PKCE - 使用代码交换证明密钥
  6. 内容安全策略 - 正确设置 CSP 标头
  7. 速率限制 - 防范暴力破解
  8. 定期更新 - 保持 Keycloak 为最新版本
  9. 审计日志记录 - 启用并监控事件日志
  10. 角色层级结构 - 使用组合角色处理复杂场景

故障排除

令牌验证问题
# Decode JWT token
echo $TOKEN | cut -d. -f2 | base64 -d | jq

# Verify token signature
curl http://localhost:8080/realms/myapp/protocol/openid-connect/certs
连接问题
# Check Keycloak health
curl http://localhost:8080/health

# Check realm endpoints
curl http://localhost:8080/realms/myapp/.well-known/openid-configuration
用户登录问题
  1. 检查用户是否已启用
  2. 验证电子邮件是否已验证(如果需要)
  3. 检查必需操作(重置密码、验证电子邮件)
  4. 查看身份验证日志(Events > Login Events)
CORS 问题
Clients > [client] > Settings
- Web origins: http://localhost:3000
- Valid redirect URIs: http://localhost:3000/*

常见任务

导出/导入 Realm
# Export realm
docker exec keycloak /opt/keycloak/bin/kc.sh export \
  --dir /tmp/export \
  --realm myapp

# Import realm
docker exec keycloak /opt/keycloak/bin/kc.sh import \
  --file /tmp/export/myapp-realm.json
备份数据库
# PostgreSQL backup
docker exec postgres pg_dump -U keycloak keycloak > keycloak-backup.sql

# Restore
docker exec -i postgres psql -U keycloak keycloak < keycloak-backup.sql
主题自定义
themes/
└── custom-theme/
    ├── login/
    │   ├── theme.properties
    │   ├── login.ftl
    │   └── resources/
    │       ├── css/
    │       └── img/
    └── account/
        └── ...

Realm Settings > Themes > Login theme: custom-theme

资源

评论

支持 Markdown