一.获取CAS5.3项目资源
GitHub - apereo/cas-overlay-template at 5.3
cas5.3.x还是基于jdk8运行,下一个版本6.0.x就基于jdk9了,随着cas版本升级要求jdk版本也越来越高,官网上和github上都有每个版本基本运行条件的说明,根据实际情况选择版本。
二.tomcat部署
利用maven-package打war包
2.将war包放到tomcat的webapps目录下,启动tomcat
三.支持HTTP协议
修改tomcat/webapps/cas/WEB-INF/classes/services/HTTPSandIMAPS-10000001.json文件,添加http支持,修改后代码如下。
修改tomcat/webapps/cas/WEB-INF/classes/application.properties文件
添加如下两行
cas.tgc.secure=false
cas.serviceRegistry.initFromJson=true
注释掉如下三行
#server.ssl.key-store=file:/etc/cas/thekeystore
#server.ssl.key-store-password=changeit
#server.ssl.key-password=changeit
重启tomcat,访问登录页进行登录
cas默认密码在application.properties文件的最后一行
cas.authn.accept.users=casuser::Mellon
四.前端代码
修改Navbar.vue,覆盖logout()方法
async logout() {
this.$confirm('确定注销并退出系统吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$store.dispatch('LogOut').then(() => {
if (!defaultSettings.casEnable) {
location.href = this.$router.options.base + "/index";
}
//location.href = '/index';
})
}).catch(() => {});
}
修改permission.js文件,复制代码粘贴到红框位置,适当修改地址,注意导包。
if (!defaultSettings.casEnable) {
next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
}
// 开启cas
if (defaultSettings.casEnable) {
window.location.href = defaultSettings.casloginUrl // 否则全部重定向到登录页
}
修改settings.js文件,添加如下配置,适当修改地址,service前是前面部署的cas服务地址,service后是自己的系统后端服务地址
/**
* 开启cas
*/
casEnable: false,
/**
* 单点登录url
*/
casloginUrl:
'http://cpmp.fulongai.cn/cas/login?service=http://192.168.2.154:8080',
/**
* 单点登出url
*/
caslogoutUrl:
'http://cpmp.fulongai.cn/cas/logout?service=http://192.168.2.154:8080'
修改user.js文件,替换LogOut方法,注意导包。
LogOut({ commit, state }) {
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
commit('SET_PERMISSIONS', [])
commit('SET_PROJECT', {})
sessionStorage.removeItem('dmp-projectId')
removeToken()
resolve()
window.location.href = defaultSettings.caslogoutUrl
}).catch(error => {
reject(error)
})
})
}
修改request.js文件,注释掉红框中代码
五.后端代码
framework.web.service下新增CasUserDetailsService.java类
package com.cpmplatform.framework.web.service;
import com.cpmplatform.common.core.domain.entity.SysUser;
import com.cpmplatform.common.core.domain.model.LoginUser;
import com.cpmplatform.common.enums.UserStatus;
import com.cpmplatform.common.exception.base.BaseException;
import com.cpmplatform.common.utils.StringUtils;
import com.cpmplatform.system.service.ISysUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
/**
* @author daixin
* @version 1.0
* @description: TODO
* @date 2022/12/30 17:41
*/
@Service
public class CasUserDetailsService implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {
private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
@Autowired
private ISysUserService userService;
@Autowired
private SysPermissionService permissionService;
public UserDetails createLoginUser(SysUser user) {
return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
}
@Override
public UserDetails loadUserDetails(CasAssertionAuthenticationToken token) throws UsernameNotFoundException {
String username = token.getName();
SysUser user = userService.selectUserByUserName(username);
if (StringUtils.isNull(user)) {
log.info("登录用户:{} 不存在.", username);
throw new UsernameNotFoundException("登录用户:" + username + " 不存在");
} else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
log.info("登录用户:{} 已被删除.", username);
throw new BaseException("对不起,您的账号:" + username + " 已被删除");
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
log.info("登录用户:{} 已被停用.", username);
throw new BaseException("对不起,您的账号:" + username + " 已停用");
}
return createLoginUser(user);
}
}
framework.security.handle下新增CasAuthenticationSuccessHandler.java类
package com.cpmplatform.framework.security.handle;
import com.cpmplatform.common.constant.Constants;
import com.cpmplatform.common.core.domain.model.LoginUser;
import com.cpmplatform.framework.config.properties.CasProperties;
import com.cpmplatform.framework.web.service.TokenService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* @author daixin
* @version 1.0
* @description: TODO
* @date 2022/12/30 17:43
*/
@Service
public class CasAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
protected final Log logger = LogFactory.getLog(this.getClass());
private RequestCache requestCache = new HttpSessionRequestCache();
@Autowired
private TokenService tokenService;
@Autowired
private CasProperties casProperties;
// 令牌有效期(默认30分钟)
@Value("${token.expireTime}")
private int expireTime;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
String targetUrlParameter = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl()
|| (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
requestCache.removeRequest(request, response);
super.onAuthenticationSuccess(request, response, authentication);
return;
}
clearAuthenticationAttributes(request);
LoginUser userDetails = (LoginUser) authentication.getPrincipal();
String token = tokenService.createToken(userDetails);
//往Cookie中设置token
Cookie casCookie = new Cookie(Constants.WEB_TOKEN_KEY, token);
casCookie.setMaxAge(expireTime * 60);
response.addCookie(casCookie);
//设置后端认证成功标识
HttpSession httpSession = request.getSession();
httpSession.setAttribute(Constants.CAS_TOKEN, token);
//登录成功后跳转到前端登录页面,加了一个jwt参数
getRedirectStrategy().sendRedirect(request, response, casProperties.getWebUrl()+"?jwt="+token);
}
}
framework.config.properties下新增CasProperties.java类
package com.cpmplatform.framework.config.properties;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author daixin
* @version 1.0
* @description: TODO
* @date 2022/12/29 13:22
*/
@Data
@Component
public class CasProperties {
@Value("${cas.server.host.url}")
private String casServerUrl;
@Value("${cas.server.host.login_url}")
private String casServerLoginUrl;
@Value("${cas.server.host.logout_url}")
private String casServerLogoutUrl;
@Value("${app.casEnable}")
private boolean casEnable;
@Value("${app.server.host.url}")
private String appServerUrl;
@Value("${app.login_url}")
private String appLoginUrl;
@Value("${app.logout_url}")
private String appLogoutUrl;
@Value("${app.web_url}")
private String webUrl;
}
LoginUser.java类,修改getAuthorities()方法的返回return new HashSet();
修改Constants.java类,增加代码
/**
* CAS登录成功后的后台标识
*/
public static final String CAS_TOKEN = "cas_token";
/**
* CAS登录成功后的前台Cookie的Key
*/
public static final String WEB_TOKEN_KEY = "Admin-Token";
修改application.yml文件,增加配置
#CAS
cas:
server:
host:
#CAS服务地址
url: http://cpmp.fulongai.cn/cas
#CAS服务登录地址
login_url: ${cas.server.host.url}/login
#CAS服务登出地址
logout_url: ${cas.server.host.url}/logout?service=${app.server.host.url}
# 应用访问地址
app:
#开启cas
casEnable: false
server:
host:
#应用系统后端地址
url: http://cpmp.fulongai.cn/cpmp/prod-api
login_url: / #应用系统登录地址
logout_url: /logout #应用系统登出地址
#应用系统前端首页地址
web_url: http://cpmp.fulongai.cn/cpmp/applicationManagement
修改SecurityConfig.java类
package com.cpmplatform.framework.config;
import com.cpmplatform.framework.config.properties.CasProperties;
import com.cpmplatform.framework.security.filter.JwtAuthenticationTokenFilter;
import com.cpmplatform.framework.security.handle.AuthenticationEntryPointImpl;
import com.cpmplatform.framework.security.handle.CasAuthenticationSuccessHandler;
import com.cpmplatform.framework.security.handle.LogoutSuccessHandlerImpl;
import com.cpmplatform.framework.web.service.CasUserDetailsService;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.web.CasAuthenticationEntryPoint;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.web.filter.CorsFilter;
import com.cpmplatform.framework.config.properties.PermitAllUrlProperties;
/**
* spring security配置
*
* @author cpmplatform
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
private CasProperties casProperties;
@Autowired
private CasUserDetailsService customUserDetailsService;
@Autowired
private CasAuthenticationSuccessHandler casAuthenticationSuccessHandler;
/**
* 自定义用户认证逻辑
*/
@Autowired
private UserDetailsService userDetailsService;
/**
* 认证失败处理类
*/
@Autowired
private AuthenticationEntryPointImpl unauthorizedHandler;
/**
* 退出处理类
*/
@Autowired
private LogoutSuccessHandlerImpl logoutSuccessHandler;
/**
* token认证过滤器
*/
@Autowired
private JwtAuthenticationTokenFilter authenticationTokenFilter;
/**
* 跨域过滤器
*/
@Autowired
private CorsFilter corsFilter;
/**
* 允许匿名访问的地址
*/
@Autowired
private PermitAllUrlProperties permitAllUrl;
/**
* anyRequest | 匹配所有请求路径
* access | SpringEl表达式结果为true时可以访问
* anonymous | 匿名可以访问
* denyAll | 用户不能访问
* fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录)
* hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问
* hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问
* hasAuthority | 如果有参数,参数表示权限,则其权限可以访问
* hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
* hasRole | 如果有参数,参数表示角色,则其角色可以访问
* permitAll | 用户可以任意访问
* rememberMe | 允许通过remember-me登录的用户访问
* authenticated | 用户登录后可访问
*/
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
// 注解标记允许匿名访问的url
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());
if (!casProperties.isCasEnable()) {
httpSecurity
// CSRF禁用,因为不使用session
.csrf().disable()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 过滤请求
.authorizeRequests()
// 对于登录login 验证码captchaImage 允许匿名访问
.antMatchers("/login", "/captchaImage").anonymous()
.antMatchers(
HttpMethod.GET,
"/*.html",
"/**/*.html",
"/**/*.css",
"/**/*.js"
).permitAll()
.antMatchers("/profile/**").anonymous()
.antMatchers("/common/download**").anonymous()
.antMatchers("/common/download/resource**").anonymous()
.antMatchers("/swagger-ui.html").anonymous()
.antMatchers("/swagger-resources/**").anonymous()
.antMatchers("/webjars/**").anonymous()
.antMatchers("/*/api-docs").anonymous()
.antMatchers("/druid/**").anonymous()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated()
.and()
.headers().frameOptions().disable();
httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
// 添加JWT filter
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// 添加CORS filter
httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
}
//开启cas
if (casProperties.isCasEnable()) {
httpSecurity
// CSRF禁用,因为不使用session
.csrf().disable()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 过滤请求
.authorizeRequests()
// 对于登录login 验证码captchaImage 允许匿名访问
.antMatchers("/login","/logout", "/captchaImage").anonymous()
.antMatchers(
HttpMethod.GET,
"/*.html",
"/**/*.html",
"/**/*.css",
"/**/*.js"
).permitAll()
.antMatchers("/profile/**").anonymous()
.antMatchers("/common/download**").anonymous()
.antMatchers("/common/download/resource**").anonymous()
.antMatchers("/swagger-ui.html").anonymous()
.antMatchers("/swagger-resources/**").anonymous()
.antMatchers("/webjars/**").anonymous()
.antMatchers("/*/api-docs").anonymous()
.antMatchers("/druid/**").anonymous()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated()
.and()
.headers().frameOptions().disable();
//单点登录登出
httpSecurity.logout().permitAll().logoutSuccessHandler(logoutSuccessHandler);
// Custom JWT based security filter
httpSecurity.addFilter(casAuthenticationFilter())
.addFilterBefore(authenticationTokenFilter, CasAuthenticationFilter.class)
//.addFilterBefore(casLogoutFilter(), LogoutFilter.class)
.addFilterBefore(singleSignOutFilter(), CasAuthenticationFilter.class).exceptionHandling()
//认证失败
.authenticationEntryPoint(casAuthenticationEntryPoint());
// 添加CORS filter
httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
// disable page caching
httpSecurity.headers().cacheControl();
}
}
/**
* 强散列哈希加密实现
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder()
{
return new BCryptPasswordEncoder();
}
/**
* 解决 无法直接注入 AuthenticationManager
*
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 定义认证用户信息获取来源,密码校验规则等
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
if (!casProperties.isCasEnable()) {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
// cas
if (casProperties.isCasEnable()) {
super.configure(auth);
auth.authenticationProvider(casAuthenticationProvider());
}
}
/**
* 认证的入口
*/
@Bean
public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
casAuthenticationEntryPoint.setLoginUrl(casProperties.getCasServerLoginUrl());
casAuthenticationEntryPoint.setServiceProperties(serviceProperties());
return casAuthenticationEntryPoint;
}
/**
* 指定service相关信息
*/
@Bean
public ServiceProperties serviceProperties() {
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService(casProperties.getAppServerUrl() + casProperties.getAppLoginUrl());
serviceProperties.setAuthenticateAllArtifacts(true);
return serviceProperties;
}
/**
* CAS认证过滤器
*/
@Bean
public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
casAuthenticationFilter.setAuthenticationManager(authenticationManager());
casAuthenticationFilter.setFilterProcessesUrl(casProperties.getAppLoginUrl());
casAuthenticationFilter.setAuthenticationSuccessHandler(casAuthenticationSuccessHandler);
return casAuthenticationFilter;
}
/**
* cas 认证 Provider
*/
@Bean
public CasAuthenticationProvider casAuthenticationProvider() {
CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
casAuthenticationProvider.setAuthenticationUserDetailsService(customUserDetailsService);
casAuthenticationProvider.setServiceProperties(serviceProperties());
casAuthenticationProvider.setTicketValidator(cas20ServiceTicketValidator());
casAuthenticationProvider.setKey("casAuthenticationProviderKey");
return casAuthenticationProvider;
}
@Bean
public Cas20ServiceTicketValidator cas20ServiceTicketValidator() {
return new Cas20ServiceTicketValidator(casProperties.getCasServerUrl());
}
/**
* 单点登出过滤器
*/
@Bean
public SingleSignOutFilter singleSignOutFilter() {
SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
//singleSignOutFilter.setCasServerUrlPrefix(casProperties.getCasServerUrl());
singleSignOutFilter.setIgnoreInitConfiguration(true);
return singleSignOutFilter;
}
/**
* 请求单点退出过滤器
*/
@Bean
public LogoutFilter casLogoutFilter() {
LogoutFilter logoutFilter = new LogoutFilter(casProperties.getCasServerLogoutUrl(),
new SecurityContextLogoutHandler());
logoutFilter.setFilterProcessesUrl(casProperties.getAppLogoutUrl());
return logoutFilter;
}
}
六.自定义数据源
CAS应用到项目时,不可能一直使用默认账号登录,可连接项目数据库进行用户信息验证。
1.修改application.properties文件,添加数据源
cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver
cas.authn.jdbc.query[0].url=jdbc:mysql://192.168.2.156:3306/cpmplatform?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
cas.authn.jdbc.query[0].user=cpmp
cas.authn.jdbc.query[0].password=123456
cas.authn.jdbc.query[0].sql=select password from sys_user where user_name=?
cas.authn.jdbc.query[0].fieldPassword=password
cas.authn.jdbc.query[0].passwordEncoder.type=BCRYPT
cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
fieldPassword:数据库中密码字段列名
passwordEncoder.type:密码加密策略,支持NONE|DEFAULT|STANDARD|BCRYPT|SCRYPT|PBKDF2等多种配置,需与应用系统的加密策略一致,本项目使用的是强散列哈希加密,也就是BCRYPT。
sql:密码查询语句,项目在实际应用中还可以增加条件,用户删除标志、用户状态等。
2.注释掉application.properties文件最后一行的默认用户名密码
#cas.authn.accept.users=casuser::Mellon
3.重启tomcat,使用系统用户登录,可登陆成功。
七.跨域问题
集成CAS过程中遇到的跨域问题,以及单点登录内嵌页面时的复杂跨域问题,单独整理了一篇,目前遇到的CORS报错都能解决:
https://blog.csdn.net/secretdaixin/article/details/129240863?spm=1001.2014.3001.5501
八.自定义登录页
本例中主题名称为cpmp,以下代码将以cpmp命名进行讲解。
classes/static/themes目录下新建以主题名称命名的文件夹用于存放静态文件,css、js、图片等。
classes目录下创建以主题名称命名的properties文件,本例文件名为cpmp.properties,用于配置主题所引用的资源的路径。前2~4行默认主题样式不能丢,否则页面会丢样式。下面的配置是步骤1创建的目录及文件。等号前的变量名称自定义即可,此变量会在步骤3创建的登录页面中引用。
# 系统默认主题样式
cas.standard.css.file = /css/cas.css
cas.javascript.file = /js/cas.js
cas.admin.css.file = /css/admin.css
# 自定义JS
login.js.element-ui = /themes/cpmp/js/element-ui.js
login.js.jquery = /themes/cpmp/js/jquery.min.js
login.js.cas = /themes/cpmp/js/cas.js
# 自定义CSS
login.css.element-ui = /themes/cpmp/css/element-ui.css
index.css.style = /themes/cpmp/css/cas.css
# 其它
login.img.path = /themes/cpmp/img
cas.page.title = cpmp
classes/templates目录下新建以主题名称命名的文件夹用于存放登录页面,登录页面是一个固定名称的html文件,必须以casLoginView.html命名。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" th:href="@{${#themes.code('index.css.style')}}"/>
<link rel="stylesheet" th:href="@{${#themes.code('login.css.elementui')}}"/>
</head>
<body>
<!-- 第一套, 无th -->
<div class="container">
<div class="login">
<h3>C A S 登录</h3>
<form id="postForm" autocomplete="off" method="post" th:object="${credential}">
<input type="hidden" name="execution" th:value="${flowExecutionKey}"/>
<input type="hidden" name="_eventId" value="submit"/>
<input type="hidden" name="geolocation"/>
<i class="iconfont icon-man"></i>
<input
id="username"
size="25"
v-model.trim="username"
tabindex="1"
placeholder="请输入用户名"
type="text"
th:disabled="${guaEnabled}"
th:field="*{username}"
th:accesskey="#{screen.welcome.label.netid.accesskey}"
autocomplete="off"
/>
<i class="iconfont icon-suo"></i>
<input
type="password"
id="password"
size="25"
tabindex="2"
placeholder="请输入密码"
th:accesskey="#{screen.welcome.label.password.accesskey}"
th:field="*{password}"
autocomplete="off"
/>
<div th:if="${#fields.hasErrors('*')}">
<span th:each="err : ${#fields.errors('*')}" th:utext="${err}" ></span>
</div>
<input
name="submit"
accesskey="l"
th:value="#{screen.welcome.button.login}"
type="submit"
value="登录"
/>
</form>
</div>
</div>
</body>
</html>
在classes/service目录下的服务注册文件中,为每个服务指定主题,以模板中默认的服务注册文件为例,也可以是自建的服务注册文件。
修改classes目录下的application.properties文件,增加代码用于指定默认主题文章来源:https://www.toymoban.com/news/detail-781690.html
# 默认主题
cas.theme.defaultThemeName=cpmp
将文件发布到tomcat,重启tomcat,访问登录页面,主题配置成功。文章来源地址https://www.toymoban.com/news/detail-781690.html
到了这里,关于若依前后端分离项目集成CAS 5.3实现单点登录的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!