CAS單點登錄(六)——自定義登錄界面和表單信息
在上一節我們講述了CAS中的Service配置及管理,對於添加CAS中的服務到注冊的表中有了一定的了解,如果不是很熟悉,可以去復習一下CAS單點登錄(五)——Service配置及管理。
今天,我們接着前面沒有講解完的文章繼續講解,關於CAS中如何自定義表單信息提交以及如何自定義用戶相關頁面的知識點。
一、自定義用戶界面
在上一節中我們講解了關於Service配置和管理,在Service的配置中,我們可以配置theme參數。比如,我們在使用上一節的代碼中使用Json來存儲Service配置,在web-10000001.json文件中,我們添加指定主題的參數為anumbrella。配置如下:
{
"@class" : "org.apereo.cas.services.RegexRegisteredService",
"serviceId" : "^(https|imaps|http)://.*",
"name" : "web",
"id" : 10000001,
"evaluationOrder" : 10,
"accessStrategy" : {
"@class" : "org.apereo.cas.services.DefaultRegisteredServiceAccessStrategy",
"enabled" : true,
"ssoEnabled" : true
},
"theme": "anumbrella"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
接着我們在src/main目錄下新建anumbrella.properties文件,文件名與主題參數一致。在官網中推薦我們在配置文件中寫法為:
cas.standard.css.file=/themes/[theme_name]/css/cas.css
cas.javascript.file=/themes/[theme_name]/js/cas.js
cas.admin.css.file=/themes/[theme_name]/css/admin.css
1
2
3
這里采用的寫法會把CAS系統中自帶的頁面樣式完全覆蓋,如果我們只想自定義一部分頁面,可以采用自定義部分樣式的寫法。
anumbrella.javascript.file=/themes/anumbrella/js/cas.js
anumbrella.standard.css.file=/themes/anumbrella/css/cas.css
1
2
比如這里我只想自定義登錄頁面,其他頁面不變,可以采用上面的寫法。所以anumbrella.properties文件的內容如下:
anumbrella.javascript.file=/themes/anumbrella/js/cas.js
anumbrella.standard.css.file=/themes/anumbrella/css/cas.css
anumbrella.login.images.path=/themes/anumbrella/images
cas.standard.css.file=/css/cas.css
cas.javascript.file=/js/cas.js
cas.admin.css.file=/css/admin.css
1
2
3
4
5
6
7
8
anumbrella.login.images.path=/themes/anumbrella/images為要在html頁面使用到的圖片路徑,所以這里自定義圖片的地址。
接着我們在src\main\resources文件下新建static和templates文件夾,同時在static文件夾下新建themes/anumbrella文件夾,在templates目錄下新建anumbrella文件夾。繼續在static/themes/anumbrella下新建css、js、images這三個文件夾,把需要的css、js、圖片放入這下面。接着我們在templates/anumbrella目錄下新建casLoginView.html文件。
具體路徑如下所示:
注意:這里的casLoginView.html文件不能亂命名,必須為casLoginView.html。這里是覆蓋登錄頁面所以命名為casLoginView.html,如果要覆蓋退出頁面則是casLogoutView.html。
關於如何確定頁面名稱和如何選擇,這里其實在CAS單點登錄(二)——搭建基礎服務這節有提示,因為采用的是覆蓋模式,所以我們建立的附件都是將原來有的文件覆蓋掉,所以我們可以在打包的cas.war解壓包中或IDE中target文件中查看到具體的各種文件。
比如,在IDE中的target目錄下的cas目錄中的WEB-INF的classes里面的templates目錄下,我們可以找到各種html,還有static文件夾,在該目錄中我們可以尋找到各種需要的文件,編寫的html也可以參考這里的源碼來寫。
在編寫html時,from表單的內容需要遵循一定的標准th:object等等。
casLoginView.html內容如下:
<!DOCTYPE html>
<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>單點登錄SSO</title>
<link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet">
<link rel='stylesheet prefetch' href='https://fonts.googleapis.com/icon?family=Material+Icons'>
<link rel="stylesheet" th:href="@{${#themes.code('anumbrella.standard.css.file')}}"/>
</head>
<body>
<div class="cotn_principal">
<div class="cont_centrar">
<div class="cont_login">
<div class="cont_info_log_sign_up">
<div class="col_md_login">
<div class="cont_ba_opcitiy">
<h2>SSO</h2>
<p>點擊登錄輸入信息</p>
<button class="btn_login" onClick="cambiar_login()">登錄</button>
</div>
</div>
</div>
<div class="cont_back_info">
<div class="cont_img_back_grey"> <img th:src="@{${#themes.code('anumbrella.login.images.path')}+'/po.jpeg'}" alt="" /> </div>
</div>
<div class="cont_forms" >
<div class="cont_img_back_"> <img th:src="@{${#themes.code('anumbrella.login.images.path')}+'/po.jpeg'}" alt="" /> </div>
<div class="cont_form_login"> <a href="#" onClick="ocultar_login_sign_up()" ><i class="material-icons"></i></a>
<form method="post" th:object="${credential}">
<h2>SSO</h2>
<section class="row">
<div th:unless="${openIdLocalId}">
<input class="required"
id="username"
size="25"
tabindex="1"
placeholder="用戶名"
type="text"
th:disabled="${guaEnabled}"
th:field="*{username}"
th:accesskey="#{screen.welcome.label.netid.accesskey}"
autocomplete="off"/>
</div>
</section>
<section class="row">
<div>
<input class="required"
type="password"
id="password"
size="25"
tabindex="2"
placeholder="密碼"
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 btn_login"
style="text-align: center"
name="submit"
accesskey="l"
th:value="#{screen.welcome.button.login}"
tabindex="6"
type="submit"/>
</section>
<div th:if="${#fields.hasErrors('*')}">
<span th:each="err : ${#fields.errors('*')}" th:utext="${err}"/>
</div>
</form>
</div>
<div class="cont_form_sign_up"/>
</div>
</div>
</div>
</div>
<script th:src="@{${#themes.code('anumbrella.javascript.file')}}"></script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
這里提一下,關於css、js、圖片引用,如果圖片引用在css中使用,直接采用相對路徑;如果要在前台(html中)寫圖片地址,可以像這里我使用這樣的寫法。然后通過th:object來應用在anumbrella.properties配置的路徑地址。
由於這里我們采用的Json服務配置,所以在application.properties中開啟Json服務注冊配置。
##
# Service Registry(服務注冊)
#
# 開啟識別Json文件,默認false
cas.serviceRegistry.initFromJson=true
#自動掃描服務配置,默認開啟
cas.serviceRegistry.watcherEnabled=true
#120秒掃描一遍
cas.serviceRegistry.schedule.repeatInterval=120000
#延遲15秒開啟
# cas.serviceRegistry.schedule.startDelay=15000
##
# Json配置
#
cas.serviceRegistry.json.location=classpath:/services
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
接着我們啟動CAS服務,輸入路由https://sso.anumbrella.net:8443/cas/login?service=http://localhost:9080/sample,可以發現出現我們自定義的登錄界面。
但如果我們直接輸入https://sso.anumbrella.net:8443/cas/login則直接返回原來的CAS主題了。
這是因為前面我們是在Service中配置了主題的,只有符合的Servivce才采用配置了的主題。那么如何更改所有的默認主題呢?
這里有兩種方法,一種是直接更改默認主題,在application.properties文件中,添加我們需要自定義的文件。
cas.standard.css.file=/css/cas.css
cas.javascript.file=/js/cas.js
cas.admin.css.file=/css/admin.css
1
2
3
然后在static文件下新建css、js、images這三個文件下,把需要的css、js、圖片放入這下面。接着我們在templates目錄下新建casLoginView.html文件。這里的配置覆蓋就是直接覆蓋原來默認主題的文件,具體同前面的一致,只是路徑不同。
另一種方法是使用默認主題配置,將新建的主題設置為默認主題,建議采用這種方案,更容易控制。在application.properties文件中添加如下配置:
# 默認主題配置
cas.theme.defaultThemeName=anumbrella
1
2
重啟CAS服務,輸入https://sso.anumbrella.net:8443/cas/login,發現登錄頁面變了。接着我們登錄,發現跳轉到原來默認的主題了,這是因為我們的主題默認是只覆蓋了登錄頁面,其他繼續采用默認主題,這個可以根據需求自定義更改。
二、自定義表單信息
先添加依賴類,這里會使用到兩個依賴類:
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-core-webflow</artifactId>
<version>${cas.version}</version>
</dependency>
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-core-webflow-api</artifactId>
<version>${cas.version}</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
在前面CAS單點登錄(四)——自定義認證登錄策略中,我們提到過如果要驗證其他信息,比如郵箱,手機號,但是郵箱,手機信息在另一個數據庫,還有在一段時間內同一IP輸入錯誤次數限制等。這里就需要我們自定義認證策略,自定義CAS的web認證流程。
首先我們創建需要的表單信息,即這里除了要使用用戶名和密碼外,還要用戶輸入郵箱,手機號,所以需要自定義Credential,由於有用戶名和密碼,所以可以直接繼承UsernamePasswordCredential,新建類CustomCredential,具體如下:
package net.anumbrella.sso.entity;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apereo.cas.authentication.UsernamePasswordCredential;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.constraints.Size;
/**
* @author Anumbrella
*/
public class CustomCredential extends UsernamePasswordCredential {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomCredential.class);
private static final long serialVersionUID = -4166149641561667276L;
@Size(min = 1, message = "require email")
private String email;
@Size(min = 1, message = "require telephone")
private String telephone;
public String getEmail() {
return email;
}
public void setEmail(final String email) {
this.email = email;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(final String telephone) {
this.telephone = telephone;
}
public CustomCredential() {
}
public CustomCredential(final String email, final String telephone) {
this.email = email;
this.telephone = telephone;
}
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof CustomCredential)) {
return false;
} else {
CustomCredential other = (CustomCredential) o;
if (!other.canEqual(this)) {
return false;
} else {
Object this$email = this.email;
Object other$email = other.email;
if (this$email == null) {
if (other$email != null) {
return false;
}
} else if (!this$email.equals(other$email)) {
return false;
}
Object this$telephone = this.telephone;
Object other$telephone = other.telephone;
if (this$telephone == null) {
if (other$telephone != null) {
return false;
}
} else if (!this$telephone.equals(other$telephone)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(final Object other) {
return other instanceof CustomCredential;
}
@Override
public int hashCode() {
return new HashCodeBuilder()
.appendSuper(super.hashCode())
.append(this.email)
.append(this.telephone)
.toHashCode();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
這里新增定義了郵箱和手機號,並且是必須輸入項。然后我們重新定義CAS的Web流程,新建類CustomWebflowConfigurer,定義如下:
package net.anumbrella.sso.config;
import net.anumbrella.sso.entity.CustomCredential;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.web.flow.CasWebflowConstants;
import org.apereo.cas.web.flow.configurer.AbstractCasWebflowConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.ViewState;
import org.springframework.webflow.engine.builder.BinderConfiguration;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
/**
* @author anumbrella
*/
public class CustomWebflowConfigurer extends AbstractCasWebflowConfigurer {
public CustomWebflowConfigurer(FlowBuilderServices flowBuilderServices,
FlowDefinitionRegistry flowDefinitionRegistry,
ApplicationContext applicationContext,
CasConfigurationProperties casProperties) {
super(flowBuilderServices, flowDefinitionRegistry, applicationContext, casProperties);
}
@Override
protected void doInitialize() {
final Flow flow = super.getLoginFlow();
bindCredential(flow);
}
/**
* 綁定自定義的Credential信息
*
* @param flow
*/
protected void bindCredential(Flow flow) {
// 重寫綁定自定義credential
// 重寫綁定自定義credential
createFlowVariable(flow, CasWebflowConstants.VAR_ID_CREDENTIAL, CustomCredential.class);
// 登錄頁綁定新參數
final ViewState state = (ViewState) flow.getState(CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM);
final BinderConfiguration cfg = getViewStateBinderConfiguration(state);
// 由於用戶名以及密碼已經綁定,所以只需對新加系統參數綁定即可
// 字段名,轉換器,是否必須字段
cfg.addBinding(new BinderConfiguration.Binding("email", null, true));
cfg.addBinding(new BinderConfiguration.Binding("telephone", null, true));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
在初始化doInitialize中,綁定我們需要的郵箱和電話信息,用戶名以及密碼是早已經綁定了,所以不用添加。
然后再新增類CustomerAuthWebflowConfiguration,更改CAS的Web流程的配置代理,如下所示:
package net.anumbrella.sso.config;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.web.flow.CasWebflowConfigurer;
import org.apereo.cas.web.flow.CasWebflowExecutionPlan;
import org.apereo.cas.web.flow.CasWebflowExecutionPlanConfigurer;
import org.apereo.cas.web.flow.config.CasWebflowContextConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
/**
* @author anumbrella
*/
@Configuration("customerAuthWebflowConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class CustomerAuthWebflowConfiguration implements CasWebflowExecutionPlanConfigurer {
@Autowired
private CasConfigurationProperties casProperties;
@Autowired
@Qualifier("loginFlowRegistry")
private FlowDefinitionRegistry loginFlowDefinitionRegistry;
@Autowired
private ApplicationContext applicationContext;
@Autowired
private FlowBuilderServices flowBuilderServices;
@Bean
public CasWebflowConfigurer customWebflowConfigurer() {
// 實例化自定義的表單配置類
final CustomWebflowConfigurer c = new CustomWebflowConfigurer(flowBuilderServices, loginFlowDefinitionRegistry,
applicationContext, casProperties);
// 初始化
c.initialize();
// 返回對象
return c;
}
@Override
public void configureWebflowExecutionPlan(final CasWebflowExecutionPlan plan) {
plan.registerWebflowConfigurer(customWebflowConfigurer());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
主要是通過繼承CasWebflowExecutionPlanConfigurer,實現配置的初始化和注冊。最后在resources下的META-INF文件下的spring.factories注入spring boot的配置,就是我們上面的類,如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
net.anumbrella.sso.config.CustomAuthenticationConfiguration,\
net.anumbrella.sso.controller.ServicesManagerController,\
net.anumbrella.sso.config.CustomerAuthWebflowConfiguration
1
2
3
4
接下來的流程就是處理自定義提交信息的邏輯,與我們在CAS單點登錄(四)——自定義認證登錄策略中講解的一致,通過攔截請求獲取到Handler,來實現自定義認證策略,主要是繼承AbstractPreAndPostProcessingAuthenticationHandler類來實現的,基本思路是一樣的,這里需要大致更改一下,如下:
package net.anumbrella.sso.authentication;
import net.anumbrella.sso.entity.CustomCredential;
import net.anumbrella.sso.entity.User;
import org.apereo.cas.authentication.*;
import org.apereo.cas.authentication.handler.support.AbstractPreAndPostProcessingAuthenticationHandler;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.services.ServicesManager;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.security.auth.login.AccountException;
import javax.security.auth.login.FailedLoginException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author anumbrella
*/
public class CustomerHandlerAuthentication extends AbstractPreAndPostProcessingAuthenticationHandler {
public CustomerHandlerAuthentication(String name, ServicesManager servicesManager, PrincipalFactory principalFactory, Integer order) {
super(name, servicesManager, principalFactory, order);
}
@Override
public boolean supports(Credential credential) {
//判斷傳遞過來的Credential 是否是自己能處理的類型
return credential instanceof CustomCredential;
}
@Override
protected AuthenticationHandlerExecutionResult doAuthentication(Credential credential) throws GeneralSecurityException, PreventedException {
CustomCredential customCredential = (CustomCredential) credential;
String username = customCredential.getUsername();
String password = customCredential.getPassword();
String email = customCredential.getEmail();
String telephone = customCredential.getTelephone();
System.out.println("username : " + username);
System.out.println("password : " + password);
System.out.println("email : " + email);
System.out.println("telephone : " + telephone);
// JDBC模板依賴於連接池來獲得數據的連接,所以必須先要構造連接池
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/cas");
dataSource.setUsername("root");
dataSource.setPassword("123");
// 創建JDBC模板
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
String sql = "SELECT * FROM user WHERE username = ?";
User info = (User) jdbcTemplate.queryForObject(sql, new Object[]{username}, new BeanPropertyRowMapper(User.class));
System.out.println("database username : "+ info.getUsername());
System.out.println("database password : "+ info.getPassword());
if (info == null) {
throw new AccountException("Sorry, username not found!");
}
if (!info.getPassword().equals(password)) {
throw new FailedLoginException("Sorry, password not correct!");
} else {
final List<MessageDescriptor> list = new ArrayList<>();
return createHandlerResult(customCredential,
this.principalFactory.createPrincipal(username, Collections.emptyMap()), list);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
在supports方法中,將實例類判斷更改為我們自定義的Credential,同時在doAuthentication中將Credential轉換為我們自定義的Credential,這里我將郵箱和手機號獲取出來,並打印到控制台。實際開發中可以具體處理邏輯。
接着我們在CustomAuthenticationConfiguration類中,將要處理的自定義邏輯更改為上面我們自定義的邏輯CustomerHandlerAuthentication類,如下:
package net.anumbrella.sso.config;
import net.anumbrella.sso.authentication.CustomerHandlerAuthentication;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer;
import org.apereo.cas.authentication.AuthenticationHandler;
import org.apereo.cas.authentication.principal.DefaultPrincipalFactory;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.services.ServicesManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author anumbrella
*/
@Configuration("customAuthenticationConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class CustomAuthenticationConfiguration implements AuthenticationEventExecutionPlanConfigurer {
@Autowired
private CasConfigurationProperties casProperties;
@Autowired
@Qualifier("servicesManager")
private ServicesManager servicesManager;
@Bean
public AuthenticationHandler myAuthenticationHandler() {
// 參數: name, servicesManager, principalFactory, order
// 定義為優先使用它進行認證
// return new CustomUsernamePasswordAuthentication(CustomUsernamePasswordAuthentication.class.getName(),
// servicesManager, new DefaultPrincipalFactory(), 1);
return new CustomerHandlerAuthentication(CustomerHandlerAuthentication.class.getName(),
servicesManager, new DefaultPrincipalFactory(), 1);
}
@Override
public void configureAuthenticationExecutionPlan(final AuthenticationEventExecutionPlan plan) {
plan.registerAuthenticationHandler(myAuthenticationHandler());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
最后在還是在resources下的META-INF文件下的spring.factories注入spring boot的配置。這里與第四節的內容基本相同,不熟悉的可以先去看看第四節的內容————CAS單點登錄(四)——自定義認證登錄策略。
后台的處理完成了,我們還要更改前台頁面casLoginView.html的內容,因為我們除了用戶名和密碼外,還要郵箱和手機號,所以需要更改界面。
在前面我們的自定義界面的casLoginView.html中,新增兩個字段,如下:
<!DOCTYPE html>
<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>單點登錄SSO</title>
<link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet">
<link rel='stylesheet prefetch' href='https://fonts.googleapis.com/icon?family=Material+Icons'>
<link rel="stylesheet" th:href="@{${#themes.code('anumbrella.standard.css.file')}}"/>
</head>
<body>
<div class="cotn_principal">
<div class="cont_centrar">
<div class="cont_login">
<div class="cont_info_log_sign_up">
<div class="col_md_login">
<div class="cont_ba_opcitiy">
<h2>SSO</h2>
<p>點擊登錄輸入信息</p>
<button class="btn_login" onClick="cambiar_login()">登錄</button>
</div>
</div>
</div>
<div class="cont_back_info">
<div class="cont_img_back_grey"> <img th:src="@{${#themes.code('anumbrella.login.images.path')}+'/po.jpeg'}" alt="" /> </div>
</div>
<div class="cont_forms" >
<div class="cont_img_back_"> <img th:src="@{${#themes.code('anumbrella.login.images.path')}+'/po.jpeg'}" alt="" /> </div>
<div class="cont_form_login"> <a href="#" onClick="ocultar_login_sign_up()" ><i class="material-icons"></i></a>
<form method="post" th:object="${credential}">
<h2>SSO</h2>
<section class="row">
<div th:unless="${openIdLocalId}">
<input class="required"
id="username"
size="25"
tabindex="1"
placeholder="用戶名"
type="text"
th:disabled="${guaEnabled}"
th:field="*{username}"
th:accesskey="#{screen.welcome.label.netid.accesskey}"
autocomplete="off"/>
</div>
</section>
<section class="row">
<div>
<input class="required"
type="password"
id="password"
size="25"
tabindex="2"
placeholder="密碼"
th:accesskey="#{screen.welcome.label.password.accesskey}"
th:field="*{password}"
autocomplete="off"/>
</div>
</section>
<section class="row">
<div>
<input class="required"
id="email"
size="25"
tabindex="1"
placeholder="郵箱"
type="text"
th:field="*{email}"
autocomplete="off"/>
</div>
</section>
<section class="row">
<div>
<input class="required"
id="telephone"
size="25"
tabindex="1"
placeholder="手機"
type="text"
th:field="*{telephone}"
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 btn_login"
style="text-align: center"
name="submit"
accesskey="l"
th:value="#{screen.welcome.button.login}"
tabindex="6"
type="submit"/>
</section>
<div th:if="${#fields.hasErrors('*')}">
<span th:each="err : ${#fields.errors('*')}" th:utext="${err}"/>
</div>
</form>
</div>
<div class="cont_form_sign_up"/>
</div>
</div>
</div>
</div>
<script th:src="@{${#themes.code('anumbrella.javascript.file')}}"></script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
主要是新增了兩個字段的內容,如下:
<section class="row">
<div>
<input class="required"
id="email"
size="25"
tabindex="1"
placeholder="郵箱"
type="text"
th:field="*{email}"
autocomplete="off"/>
</div>
</section>
<section class="row">
<div>
<input class="required"
id="telephone"
size="25"
tabindex="1"
placeholder="手機"
type="text"
th:field="*{telephone}"
autocomplete="off"/>
</div>
</section>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
頁面綁定參數為新參數th:field=”{email}”、th:field=”{telephone}”這兩個字段。保存代碼后,重啟CAS服務,然后我們可以發現登錄界面樣式更改如下:
接着我們輸入用戶名、密碼和郵箱,不輸入電話號碼,點擊登錄會失敗,同時也帶有提示。如下:
最后我們輸入完整的信息,登錄成功,然后我們可以發現后台打印的信息。
OK,本節內容就到此結束,后面再介紹其他關於CAS的內容。
代碼實例:Chapter5
參考
CAS單點登錄-自定義認證之重寫Credential(十五)
CAS之5.2.x版本自定義表單信息-yellowcong
https://apereo.github.io/cas/5.3.x/installation/Webflow-Customization-Extensions.html
https://apereo.github.io/cas/5.3.x/installation/User-Interface-Customization.html
點贊 2
————————————————
版權聲明:本文為CSDN博主「Anumbrella」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/anumbrella/article/details/82728641