1. 概述
Keycloak 是一款开源的身份与访问管理(IAM)解决方案,常作为第三方授权服务器,用于管理 Web 或移动应用的认证与授权逻辑。
本文重点讲解 如何定制 Keycloak 服务器的主题(Theme),从而让面向最终用户的登录、注册、账户管理等页面拥有符合自身产品风格的视觉体验。
我们将以独立部署的 Keycloak 服务器为背景展开说明,后续再延伸到嵌入式(Embedded)场景的应用。
本文基于之前两篇文章构建:《Spring Boot 集成 Keycloak 快速入门》 和 《将 Keycloak 嵌入 Spring Boot 应用》。如果你是初次接触,建议先阅读这两篇打基础。
2. Keycloak 中的主题机制
2.1. 默认主题
Keycloak 自带几个预置主题,打包在发行版中。
对于独立部署的服务器,这些主题位于 lib/lib/main/org.keycloak.keycloak-themes-20.0.3.jar
的 theme
目录下,可用任意 ZIP 工具打开查看:
- **
base
**:基础骨架主题,包含 HTML 模板和国际化资源包(message bundles),所有其他主题(包括自定义的)都默认继承它 - **
keycloak
**:经典主题,包含页面样式和图片资源;若未指定自定义主题,系统默认使用此主题 - **
keycloak.v2
**:基于 React 的新版管理控制台主题;旧版控制台已弃用,将在 Keycloak 21 中移除
⚠️ 强烈不建议直接修改这些内置主题。正确的做法是创建一个新主题,继承上述某个主题进行扩展。
✅ 创建自定义主题的正确姿势:在 themes
目录下新建一个文件夹,比如叫 custom
。如果想从零开始,可以复制 base
的内容;如果只是微调样式,建议从 keycloak
目录复制,省时省力。
2.2. 主题类型
Keycloak 支持五种主题类型,分别对应不同用途的页面:
- Welcome:服务首页(根路径)
- Login:登录、双因素认证(OTP)、授权确认、注册、找回密码等页面
- Account:用户账户管理页面
- Admin Console:管理员控制台
- Email:系统自动发送邮件的模板
其中,后四种可以在管理控制台中为每个 Realm 单独设置。只要在 themes
目录下新建对应文件夹,重启服务器后就能在控制台中选择。
以我们的示例为例,使用账号 initial1
/ zaq1!QAZ
登录管理后台,进入对应 Realm 的 Themes 设置页:
可以看到,主题是按 Realm 隔离的,不同 Realm 可使用不同主题。图中我们为 SpringBootKeycloak
这个 Realm 设置了 custom
主题用于账户管理。
2.3. 主题结构解析
一个完整的主题除了 HTML 模板、资源文件(图片、CSS)、国际化资源外,还包括两个关键部分:主题属性文件 和 脚本支持。
每个主题类型下都有一个 theme.properties
文件,定义了继承关系、资源引用等。例如 account
类型的配置:
parent=base
import=common/keycloak
styles=css/account.css
stylesCommon=node_modules/patternfly/dist/css/patternfly.min.css node_modules/patternfly/dist/css/patternfly-additions.min.css
解释一下:
parent=base
:继承base
主题的模板和资源import=common/keycloak
:引入公共样式资源styles
:声明本主题独有的 CSS 文件stylesCommon
:引入通用样式(如 PatternFly UI 框架)
如果需要添加 JS 脚本(比如表单验证、动态交互),可以:
- 在主题目录下创建
resources/js/
文件夹 - 放入 JS 文件(如
script1.js
) - 在
theme.properties
中声明:
scripts=js/script1.js js/script2.js
Keycloak 会在渲染页面时自动加载这些脚本。
2.4. 实战:定制账户管理页面
来点实际的!我们以 账户管理页面 为例,演示如何更换 Logo。
原始页面长这样(访问 http://localhost:8080/realms/master/account/#/personal-info
):
步骤一:创建主题目录
在 themes/custom/
下新建 account
文件夹。建议直接从 theme/keycloak/account
或 keycloak.v2/account
复制一份,避免遗漏依赖。
步骤二:替换 Logo
- 将新 Logo(如
baeldung.png
)放入themes/custom/account/resources/public/
- 修改
themes/custom/account/theme.properties
,添加:
# 页面左上角的 Logo,必须是相对路径
logo=/public/baeldung.png
刷新页面,效果立现:
开发技巧:热加载
开发时频繁重启太麻烦?用以下命令启动 Keycloak,开启热加载:
bin/kc.sh start --spi-theme-static-max-age=-1 --spi-theme-cache-themes=false --spi-theme-cache-templates=false
这样修改 CSS、HTML、图片后无需重启即可生效,效率翻倍 ✅
💡 其他类型主题(如 login、admin)的定制方式完全相同:新建对应目录,放入资源,修改
theme.properties
。
2.5. 定制欢迎页(Welcome Page)
欢迎页是用户访问 Keycloak 根路径(如 http://localhost:8080
)时看到的首页。
步骤一:创建 welcome 主题
- 在
themes/custom/
下新建welcome
目录 - 从
theme/keycloak/welcome
复制index.ftl
、theme.properties
和resources
资源
步骤二:启动时指定欢迎主题
必须通过启动参数显式指定,否则不生效:
bin/kc.sh start-dev --spi-theme-welcome-theme=custom
步骤三:修改背景图
原始页面:
操作:
- 将新背景图
geo.png
放入themes/custom/welcome/resources/
- 修改
resources/css/welcome.css
:
body {
background: #fff url(../geo.png);
background-size: cover;
}
效果:
3. 嵌入式 Keycloak 服务器的主题定制
在 嵌入式 Keycloak 场景中,Keycloak 作为 Spring Boot 应用的一部分运行,没有独立安装包。因此,所有主题资源必须放在项目源码中。
推荐路径:src/main/resources/themes
主题结构和定制方式与独立服务器完全一致,但需额外配置让 Keycloak 找到这些资源。
3.1. 在 Realm 配置中指定主题
以账户管理主题为例。独立部署时我们在控制台设置,嵌入式则需修改 Realm 配置文件(如 baeldung-realm.json
):
"accountTheme": "custom",
其他类型(如 login、email)若未指定,则使用默认主题。
3.2. 指定主题目录位置
告诉 Keycloak 去哪里加载 custom
主题,有三种方式:
方式一:启动参数(推荐)
mvn spring-boot:run -Dkeycloak.theme.dir=src/main/resources/themes
方式二:代码中设置系统属性
在 @SpringBootApplication
主类中:
public static void main(String[] args) {
System.setProperty("keycloak.theme.dir", "src/main/resources/themes");
SpringApplication.run(JWTAuthorizationServerApp.class, args);
}
方式三:修改 keycloak-server.json
在服务器配置中指定:
"theme": {
"welcomeTheme": "custom",
"folder": {
"dir": "src/main/resources/themes"
}
}
⚠️ 注意:
welcomeTheme
属性必须显式设置才能启用自定义欢迎页。
其余所有操作(改 CSS、换图片)与独立部署完全相同。
访问地址
- 欢迎页:
http://localhost:8083/auth
- 账户管理页:
http://localhost:8083/auth/realms/baeldung/account
- 登录账号:
user@example.com
/123
4. 总结
本文系统讲解了 Keycloak 主题的:
- ✅ 五种类型及其用途
- ✅ 主题继承机制与
theme.properties
配置 - ✅ 独立部署与嵌入式场景下的定制方法
- ✅ 开发期热加载技巧,避免反复重启
核心思想就一句话:不要改内置主题,新建继承主题才是正道。
所有示例代码已上传 GitHub:
- 独立服务器示例:tutorials GitHub
- 嵌入式示例:OAuth GitHub