keycloak - Keycloak 自定义主题拒绝 login.ftl
问题描述
我创建自定义 keycloak 登录屏幕。
我从keycloak网页下载
服务器 独立服务器分发
然后复制keycloak-8.0.2\themes\keycloak
到C:\xx\configuration\keycloak\theme\my-theme
那里我打开login
文件夹,我有
- theme.properties
- resources
在资源中,我改变login.css
并且一切正常,我可以改变颜色,img 等......
直到现在,在login
我没有的文件夹中login.ftl
,我从keycloak
下载的文件中取出并复制了一个。但是现在,当我尝试加载我的页面时,我得到了
This page isn’t working
localhost.test.net is currently unable to handle this request.
HTTP ERROR 500
如果我删除此文件,一切正常。
我需要这个文件来改变 html 中的一些东西
这是theme.properties
文件
parent=base
import=common/keycloak
styles=node_modules/patternfly/dist/css/patternfly.min.css node_modules/patternfly/dist/css/patternfly-additions.min.css lib/zocial/zocial.css css/login.css
meta=viewport==width=device-width,initial-scale=1
kcHtmlClass=login-pf
kcLoginClass=login-pf-page
kcLogoLink=http://www.keycloak.org
kcLogoClass=login-pf-brand
kcContainerClass=container-fluid
kcContentClass=col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 col-lg-6 col-lg-offset-3
kcContentWrapperClass=row
kcHeaderClass=login-pf-page-header
kcFeedbackAreaClass=col-md-12
kcLocaleClass=col-xs-12 col-sm-1
kcAlertIconClasserror=pficon pficon-error-circle-o
kcFormAreaClass=col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2 col-lg-8 col-lg-offset-2
kcFormCardClass=card-pf
kcFormCardAccountClass=login-pf-accounts
kcFormSocialAccountClass=login-pf-social-section
kcFormSocialAccountContentClass=col-xs-12 col-sm-6
kcFormSocialAccountListClass=login-pf-social list-unstyled login-pf-social-all
kcFormSocialAccountDoubleListClass=login-pf-social-double-col
kcFormSocialAccountListLinkClass=login-pf-social-link
kcFormHeaderClass=login-pf-header
kcFeedbackErrorIcon=pficon pficon-error-circle-o
kcFeedbackWarningIcon=pficon pficon-warning-triangle-o
kcFeedbackSuccessIcon=pficon pficon-ok
kcFeedbackInfoIcon=pficon pficon-info
kcFormClass=form-horizontal
kcFormGroupClass=form-group
kcFormGroupErrorClass=has-error
kcLabelClass=control-label
kcLabelWrapperClass=col-xs-12 col-sm-12 col-md-12 col-lg-12
kcInputClass=form-control
kcInputWrapperClass=col-xs-12 col-sm-12 col-md-12 col-lg-12
kcFormOptionsClass=col-xs-12 col-sm-12 col-md-12 col-lg-12
kcFormButtonsClass=col-xs-12 col-sm-12 col-md-12 col-lg-12
kcFormSettingClass=login-pf-settings
kcTextareaClass=form-control
kcSignUpClass=login-pf-signup
kcInfoAreaClass=col-xs-12 col-sm-4 col-md-4 col-lg-5 details
##### css classes for form buttons
# main class used for all buttons
kcButtonClass=btn
# classes defining priority of the button - primary or default (there is typically only one priority button for the form)
kcButtonPrimaryClass=btn-primary
kcButtonDefaultClass=btn-default
# classes defining size of the button
kcButtonLargeClass=btn-lg
kcButtonBlockClass=btn-block
##### css classes for input
kcInputLargeClass=input-lg
##### css classes for form accessability
kcSrOnlyClass=sr-only
这是login.ftl
<#import "template.ftl" as layout>
<@layout.registrationLayout displayInfo=social.displayInfo displayWide=(realm.password && social.providers??); section>
<#if section = "header">
${msg("doLogIn")}
<#elseif section = "form">
<div id="kc-form" <#if realm.password && social.providers??>class="${properties.kcContentWrapperClass!}"</#if>>
<div id="kc-form-wrapper" <#if realm.password && social.providers??>class="${properties.kcFormSocialAccountContentClass!} ${properties.kcFormSocialAccountClass!}"</#if>>
<#if realm.password>
<form id="kc-form-login" onsubmit="login.disabled = true; return true;" action="${url.loginAction}" method="post">
<div class="${properties.kcFormGroupClass!}">
<label for="username" class="${properties.kcLabelClass!}"><#if !realm.loginWithEmailAllowed>${msg("username")}<#elseif !realm.registrationEmailAsUsername>${msg("usernameOrEmail")}<#else>${msg("email")}</#if></label>
<#if usernameEditDisabled??>
<input tabindex="1" id="username" class="${properties.kcInputClass!}" name="username" value="${(login.username!'')}" type="text" disabled />
<#else>
<input tabindex="1" id="username" class="${properties.kcInputClass!}" name="username" value="${(login.username!'')}" type="text" autofocus autocomplete="off" />
</#if>
</div>
<div class="${properties.kcFormGroupClass!}">
<label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
<input tabindex="2" id="password" class="${properties.kcInputClass!}" name="password" type="password" autocomplete="off" />
</div>
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
<div id="kc-form-options">
<#if realm.rememberMe && !usernameEditDisabled??>
<div class="checkbox">
<label>
<#if login.rememberMe??>
<input tabindex="3" id="rememberMe" name="rememberMe" type="checkbox" checked> ${msg("rememberMe")}
<#else>
<input tabindex="3" id="rememberMe" name="rememberMe" type="checkbox"> ${msg("rememberMe")}
</#if>
</label>
</div>
</#if>
</div>
<div class="${properties.kcFormOptionsWrapperClass!}">
<#if realm.resetPasswordAllowed>
<span><a tabindex="5" href="${url.loginResetCredentialsUrl}">${msg("doForgotPassword")}</a></span>
</#if>
</div>
</div>
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
<input type="hidden" id="id-hidden-input" name="credentialId" <#if auth.selectedCredential?has_content>value="${auth.selectedCredential}"</#if>/>
<input tabindex="4" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
</div>
</form>
</#if>
</div>
<#if realm.password && social.providers??>
<div id="kc-social-providers" class="${properties.kcFormSocialAccountContentClass!} ${properties.kcFormSocialAccountClass!}">
<ul class="${properties.kcFormSocialAccountListClass!} <#if social.providers?size gt 4>${properties.kcFormSocialAccountDoubleListClass!}</#if>">
<#list social.providers as p>
<li class="${properties.kcFormSocialAccountListLinkClass!}"><a href="${p.loginUrl}" id="zocial-${p.alias}" class="zocial ${p.providerId}"> <span>${p.displayName}</span></a></li>
</#list>
</ul>
</div>
</#if>
</div>
<#elseif section = "info" >
<#if realm.password && realm.registrationAllowed && !usernameEditDisabled??>
<div id="kc-registration">
<span>${msg("noAccount")} <a tabindex="6" href="${url.registrationUrl}">${msg("doRegister")}</a></span>
</div>
</#if>
</#if>
</@layout.registrationLayout>
解决方案
Keycloak 的服务器开发人员指南建议您在创建主题时首先扩展现有主题。
所以我为我的新主题创建了一个项目目录:
mkdir serendipity-keycloak-theme
启动 Keycloak Docker 镜像:
cd serendipity-keycloak-theme
docker run -d --name keycloak \
-p 10001:8080 \
-v ~/workspace/Robinyo/serendipity-keycloak-theme:/serendipity-keycloak-theme \
-e KEYCLOAK_USER=admin \
-e KEYCLOAK_PASSWORD=secret \
jboss/keycloak:9.0.2
然后我复制了默认的 Keycloak 主题:
docker cp keycloak:/opt/jboss/keycloak/themes/keycloak ./theme
例如:
├── /serendipity-keycloak-theme
└── /theme
└── /account
└── /admin
└── /common
└── /email
└── /login
└── /resources
└── /css
└── /img
├── theme.properties
└── /welcome
Keycloak 的服务器开发人员指南还建议在创建主题时禁用缓存,以便在进行更改时预览更改。
所以我复制了 Keycloak Docker 镜像的standalone-ha.xml:
docker cp keycloak:/opt/jboss/keycloak/standalone/configuration/standalone-ha.xml .
并更新如下:
<theme>
<staticMaxAge>-1</staticMaxAge>
<cacheThemes>false</cacheThemes>
<cacheTemplates>false</cacheTemplates>
...
</theme>
Keycloak 使用以 FreeMarker 模板语言 (FTL) 编写的模板来生成 HTML。
Keycloak 的服务器开发人员指南建议,当您创建自定义模板时,将模板从基本主题复制到您自己的主题,然后应用您需要的修改。
例如:
├── /serendipity-keycloak-theme
└── /theme
└── /account
└── /admin
└── /email
└── /login
└── /messages
└── /resources
└── /css
└── /img
└── /js
├── login.ftl
├── template.ftl
├── theme.properties
└── /welcome
我从基本主题中复制了类别模板 (template.ftl) 和登录模板 (login.ftl),并将它们更新为使用 MDC Web 组件,例如:
<#-- login.ftl -->
...
<div class="${properties.kcFormGroupClass!}">
<div class="mdc-text-field mdc-text-field--with-leading-icon ${properties.kcLabelClass!} <#if usernameEditDisabled??>mdc-text-field--disabled</#if>">
<i class="material-icons mdc-text-field__icon" role="button">person</i>
<input tabindex="0" required id="username" class="mdc-text-field__input ${properties.kcInputClass!}" name="username" value="${(login.username!'')}" type="text" autofocus autocomplete="off" <#if usernameEditDisabled??>disabled</#if>>
<div class="mdc-line-ripple"></div>
<label for="username" class="mdc-floating-label ${properties.kcLabelClass!}">
<#if !realm.loginWithEmailAllowed>
${msg("username")}
<#elseif !realm.registrationEmailAsUsername>
${msg("usernameOrEmail")}
<#else>
${msg("email")}
</#if>
</label>
</div>
</div>
...
登录页面:
参考:
推荐阅读
- ace-editor - ACE 编辑器:如何通过代码折叠第 11 行的折叠?
- json - Laravel - 带有参数的 Guzzle 发布请求
- java - CodenameOne 展开完整的树
- php - 如何使用 Bootstrap 4 解决此布局,以调整行中列之间的空间
- javascript - 在错误 3 秒后反应 setTimeout
- ios - 如何在较低的表格视图中启动我的单元格?
- c - C 语言中的 while(*pointer) 合法吗?
- r - R:函数工厂的管道输入
- php - 该页面无法处理请求。在本地主机上运行 php
- javascript - Javascript 运行时是否缓存/优化嵌套对象访问?