在我们的 CAS 配置中,我们有很多身份验证源。因此,在此页面上,用户使用 select2 等方法搜索和选择源将很有用。 如何修改loginform.html片段? (https://github.com/apereo/cas/blob/master/support/cas-server-support-thymeleaf/src/main/resources/templates/fragments/loginform.html)
感谢您的支持。
v.
答案在这里。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
<title>Login Form Fragment</title>
<link href="../../static/css/cas.css" rel="stylesheet" th:remove="tag"/>
</head>
<body>
<main class="container mt-3 mb-3">
<div th:fragment="loginform" class="d-flex flex-column justify-content-between m-auto"
th:with="loginFormEnabled=${#strings.defaultString(#themes.code('cas.login-form.enabled'), 'true') == 'true'},
loginFormViewable=${@casThymeleafTemplatesDirector.isLoginFormViewable(#vars)}">
<div th:if="${delegatedAuthenticationProviderPrimary == null}">
<div th:if="${!#strings.isEmpty(#themes.code('cas.hero-banner.file'))}">
<p>
<img id="heroimg"
th:title="${#strings.defaultString(#themes.code('cas.theme.name'), 'CAS')}"
th:src="@{${#themes.code('cas.hero-banner.file')}}"/>
</p>
</div>
<div class="service-ui" th:replace="~{fragments/serviceui :: serviceUI}">
<a href="fragments/serviceui.html">service ui fragment</a>
</div>
</div>
<div class="form-wrapper">
<form method="post" id="fm1" th:object="${credential}" action="login">
<div id="login-form-controls" th:unless="${loginFormViewable or loginFormEnabled}">
<div id="loginErrorsPanel" class="alert alert-danger banner banner-danger banner-dismissible"
th:if="${#fields.hasErrors('*')}">
<p th:each="err : ${#fields.errors('*')}" th:utext="${err + ' '}">Example error</p>
<!--<a href="#" class="close" data-dismiss="alert" aria-label="close">×</a>-->
</div>
</div>
<div id="login-form-controls" th:if="${loginFormViewable and loginFormEnabled}">
<div th:if="${existingSingleSignOnSessionAvailable}">
<i class="mdi mdi-alert-decagram fas fa-exclamation-triangle"></i>
<span id="existingSsoMsg" th:if="${registeredService}" class="mdc-button__label"
th:utext="#{screen.welcome.forcedsso(${existingSingleSignOnSessionPrincipal?.id},${registeredService.name})}"/>
<span id="existingSsoMsg" th:unless="${registeredService}" class="mdc-button__label"
th:utext="#{screen.welcome.forcedsso(${existingSingleSignOnSessionPrincipal?.id}, 'CAS')}"/>
</div>
<h2 th:unless="${existingSingleSignOnSessionAvailable}" class="text-center">
<i class="mdi mdi-security fas fa-shield-alt" aria-hidden="true"></i>
<span th:utext="#{screen.welcome.instructions}">Enter your Username and Password:</span>
</h2>
<div id="loginErrorsPanel" class="banner banner-danger alert alert-danger banner-dismissible"
th:if="${#fields.hasErrors('*')}" role="alert">
<p th:each="err : ${#fields.errors('*')}" th:utext="${err + ' '}">Example error</p>
<!--<a href="#" class="close" data-dismiss="alert" aria-label="close">×</a>-->
</div>
<section class="cas-field form-group my-3" id="usernameSection">
<label for="username"
class="mdc-text-field mdc-text-field--outlined control-label w-100">
<span class="mdc-notched-outline">
<span class="mdc-notched-outline__leading"></span>
<span class="mdc-notched-outline__notch">
<span class="mdc-floating-label"
th:utext="#{screen.welcome.label.netid}">Username</span>
</span>
<span class="mdc-notched-outline__trailing"></span>
</span>
<input class="mdc-text-field__input form-control" id="username"
size="25"
type="text"
th:readonly="!${@casThymeleafTemplatesDirector.isLoginFormUsernameInputVisible(#vars)}"
th:field="*{username}"
th:accesskey="#{screen.welcome.label.netid.accesskey}"
autocapitalize="none"
spellcheck="false"
autocomplete="username" required />
</label>
<div class="mdc-text-field-helper-line invalid-feedback">
<div class="mdc-text-field-helper-text mdc-text-field-helper-text--validation-msg" aria-hidden="true">
<span id="usernameValidationMessage" th:utext="#{username.required}"></span>
</div>
</div>
<script type="text/javascript" th:inline="javascript">
/*<![CDATA[*/
let username = /*[[${@casThymeleafTemplatesDirector.getLoginFormUsername(#vars)}]]*/;
let disabled = /*[[${@casThymeleafTemplatesDirector.isLoginFormUsernameInputDisabled(#vars)}]]*/;
if (username != null && username !== '') {
$('#username').val(username);
if (disabled) {
$('#usernameSection').hide();
}
}
/*]]>*/
</script>
</section>
<section class="cas-field form-group my-3 mdc-input-group form-group" id="passwordSection">
<div class="mdc-input-group-field mdc-input-group-field-append">
<label for="password"
class="mdc-text-field caps-check mdc-text-field--outlined control-label mdc-text-field--with-trailing-icon control-label w-100">
<span class="mdc-notched-outline">
<span class="mdc-notched-outline__leading"></span>
<span class="mdc-notched-outline__notch">
<span class="mdc-floating-label" th:utext="#{screen.welcome.label.password}">Password</span>
</span>
<span class="mdc-notched-outline__trailing"></span>
</span>
<input class="mdc-text-field__input form-control pwd"
type="password"
id="password"
size="25"
required
th:accesskey="#{screen.welcome.label.password.accesskey}"
th:field="*{password}"
autocomplete="off"/>
<button
class="reveal-password mdc-button mdc-button--unelevated mdc-input-group-append mdc-icon-button btn btn-primary"
tabindex="-1" type="button">
<i class="mdi mdi-eye reveal-password-icon fas fa-eye"></i>
<span class="visually-hidden">Toggle Password</span>
</button>
</label>
<div class="mdc-text-field-helper-line invalid-feedback">
<div class="mdc-text-field-helper-text mdc-text-field-helper-text--validation-msg" aria-hidden="true">
<span id="passwordValidationMessage" th:utext="#{password.required}"></span>
</div>
</div>
</div>
</section>
<section id="authnSourceSection" class="cas-field form-group my-3"
th:if="${availableAuthenticationHandlerNames != null}">
<p>
Per poter accedere alla risorsa per favore selezioni o cerchi l'organizzazione con la quale è affiliato.
</p>
<style>
li[data-isfound="false"] {
display: none;
}
.hide-none {
display: none !important;
}
</style>
<div class="mdc-menu-surface--anchor">
<label class="mdc-text-field mdc-text-field--outlined">
<span class="mdc-notched-outline">
<span class="mdc-notched-outline__leading"></span>
<span class="mdc-notched-outline__notch">
<span class="mdc-floating-label" id="my-label-id" th:utext="#{screen.welcome.label.source}">Source</span>
</span>
<span class="mdc-notched-outline__trailing"></span>
</span>
<input id="searchSources" type="text" class="mdc-text-field__input mdc-select__selected-text" aria-labelledby="my-label-id">
</label>
<div id="menu" class="mdc-select__menu mdc-menu mdc-menu-surface mdc-menu-surface--fullwidth">
<ul class="mdc-list" role="listbox" >
<li id="menuNoData" class="mdc-list-item hide-none" aria-selected="false" aria-disabled="true" data-value="notfound" role="option" value="">
<span class="mdc-list-item__ripple"></span>
<span class="mdc-list-item__text">Nessuna origine trovata.</span>
</li>
<li th:each="handler,iter : ${availableAuthenticationHandlerNames}"
class="mdc-list-item " th:id="${handler + '-authnSource'}"
th:classappend="${iter.index == 0 ? 'mdc-list-item--selected' : ''}"
th:data-value="${handler}" role="option">
<span class="mdc-list-item__ripple"></span>
<span class="mdc-list-item__text" th:utext="${handler}">Option</span>
</li>
</ul>
</div>
</div>
<script>
var textFields = document.querySelectorAll('.mdc-text-field');
textFields.forEach(field => {
mdc.textField.MDCTextField.attachTo(field);
});
var menuS = document.querySelector("#menu");
var menu = new mdc.menu.MDCMenu(menuS);
var searchFieldS = document.querySelector("#searchSources");
menu.setAnchorCorner(mdc.menuSurface.Corner.BOTTOM_LEFT);
document.querySelectorAll('#menu li').forEach(function(li) {
li.addEventListener('click', function() {
const selectedLi = this.getAttribute("data-value");
if (selectedLi != "notfound") {
// If you are going to post the text field data, I recommend you to get data-value.
searchFieldS.value = selectedLi;
searchFieldS.setAttribute("data-value", selectedLi);
var hiddenInput = document.querySelector("#source");
hiddenInput.setAttribute("data-value", selectedLi);
hiddenInput.setAttribute("value", selectedLi);
}
});
});
// Open the menu when text field is focused.
(function() {
let menuFocused = false;
searchFieldS.addEventListener("focusin", () => {
if (!menuFocused) menu.open = true;
});
searchFieldS.addEventListener("click", () => {
menu.open = true;
});
menuS.addEventListener("focusin", () => {
menuFocused = true;
});
menuS.addEventListener("focusout", () => {
// This interval is to help make sure that input.focusIn doesn't re-open the menu
setTimeout(() => {
menuFocused = false;
}, 0);
});
searchFieldS.addEventListener("focusout", () => {
setTimeout(() => {
if (!menuFocused) menu.open = false;
}, 0);
});
})();
searchFieldS.addEventListener("keyup", function(e) {
const keyT = event.target.value.toUpperCase();
const menuList = document.querySelectorAll('.mdc-list > li > .mdc-list-item__text');
const menuLiss = document.querySelectorAll('.mdc-list-item');
const noDataEl = document.querySelector("#menuNoData");
//
const arr = [];
menuList.forEach(function(searchItem) {
if (searchItem.parentElement.getAttribute('id') != "menuNoData") {
searchItem.parentElement.dataset.isfound = searchItem.textContent.toUpperCase().includes(keyT) ? "true" : "false";
arr.push(searchItem.parentElement.getAttribute("data-isfound"));
}
});
if (arr.filter(function(countArr) {
return countArr == "true"
}).length == 0) {
noDataEl.classList.remove("hide-none");
} else {
if (!noDataEl.classList.contains("hide-none")) {
noDataEl.classList.add("hide-none");
}
}
});
</script>
<div class="d-flex">
<div th:if="${availableAuthenticationHandlerNames.size() > 1}"
class="mdc-select mdc-select--outlined mdc-select--required mdc-menu-surface--fullwidth authn-source">
<input type="hidden" id="source" th:field="*{source}" name="source"/>
</div>
<span th:if="${availableAuthenticationHandlerNames.size() == 1}">
<input type="hidden" id="source" name="source"
th:value="${availableAuthenticationHandlerNames.get(0)}"/>
</span>
</div>
</section>
<section class="cas-field form-group my-3">
<div th:each="entry: ${customLoginFormFields}">
<label class="mdc-text-field mdc-text-field--outlined control-label">
<input class="mdc-text-field__input form-control"
th:id="${entry.key + '-customField'}" th:name="${entry.key + '-customField'}"
size="25" type="text" th:field="*{customFields[__${entry.key}__]}"
autocomplete="off"/>
<span class="mdc-notched-outline">
<span class="mdc-notched-outline__leading"></span>
<span class="mdc-notched-outline__notch">
<span class="mdc-floating-label"
th:text="#{${entry.value.messageBundleKey}}">Label</span>
</span>
<span class="mdc-notched-outline__trailing"></span>
</span>
</label>
</div>
</section>
<section class="cas-field form-check"
th:if="${'true' == #strings.defaultString(#themes.code('cas.warn-on-redirect.enabled'), 'true')}">
<div class="mdc-form-field ">
<div class="mdc-checkbox">
<input type="checkbox"
class="mdc-checkbox__native-control form-check-input"
name="warn"
id="warn"/>
<div class="mdc-checkbox__background">
<svg class="mdc-checkbox__checkmark"
viewBox="0 0 24 24">
<path class="mdc-checkbox__checkmark-path"
fill="none"
d="M1.73,12.91 8.1,19.28 22.79,4.59"/>
</svg>
<div class="mdc-checkbox__mixedmark"></div>
</div>
<div class="mdc-checkbox__ripple"></div>
</div>
<label class="form-check-label" for="warn"
th:utext="#{screen.welcome.label.warn}">Warn Me</label>
</div>
<p/>
</section>
<section class="cas-field form-check"
th:if="${'true' == #strings.defaultString(#themes.code('cas.public-workstation.enabled'), 'true')}">
<div class="mdc-form-field ">
<div class="mdc-checkbox">
<input type="checkbox"
class="mdc-checkbox__native-control form-check-input"
name="publicWorkstation"
id="publicWorkstation"/>
<div class="mdc-checkbox__background">
<svg class="mdc-checkbox__checkmark"
viewBox="0 0 24 24">
<path class="mdc-checkbox__checkmark-path"
fill="none"
d="M1.73,12.91 8.1,19.28 22.79,4.59"/>
</svg>
<div class="mdc-checkbox__mixedmark"></div>
</div>
<div class="mdc-checkbox__ripple"></div>
</div>
<label class="form-check-label" for="publicWorkstation"
th:text="#{screen.welcome.label.publicstation}">Public Workstation</label>
</div>
<p/>
</section>
<section class="cas-field form-check" th:if="${rememberMeAuthenticationEnabled}">
<div class="mdc-form-field ">
<div class="mdc-checkbox">
<input type="checkbox"
class="mdc-checkbox__native-control form-check-input"
name="rememberMe"
id="rememberMe"/>
<div class="mdc-checkbox__background">
<svg class="mdc-checkbox__checkmark"
viewBox="0 0 24 24">
<path class="mdc-checkbox__checkmark-path"
fill="none"
d="M1.73,12.91 8.1,19.28 22.79,4.59"/>
</svg>
<div class="mdc-checkbox__mixedmark"></div>
</div>
<div class="mdc-checkbox__ripple"></div>
</div>
<label class="form-check-label" for="rememberMe"
th:text="#{screen.rememberme.checkbox.title}">Remember Me</label>
</div>
<p/>
</section>
<section class="cas-field">
<span th:if="${recaptchaLoginEnabled}">
<div th:replace="~{fragments/recaptcha :: recaptchaToken}"/>
</span>
<input type="hidden" name="execution" th:value="${flowExecutionKey}"/>
<input type="hidden" name="_eventId" value="submit"/>
<input type="hidden" name="geolocation"/>
<p th:if="${#strings.equalsIgnoreCase(httpRequestMethod, 'POST')}">
<span th:each="entry : ${httpRequestInitialPostParameters}" th:remove="tag">
<span th:each="entryValue : ${entry.value}" th:remove="tag">
<input type="hidden" th:name="${entry.key}" th:value="${entryValue}"/>
</span>
</span>
</p>
</section>
<div th:replace="~{fragments/submitbutton :: submitButton (messageKey='screen.welcome.button.login')}"/>
</div>
</form>
<div id="selectIdentityProvider"
th:if="${#bools.isTrue(delegatedAuthenticationDynamicProviderSelection) and loginFormViewable and loginFormEnabled}">
<p>
<form method="post" id="providerDiscoveryForm">
<input type="hidden" name="execution" th:value="${flowExecutionKey}"/>
<input type="hidden" name="_eventId" value="discovery"/>
<span class="fa fa-unlock"></span>
<button th:id="selectProviderButton"
class="mdc-button mdc-button--raised"
onclick="$('#providerDiscoveryForm').submit();"
th:value="#{screen.pac4j.button.selectprovider}">
<span class="mdc-button__label" th:text="#{screen.pac4j.button.selectprovider}">Select</span>
</button>
</form>
</div>
<div id="x509Login" th:if="${x509ClientAuthLoginEndpointUrl}">
<span th:if="${loginFormViewable and loginFormEnabled}">
<hr class="my-4"/>
<script th:inline="javascript">
/*<![CDATA[*/
function x509login() {
let url = /*[[${x509ClientAuthLoginEndpointUrl}]]*/;
url += window.location.search;
window.location.assign(url)
}
/*]]>*/
</script>
<a id="x509LoginLink" class="mdc-button mdc-button--raised btn btn-primary"
onclick="javascript:x509login();"
th:text="#{screen.welcome.button.loginx509}">X509 Login</a>
</span>
</div>
<hr th:if="${loginFormViewable and loginFormEnabled}" class="my-4"/>
<span id="webauthnLoginPanel" th:if="${webAuthnPrimaryAuthenticationEnabled}">
<script type="text/javascript">
$('#webauthnLoginPanel').show();
</script>
<div th:replace="~{fragments/webAuthnLogin :: webAuthnLogin}"/>
<hr class="my-4"/>
</span>
<div th:if="${loginFormViewable and loginFormEnabled}">
<span th:remove="tag"
th:if="${'true' == #strings.defaultString(#themes.code('cas.pm-links.enabled'), 'true')}">
<div th:replace="~{fragments/pmlinks :: pmlinks}"/>
</span>
</div>
<script type="text/javascript" th:inline="javascript">
/*<![CDATA[*/
var i = /*[[@{#{screen.welcome.button.loginwip}}]]*/
var j = /*[[@{#{screen.welcome.button.login}}]]*/
/*]]>*/
$(window).on('pageshow', function () {
$(':submit').prop('disabled', false);
$(':submit').attr('value', j);
});
$(document).ready(function () {
$("#fm1").submit(function () {
$(":submit").attr("disabled", true);
$(":submit").attr("value", i);
return true;
});
});
</script>
</div>
<div th:if="${loginFormViewable and loginFormEnabled}">
<div th:replace="~{fragments/loginsidebar :: loginsidebar}"/>
</div>
</div>
</main>
</body>
</html>