若依前后端分离项目集成CAS 5.3实现单点登录

一.获取CAS5.3项目资源

GitHub - apereo/cas-overlay-template at 5.3

cas5.3.x还是基于jdk8运行,下一个版本6.0.x就基于jdk9了,随着cas版本升级要求jdk版本也越来越高,官网上和github上都有每个版本基本运行条件的说明,根据实际情况选择版本。

二.tomcat部署

  1. 利用maven-package打war包

2.将war包放到tomcat的webapps目录下,启动tomcat

三.支持HTTP协议

  1. 修改tomcat/webapps/cas/WEB-INF/classes/services/HTTPSandIMAPS-10000001.json文件,添加http支持,修改后代码如下。

  1. 修改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
  1. 重启tomcat,访问登录页进行登录

cas默认密码在application.properties文件的最后一行

cas.authn.accept.users=casuser::Mellon

四.前端代码

  1. 修改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(() => {});
    }
  1. 修改permission.js文件,复制代码粘贴到红框位置,适当修改地址,注意导包。

      if (!defaultSettings.casEnable) {
        next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
      }
      // 开启cas
      if (defaultSettings.casEnable) {
        window.location.href = defaultSettings.casloginUrl // 否则全部重定向到登录页
      }
  1. 修改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'
  1. 修改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)
        })
      })
    }
  1. 修改request.js文件,注释掉红框中代码

五.后端代码

  1. 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);
    }
}
  1. 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);
    }
}
  1. 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;

}
  1. LoginUser.java类,修改getAuthorities()方法的返回return new HashSet();

  1. 修改Constants.java类,增加代码

    /**
     * CAS登录成功后的后台标识
     */
    public static final String CAS_TOKEN = "cas_token";

    /**
     * CAS登录成功后的前台Cookie的Key
     */
    public static final String WEB_TOKEN_KEY = "Admin-Token";
  1. 修改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
  1. 修改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命名进行讲解。

  1. classes/static/themes目录下新建以主题名称命名的文件夹用于存放静态文件,css、js、图片等。

  1. 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
  1. 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>
  1. 在classes/service目录下的服务注册文件中,为每个服务指定主题,以模板中默认的服务注册文件为例,也可以是自建的服务注册文件。

  1. 修改classes目录下的application.properties文件,增加代码用于指定默认主题

# 默认主题
cas.theme.defaultThemeName=cpmp
  1. 将文件发布到tomcat,重启tomcat,访问登录页面,主题配置成功。

猜你喜欢

转载自blog.csdn.net/secretdaixin/article/details/129690564