1. 下载地址 https://github.com/apereo/cas/archive/v4.2.7.zip
2. 解压后, 用intellj idea 打开
3. 执行 gradle build -x test ,打包编译
4. 取消https,能够支持直接http
cas.properties 修改两个地方
# Decides whether SSO cookie should be created only under secure connections. tgc.secure=false # The expiration value of the SSO cookie # tgc.maxAge=-1 # The name of the SSO cookie # tgc.name=TGC # The path to which the SSO cookie will be scoped # tgc.path=/cas # The expiration value of the SSO cookie for long-term authentications # tgc.remember.me.maxAge=1209600 # Decides whether SSO Warning cookie should be created only under secure connections. warn.cookie.secure=false
casLoginView.jsp
<jsp:directive.include file="includes/top.jsp" /> <%--<c:if test="${not pageContext.request.secure}"> <div id="msg" class="errors"> <h2><spring:message code="screen.nonsecure.title" /></h2> <p><spring:message code="screen.nonsecure.message" /></p> </div> </c:if>--%>
注销上面的代码
HTTPSandIMAPS-10000001.json 中增加http的service
{ "@class" : "org.jasig.cas.services.RegexRegisteredService", "serviceId" : "^(https|imaps|http)://.*", "name" : "HTTPS and IMAPS", "id" : 10000001, "description" : "This service definition authorized all application urls that support HTTPS and IMAPS protocols.", "proxyPolicy" : { "@class" : "org.jasig.cas.services.RefuseRegisteredServiceProxyPolicy" }, "evaluationOrder" : 10000, "usernameAttributeProvider" : { "@class" : "org.jasig.cas.services.DefaultRegisteredServiceUsernameProvider" }, "logoutType" : "BACK_CHANNEL", "attributeReleasePolicy" : { "@class" : "org.jasig.cas.services.ReturnAllowedAttributeReleasePolicy", "principalAttributesRepository" : { "@class" : "org.jasig.cas.authentication.principal.DefaultPrincipalAttributesRepository" }, "authorizedToReleaseCredentialPassword" : false, "authorizedToReleaseProxyGrantingTicket" : false }, "accessStrategy" : { "@class" : "org.jasig.cas.services.DefaultRegisteredServiceAccessStrategy", "enabled" : true, "ssoEnabled" : true } }
5. 修改cas的认证方式,包括jdbc、 自定义密码加密等
5.1 修改 cas-server-webapp 的依赖,修改 该工程下build.gradle
5.2 修改cas.properties
cas.jdbc.authn.query.sql=SELECT username,pwd,salt,`status` FROM sys_user WHERE username = ? cas.jdbc.authn.search.password=root cas.jdbc.authn.search.user=root
5.3 自定义密码加密 CustomEncoder
package org.jasig.cas.adaptors.jdbc; import org.apache.shiro.crypto.RandomNumberGenerator; import org.apache.shiro.crypto.SecureRandomNumberGenerator; import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Value; /** * Created by 肖建锋 on 2017/3/25. */ public class CustomEncoder { /** 随机字符生产工具 */ private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator(); /** 加密方式 */ private String algorithmName = "sha"; /** 多重加密次数 */ private int hashIterations = 2; /** * 配置随机字符生产工具 * @param randomNumberGenerator * * @author xiaojf 2016-01-07 01:46 */ public void setRandomNumberGenerator(RandomNumberGenerator randomNumberGenerator) { this.randomNumberGenerator = randomNumberGenerator; } /** * 配置加密方式 * @param algorithmName * * @author xiaojf 2016-01-07 01:46 */ public void setAlgorithmName(String algorithmName) { this.algorithmName = algorithmName; } /** * 配置重复加密次数 * @param hashIterations * * @author xiaojf 2016-01-07 01:46 */ public void setHashIterations(int hashIterations) { this.hashIterations = hashIterations; } /** * 密码加密 * @param pwd * @param salt * @author xiaojf 2016-01-07 01:46 */ public String encryptPassword(String pwd,String salt) { return new SimpleHash(algorithmName, pwd, ByteSource.Util.bytes(salt), hashIterations).toHex(); } /** * 根据私钥加密 * @param value 要加密字段 * @param salt 密钥 * @author hp * @return 加密后字段 * 2016-05-07 01:46 */ public String encrypt(String value , String salt) { return new SimpleHash(algorithmName, value, ByteSource.Util.bytes(salt), hashIterations).toHex(); } /** * 获取加密后的新密码 * * @param pwd 密码 * @param salt 盐 * @return 新密码 * @author xiaojf 2016-5-7 15:37:54 */ public String getEncryptPassword(String pwd,String salt){ String newPassword = new SimpleHash(algorithmName, pwd, ByteSource.Util.bytes(salt), hashIterations).toHex(); return newPassword; } /** * @param args the input arguments * @author xiaojf 2016-5-7 15:37:54 */ public static void main(String[] args) { /*SysUser sysUser = new SysUser(); sysUser.setUsername("super"); sysUser.setPwd("admin");*/ String s = new CustomEncoder().encrypt("super","e910c85b7f5c5e789d50fafcfa5d4efc"); System.out.println(s); } }
5.4 自定义用户认证方式
package org.jasig.cas.adaptors.jdbc; import org.apache.commons.lang3.StringUtils; import org.jasig.cas.authentication.HandlerResult; import org.jasig.cas.authentication.PreventedException; import org.jasig.cas.authentication.UsernamePasswordCredential; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.dao.DataAccessException; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.jdbc.support.rowset.SqlRowSet; import org.springframework.stereotype.Component; import javax.security.auth.login.AccountNotFoundException; import javax.security.auth.login.FailedLoginException; import javax.sql.DataSource; import javax.validation.constraints.NotNull; import java.security.GeneralSecurityException; import java.util.Map; /** * Class that if provided a query that returns a password (parameter of query * must be username) will compare that password to a translated version of the * password provided by the user. If they match, then authentication succeeds. * Default password translator is plaintext translator. * * @author Scott Battaglia * @author Dmitriy Kopylenko * @author Marvin S. Addison * * @since 3.0.0 */ @Component("queryDatabaseAuthenticationHandler2") public class QueryDatabaseAuthenticationHandler2 extends AbstractJdbcUsernamePasswordAuthenticationHandler { @NotNull private String sql; @Override protected final HandlerResult authenticateUsernamePasswordInternal(final UsernamePasswordCredential credential) throws GeneralSecurityException, PreventedException { if (StringUtils.isBlank(this.sql) || getJdbcTemplate() == null) { throw new GeneralSecurityException("Authentication handler is not configured correctly"); } final String username = credential.getUsername(); try { Map<String, Object> map = getJdbcTemplate().queryForMap(sql, username); final String dbPassword = map.get("pwd")+""; String salt = map.get("salt")+""; int status = Integer.parseInt(map.get("status")+""); final String encryptedPassword = new CustomEncoder().encryptPassword(credential.getPassword(),salt); if (!dbPassword.equals(encryptedPassword)) { throw new FailedLoginException("Password does not match value on record."); } } catch (final IncorrectResultSizeDataAccessException e) { if (e.getActualSize() == 0) { throw new AccountNotFoundException(username + " not found with SQL query"); } else { throw new FailedLoginException("Multiple records found for " + username); } } catch (final DataAccessException e) { throw new PreventedException("SQL exception while executing query for " + username, e); } return createHandlerResult(credential, this.principalFactory.createPrincipal(username), null); } /** * @param sql The sql to set. */ @Autowired public void setSql(@Value("${cas.jdbc.authn.query.sql:}") final String sql) { this.sql = sql; } @Override @Autowired(required = false) public void setDataSource(@Qualifier("queryDatabaseDataSource") final DataSource dataSource) { super.setDataSource(dataSource); } }
6. 自定义数据源
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xmlns:sec="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!-- 设置druid数据源 --> <bean id="queryDatabaseDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="jdbc:mysql://localhost:3306/tomorrow?characterEncoding=utf-8" /> <property name="username" value="root" /> <property name="password" value="root" /> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> </bean> </beans>
7. 修改 cas默认的用户校验方式,注入我们上面定义的规则 deployerConfigContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xmlns:sec="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <util:map id="authenticationHandlersResolvers"> <entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" /> <entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" /> </util:map> <util:list id="authenticationMetadataPopulators"> <ref bean="successfulHandlerMetaDataPopulator" /> <ref bean="rememberMeAuthenticationMetaDataPopulator" /> </util:list> <bean id="attributeRepository" class="org.jasig.services.persondir.support.NamedStubPersonAttributeDao" p:backingMap-ref="attrRepoBackingMap" /> <alias name="queryDatabaseAuthenticationHandler2" alias="primaryAuthenticationHandler" /> <alias name="personDirectoryPrincipalResolver" alias="primaryPrincipalResolver" /> <util:map id="attrRepoBackingMap"> <entry key="uid" value="uid" /> <entry key="eduPersonAffiliation" value="eduPersonAffiliation" /> <entry key="groupMembership" value="groupMembership" /> <entry> <key><value>memberOf</value></key> <list> <value>faculty</value> <value>staff</value> <value>org</value> </list> </entry> </util:map> <alias name="serviceThemeResolver" alias="themeResolver" /> <alias name="jsonServiceRegistryDao" alias="serviceRegistryDao" /> <alias name="defaultTicketRegistry" alias="ticketRegistry" /> <alias name="ticketGrantingTicketExpirationPolicy" alias="grantingTicketExpirationPolicy" /> <alias name="multiTimeUseOrTimeoutExpirationPolicy" alias="serviceTicketExpirationPolicy" /> <alias name="anyAuthenticationPolicy" alias="authenticationPolicy" /> <alias name="acceptAnyAuthenticationPolicyFactory" alias="authenticationPolicyFactory" /> <bean id="auditTrailManager" class="org.jasig.inspektr.audit.support.Slf4jLoggingAuditTrailManager" p:entrySeparator="${cas.audit.singleline.separator:|}" p:useSingleLine="${cas.audit.singleline:false}"/> <alias name="neverThrottle" alias="authenticationThrottle" /> <util:list id="monitorsList"> <ref bean="memoryMonitor" /> <ref bean="sessionMonitor" /> </util:list> <alias name="defaultPrincipalFactory" alias="principalFactory" /> <alias name="defaultAuthenticationTransactionManager" alias="authenticationTransactionManager" /> <alias name="defaultPrincipalElectionStrategy" alias="principalElectionStrategy" /> <alias name="tgcCipherExecutor" alias="defaultCookieCipherExecutor" /> </beans>
8. 打war包,复制到tomcat下启动,或者直接用idea运行
到这里cas安装修改完成了,接下来是修改springboot+shiro + cas(重点部分,已经用红色标出)
9. 加入shiro-cas依赖
compile ("org.apache.shiro:shiro-cas:1.3.2")
10. 修改 ShiroConfiguration.java 自定义cas filter
@Bean public CasFilter casFilter() { CasFilter casFilter = new CasFilter(); casFilter.setFailureUrl("/error/403"); casFilter.setSuccessUrl("/"); return casFilter; }
11. 自定义 cas realm
@Bean public MyCasRealm myCasRealm(RetryLimitHashedCredentialsMatcher credentialsMatcher) { MyCasRealm casRealm = new MyCasRealm(); casRealm.setCachingEnabled(true); casRealm.setCasServerUrlPrefix("http://localhost:8080/cas"); casRealm.setCasService("http://localhost:8000/shiro-cas"); return casRealm; }
12. 自定义多点登出逻辑,不能使用shiro自带的logout filter
package cn.xiaojf.today.sys.web.controller; import cn.xiaojf.today.base.constant.SystemConstant; import cn.xiaojf.today.base.model.CommonResult; import cn.xiaojf.today.base.exception.BusinessException; import cn.xiaojf.today.base.web.controller.BaseController; import cn.xiaojf.today.log.OperationType; import cn.xiaojf.today.log.annotation.OperateLog; import cn.xiaojf.today.sys.entity.SysUser; import cn.xiaojf.today.sys.service.SysUserService; import org.apache.commons.lang3.StringUtils; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.session.HttpServletSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpSession; import java.util.Set; /** * @author xiaojf 2017/2/9 15:48. */ @RestController @RequestMapping("/login") public class LoginController { @Autowired private SysUserService userService; Logger logger = LoggerFactory.getLogger(LoginController.class); @RequestMapping("index") public ModelAndView index() { ModelAndView mv = new ModelAndView("/login"); return mv; } @RequestMapping("out") public ModelAndView out() { SecurityUtils.getSubject().logout(); ModelAndView view = new ModelAndView(); view.setViewName("redirect:http://localhost:8080/cas/logout"); return view; } @RequestMapping("auth") @OperateLog(module = OperationType.LOGIN) public ModelAndView auth(String username, String password, boolean rememberMe, HttpSession session) { ModelAndView mv = new ModelAndView("/"); try { Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe); subject.login(token); SysUser user = userService.getByUsername(username); //在session中保存当前用户的个人信息 session.setAttribute(SystemConstant.SYS_CURRENT_USER, user); //在session中保存用户的r_path Set<String> rpathSet = userService.loadUserRPath(user.getId()); String rpath = StringUtils.join(rpathSet, SystemConstant.DATA_SPLIT_CHAR); session.setAttribute(SystemConstant.SYS_CURRENT_USER_RPATH, rpath); } catch (IllegalArgumentException e) { mv.setViewName("/login/index"); mv.addObject("errMsg","参数异常"); logger.error(e.getMessage(), e.getStackTrace()); } catch (AuthenticationException e) { logger.error(e.getMessage(), e.getStackTrace()); mv.setViewName("/login/index"); mv.addObject("errMsg","认证失败"); } catch (BusinessException e) { mv.setViewName("/login/index"); mv.addObject("errMsg","登录异常"); logger.error(e.getMessage(), e.getStackTrace()); } catch (Exception e) { mv.setViewName("/login/index"); mv.addObject("errMsg","登录异常"); logger.error(e.getMessage(), e.getStackTrace()); } return mv; } }
13. 完整的 ShiroConfiguration
package cn.xiaojf.today.shiro.configuration; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import cn.xiaojf.today.sys.security.credentials.RetryLimitHashedCredentialsMatcher; import cn.xiaojf.today.sys.security.filter.RoleAuthorizationFilter; import cn.xiaojf.today.sys.security.realm.MyCasRealm; import cn.xiaojf.today.sys.security.realm.UsernameRealm; import cn.xiaojf.today.sys.service.SysResService; import org.apache.shiro.cas.CasFilter; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.filter.authc.LogoutFilter; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.filter.DelegatingFilterProxy; import javax.servlet.Filter; import java.util.LinkedHashMap; import java.util.Map; /** * shiro配置 * @author xiaojf 2017/2/10 11:30. */ @Configuration public class ShiroConfiguration { @Bean public FilterRegistrationBean filterRegistrationBean() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new DelegatingFilterProxy("shiroFilter")); filterRegistrationBean.addInitParameter("targetFilterLifecycle", "true"); filterRegistrationBean.setEnabled(true); filterRegistrationBean.addUrlPatterns("/*"); return filterRegistrationBean; } @Bean public RetryLimitHashedCredentialsMatcher credentialsMatcher() { RetryLimitHashedCredentialsMatcher credentialsMatcher = new RetryLimitHashedCredentialsMatcher(); credentialsMatcher.setHashAlgorithmName("sha"); credentialsMatcher.setHashIterations(2); credentialsMatcher.setStoredCredentialsHexEncoded(true); credentialsMatcher.setRetryCount(5); credentialsMatcher.setRetryTime(1800000); return credentialsMatcher; } @Bean public UsernameRealm usernameRealm(RetryLimitHashedCredentialsMatcher credentialsMatcher) { UsernameRealm usernameRealm = new UsernameRealm(); usernameRealm.setCredentialsMatcher(credentialsMatcher); usernameRealm.setCachingEnabled(true); return usernameRealm; } @Bean public MyCasRealm myCasRealm(RetryLimitHashedCredentialsMatcher credentialsMatcher) { MyCasRealm casRealm = new MyCasRealm(); // casRealm.setCredentialsMatcher(credentialsMatcher); casRealm.setCachingEnabled(true); casRealm.setCasServerUrlPrefix("http://localhost:8080/cas"); casRealm.setCasService("http://localhost:8000/shiro-cas"); return casRealm; } @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator(); daap.setProxyTargetClass(true); return daap; } /*@Bean public DefaultWebSecurityManager securityManager(UsernameRealm usernameRealm) { DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager(); dwsm.setRealm(usernameRealm); return dwsm; }*/ @Bean public DefaultWebSecurityManager securityManager(MyCasRealm myCasRealm) { DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager(); dwsm.setRealm(myCasRealm); return dwsm; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager defaultWebSecurityManager) { AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor(); aasa.setSecurityManager(defaultWebSecurityManager); return aasa; } @Bean public CasFilter casFilter() { CasFilter casFilter = new CasFilter(); casFilter.setFailureUrl("/error/403"); casFilter.setSuccessUrl("/"); return casFilter; } @Bean(name = "shiroFilter") public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager, CasFilter casFilter,ApplicationContext context) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); shiroFilterFactoryBean.setLoginUrl("http://localhost:8080/cas/login?service=http://localhost:8000/shiro-cas"); shiroFilterFactoryBean.setUnauthorizedUrl("http://localhost:8080/cas/login?service=http://localhost:8000/shiro-cas"); Map<String, Filter> filters = new LinkedHashMap<>(); filters.put("role", new RoleAuthorizationFilter()); filters.put("cas",casFilter); shiroFilterFactoryBean.getFilters().putAll(filters); SysResService resService = context.getBean(SysResService.class); loadShiroFilterChain(shiroFilterFactoryBean,resService); return shiroFilterFactoryBean; } private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean,SysResService resService) { Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); // filterChainDefinitionMap.put("/login/index", "anon"); filterChainDefinitionMap.put("/error/403", "anon"); filterChainDefinitionMap.put("/error/404", "anon"); filterChainDefinitionMap.put("/error/500", "anon"); // filterChainDefinitionMap.put("/login/auth", "anon"); filterChainDefinitionMap.put("login/out", "authc"); filterChainDefinitionMap.put("/plugins/**", "anon"); filterChainDefinitionMap.put("/css/**", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/images/**", "anon"); filterChainDefinitionMap.put("/shiro-cas", "cas"); filterChainDefinitionMap = resService.loadFilterChainDefinitions(filterChainDefinitionMap); filterChainDefinitionMap.put("/**", "role[ROLE_SUPER]"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); } @Bean public ShiroDialect shiroDialect() { return new ShiroDialect(); } }
14. 修改完成以后, 直接访问自己的项目会自动的跳转到cas的登陆页面,登陆完成后会跳转到自己项目的首页,注意主要是下面这句话的效果
shiroFilterFactoryBean.setLoginUrl("http://localhost:8080/cas/login?service=http://localhost:8000/shiro-cas");
首先登陆的请求被转发到
http://localhost:8080/cas/login
然后登陆成功会返回ticket,并重定向到
http://localhost:8000/shiro-cas
shiro-cas 会通过cas filter 拦截到shiro-cas,然后根据返回的username 对用户进行授权
还有一点需要注意的时候 , cas 默认接受的参数名称是 username, password ,可以在cas.properties 中修改默认, 下面是我的登陆表单代码,仅供参考
<form action="/login/auth" method="post"> <input id="username" type="text" name="username" class="username" placeholder="用户名"> <input id="pwd" type="password" name="pwd" class="password" placeholder="密码"> <button type="submit">登 录</button> </form>