CAS单点登录-自定义主题、界面 (十一)

来源:互联网 发布:js match对象 编辑:程序博客网 时间:2024/06/05 17:20

CAS单点登录-自定义主题、界面 (十一)

在集成了sso之后,cas的登录界面一般都不满足上线要求,所以必须来一套自定义登录界面,当然了甚至会提出登录界面里面新增部门选择呀、区域选择等等这些业务性的

当然了我们还有以下的使用场景:

  • 不同接入端登录页不一
  • 默认主题

本章目标

  1. 图标改变
  2. 背景颜色改变
  3. 部分字体改变

疑问与介绍

官方文档:https://apereo.github.io/cas/5.1.x/installation/User-Interface-Customization.html

主题?

先介绍下什么叫主题,主题就意味着风格不一,目的就是为了在不同的接入端(service)展示不同的页面,就例如淘宝登录天猫登录,其中登录点还是一个sso,但淘宝登录卖的广告是淘宝的,而天猫登录卖的广告是天猫的


简略看完后,会有以下的规范:

  • 静态资源(js,css)存放目录为src/main/resources/static
  • html资源存(thymeleaf)放目录为src/main/resources/templates
  • 主题配置文件存放在src/main/resources并且命名为[theme_name].properties
  • 主题页面html存放目录为src/main/resources/templates/<theme-id>

可能看完上面的规范会有一些疑问

主题渲染是怎么样的?

官方文档明确说明,登录页渲染文件为casLoginView.html,那意味我们在主题具体目录下新增改文件并且按照cas要求写那就可以了

Created with Raphaël 2.1.0访问cas有service?获取service主题加载主题配置加载主题登录页渲染获取默认主题yesno

最终目的还是获取到对应的配置文件,渲染对应主题的登录页

接入服务如何指定主题?

{  "@class" : "org.apereo.cas.services.RegexRegisteredService",  "serviceId" : "^https://www.example.org",  "name" : "MyTheme",  "theme" : "[theme_name]",  "id" : 1000}

theme为key指定配置文件id

若主题配置文件为test_theme.application"theme":"test_theme"

如何修改默认主题?

application.properties

cas.theme.defaultThemeName=[theme_id]

实战

新建主题配置文件

在src/main下新建demo.properties

demo.css.file=/themes/demo/css/demo.css

新建样式文件

由于上面指定了样式文件位置,我们必须在,src\main\resources\static\themes\demo\css下建立demo.css

为了简单起见,h1标签的全为蓝色

h1 {    color: blue;}

新建登录也文件

明码规定文件名为casLoginView.html,路径为src/main/resources/templates/demo

为了简单起见,以最简单的方式展示出来

注意要点:

from表单的内容需要遵循一定的标准th:object等等

<!DOCTYPE html><!--  ~ 版权所有.(c)2008-2017.卡尔科技工作室  --><html><head>    <meta charset="UTF-8"/>    <meta name="viewport" content="width=device-width, initial-scale=1"/>    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>    <title th:text="${#themes.code('demo.pageTitle')}"></title>    <link rel="stylesheet" th:href="@{${#themes.code('demo.css.file')}}"/></head><body><h1 th:text="${#themes.code('demo.pageTitle')}"></h1><div>    <form method="post" th:object="${credential}">        <div th:if="${#fields.hasErrors('*')}">            <span th:each="err : ${#fields.errors('*')}" th:utext="${err}"/>        </div>        <h2 th:utext="#{screen.welcome.instructions}"></h2>        <section class="row">            <label for="username" th:utext="#{screen.welcome.label.netid}"/>            <div th:unless="${openIdLocalId}">                <input class="required"                       id="username"                       size="25"                       tabindex="1"                       type="text"                       th:disabled="${guaEnabled}"                       th:field="*{username}"                       th:accesskey="#{screen.welcome.label.netid.accesskey}"                       autocomplete="off"/>            </div>        </section>        <section class="row">            <label for="password" th:utext="#{screen.welcome.label.password}"/>            <div>                <input class="required"                       type="password"                       id="password"                       size="25"                       tabindex="2"                       th:accesskey="#{screen.welcome.label.password.accesskey}"                       th:field="*{password}"                       autocomplete="off"/>            </div>        </section>        <section>            <input type="hidden" name="execution" th:value="${flowExecutionKey}"/>            <input type="hidden" name="_eventId" value="submit"/>            <input type="hidden" name="geolocation"/>            <input class="btn btn-submit btn-block"                   name="submit"                   accesskey="l"                   th:value="#{screen.welcome.button.login}"                   tabindex="6"                   type="submit"/>        </section>    </form></div></body></html>

修改service主题

{  "@class": "org.apereo.cas.services.RegexRegisteredService",  "serviceId": "^http://(localhost|192.168).*",  "name": "Local Services",  "id": 10000002,  "description": "this is a localhost service",  "evaluationOrder": 10000000,  "theme":"demo"}

重点为: "theme":"demo"

最终效果如下

这里写图片描述


这里写图片描述


bug和疑问

  1. cas版本5.1.0~5.1.5不支持默认主题目录(application.properties配置了cas.theme.defaultThemeName不会加载主题目录下src/main/resources/templates/[theme_id]/casLoginView.html文件,解决办法为覆盖方式)
  2. 由于访问第一次的时候默认会对页面进行缓存,需要spring.thymeleaf.cache=false
  3. 当访问cas携带service,主题目录生效
  4. 上述问题在cas5.2.x得到 解决

加载代码如下:
发现,如果没有service(39~52行代码)是不会到主题目录下加载主题页面

public class RegisteredServiceThemeBasedViewResolver extends ThymeleafViewResolver {    private static final Logger LOGGER = LoggerFactory.getLogger(RegisteredServiceThemeBasedViewResolver.class);    private final ServicesManager servicesManager;    private final ArgumentExtractor argumentExtractor;    private final String prefix;    private final String suffix;    public RegisteredServiceThemeBasedViewResolver(final ServicesManager servicesManager,                                                   final ArgumentExtractor argumentExtractor,                                                   final String prefix,                                                   final String suffix) {        this.servicesManager = servicesManager;        this.argumentExtractor = argumentExtractor;        this.prefix = prefix;        this.suffix = suffix;    }    @Override    protected View loadView(final String viewName, final Locale locale) throws Exception {        final View view = super.loadView(viewName, locale);        final RequestContext requestContext = RequestContextHolder.getRequestContext();        final WebApplicationService service;        final HttpServletResponse response;        final List<ArgumentExtractor> argumentExtractorList = Collections.singletonList(this.argumentExtractor);        if (requestContext != null) {            response = WebUtils.getHttpServletResponse(requestContext);            service = WebUtils.getService(argumentExtractorList, requestContext);        } else {            final HttpServletRequest request = WebUtils.getHttpServletRequestFromRequestAttributes();            service = WebUtils.getService(argumentExtractorList, request);            response = WebUtils.getHttpServletResponseFromRequestAttributes();        }        if (service == null) {            return view;        }        final RegisteredService registeredService = this.servicesManager.findServiceBy(service);        if (registeredService != null) {            try {                RegisteredServiceAccessStrategyUtils.ensureServiceAccessIsAllowed(service, registeredService);            } catch (final Exception e) {                response.setStatus(HttpStatus.UNAUTHORIZED.value());            }        }        if (registeredService != null && StringUtils.hasText(registeredService.getTheme()) && view instanceof AbstractThymeleafView) {            LOGGER.debug("Attempting to locate views for service [{}] with theme [{}]",                    registeredService.getServiceId(), registeredService.getTheme());            final AbstractThymeleafView thymeleafView = (AbstractThymeleafView) view;            final String viewUrl = registeredService.getTheme() + '/' + thymeleafView.getTemplateName();            final String viewLocationUrl = prefix.concat(viewUrl).concat(suffix);            LOGGER.debug("Attempting to locate view at [{}]", viewLocationUrl);            final TemplateLocation location = new TemplateLocation(viewLocationUrl);            if (location.exists(getApplicationContext())) {                LOGGER.debug("Found view [{}]", viewUrl);                thymeleafView.setTemplateName(viewUrl);            } else {                LOGGER.debug("View [{}] does not exist. Falling back to default view at [{}]", viewLocationUrl, thymeleafView.getTemplateName());            }        }        return view;    }}

下载代码尝试:GitHub 其他版本可以到GitHub或者码云查看

发现一些意外的事情可以考虑翻翻前面的博客进行学习哦

作者联系方式

如果技术的交流或者疑问可以联系或者提出issue。

邮箱:huang.wenbin@foxmail.com

QQ: 756884434 (请注明:SSO-CSDN)

阅读全文
3 1
原创粉丝点击