以命令方式换到目录%TOMCAT_HOME%,在command命令行输入以下命令:
keytool -genkey -alias tomcat_key -keyalg RSA -storepass changeit -keystore server.keystore -validity 3600
用户名输入域名,如localhost(开发或测试用)或 hostname.domainname(用户拥有的域名),其它所有以Enter跳过,最后确认,此时会在%TOMCAT_HOME%下生成server.keystore文件。html
这步对于Tomcat的SSL配置不是必须,但对于CAS SSO是必须的,不然会出现以下错误:
edu.yale.its.tp.cas.client.CASAuthenticationException: Unable to validate ProxyTicketValidate.
导入过程分2步,第一步是导出证书,第二步是导入到证书信任库,命令以下:
keytool -export -trustcacerts -alias tomcat_key -file server.cer -keystore server.keystore -storepass changeit
keytool -import -trustcacerts -alias tomcat_key -file server.cer -keystore D:/”Program Files”/Java/jdk1.8.0_60/jre/lib/security/cacerts -storepass changeit
若是有提示,输入Y就能够了。
其它有用keytool命令(列出信任证书库中全部已有证书,删除库中某个证书):
keytool -list -keystore %JAVA_HOME%/jre/lib/security/cacerts >t.txt
keytool -delete -trustcacerts -alias tomcat_key -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit
注意:CAS 建议不要使用 IP 地址,而要使用机器名或域名。java
在Tomcat的server.xml配置文件中加入: mysql
CAS Server 负责完成对用户的认证工做,它会处理登陆时的用户凭证 (Credentials) 信息,用户名/密码对是最多见的凭证信息。CAS Server 可能须要到数据库检索一条用户账号信息,也可能在 XML 文件中检索用户名/密码,还可能经过 LDAP Server 获取等,在这种状况下,CAS 提供了一种灵活但统一的接口和实现分离的方式,实际使用中 CAS 采用哪一种方式认证是与 CAS 的基本协议分离开的,用户能够根据认证的接口去定制和扩展。git
CAS 提供扩展认证的核心是 AuthenticationHandler 接口,该接口定义如清单 1 下:github
清单 1. AuthenticationHandler定义web
public interface AuthenticationHandler {
/**
* Method to determine if the credentials supplied are valid. * @param credentials The credentials to validate. * @return true if valid, return false otherwise. * @throws AuthenticationException An AuthenticationException can contain * details about why a particular authentication request failed. */ boolean authenticate(Credentials credentials) throws AuthenticationException; /** * Method to check if the handler knows how to handle the credentials * provided. It may be a simple check of the Credentials class or something * more complicated such as scanning the information contained in the * Credentials object. * @param credentials The credentials to check. * @return true if the handler supports the Credentials, false othewrise. */ boolean supports(Credentials credentials); }
该接口定义了 2 个须要实现的方法,supports ()方法用于检查所给的包含认证信息的Credentials 是否受当前 AuthenticationHandler 支持;而 authenticate() 方法则担当验证认证信息的任务,这也是须要扩展的主要方法,根据状况与存储合法认证信息的介质进行交互,返回 boolean 类型的值,true 表示验证经过,false 表示验证失败。spring
CAS中还提供了对 AuthenticationHandler 接口的一些抽象实现,好比,可能须要在执行authenticate() 方法先后执行某些其余操做,那么可让本身的认证类扩展自清单 2 中的抽象类:sql
清单 2. AbstractPreAndPostProcessingAuthenticationHandler定义数据库
public abstract class AbstractPreAndPostProcessingAuthenticationHandler implements AuthenticateHandler{ protected Log log = LogFactory.getLog(this.getClass()); protected boolean preAuthenticate(final Credentials credentials) { return true; } protected boolean postAuthenticate(final Credentials credentials, final boolean authenticated) { return authenticated; } public final boolean authenticate(final Credentials credentials) throws AuthenticationException { if (!preAuthenticate(credentials)) { return false; } final boolean authenticated = doAuthentication(credentials); return postAuthenticate(credentials, authenticated); } protected abstract boolean doAuthentication(final Credentials credentials) throws AuthenticationException; }
AbstractPreAndPostProcessingAuthenticationHandler 类新定义了 preAuthenticate() 方法和 postAuthenticate() 方法,而实际的认证工做交由 doAuthentication() 方法来执行。所以,若是须要在认证先后执行一些额外的操做,能够分别扩展 preAuthenticate()和 ppstAuthenticate() 方法,而 doAuthentication() 取代 authenticate() 成为了子类必需要实现的方法。apache
因为实际运用中,最经常使用的是用户名和密码方式的认证,CAS 提供了针对该方式的实现,如清单 3 所示:
清单 3. AbstractUsernamePasswordAuthenticationHandler 定义
public abstract class AbstractUsernamePasswordAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler{ ... protected final boolean doAuthentication(final Credentials credentials) throws AuthenticationException { return authenticateUsernamePasswordInternal((UsernamePasswordCredentials) credentials); } protected abstract boolean authenticateUsernamePasswordInternal( final UsernamePasswordCredentials credentials) throws AuthenticationException; protected final PasswordEncoder getPasswordEncoder() { return this.passwordEncoder; } public final void setPasswordEncoder(final PasswordEncoder passwordEncoder) { this.passwordEncoder = passwordEncoder; } ... }
基于用户名密码的认证方式可直接扩展自 AbstractUsernamePasswordAuthenticationHandler,验证用户名密码的具体操做经过实现 authenticateUsernamePasswordInternal() 方法达到,另外,一般状况下密码会是加密过的,setPasswordEncoder() 方法就是用于指定适当的加密器。
从以上清单中能够看到,doAuthentication() 方法的参数是 Credentials 类型,这是包含用户认证信息的一个接口,对于用户名密码类型的认证信息,能够直接使用 UsernamePasswordCredentials,若是须要扩展其余类型的认证信息,须要实现Credentials接口,而且实现相应的 CredentialsToPrincipalResolver 接口,其具体方法能够借鉴 UsernamePasswordCredentials 和 UsernamePasswordCredentialsToPrincipalResolver。
JDBC 认证方法
用户的认证信息一般保存在数据库中,所以本文就选用这种状况来介绍。将前面下载的 cas-server-{version}-release.zip 包解开后,在 modules 目录下能够找到包 cas-server-support-jdbc-{version}.jar,其提供了经过 JDBC 链接数据库进行验证的缺省实现,基于该包的支持,咱们只须要作一些配置工做便可实现 JDBC 认证。
JDBC 认证方法支持多种数据库,DB2, Oracle, MySQL, Microsoft SQL Server 等都可,这里以 DB2 做为例子介绍。而且假设DB2数据库名: CASTest,数据库登陆用户名: db2user,数据库登陆密码: db2password,用户信息表为: userTable,该表包含用户名和密码的两个数据项分别为 userName 和 password。
打开文件 cas/WEB-INF/deployerConfigContext.xml,添加一个新的 bean 标签,以mysql为例,内容如清单 4 所示:
<!-- Data source definition --> <bean id="casDataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://localhost:3306/test</value> </property> <property name="username"> <value>sites</value> </property> <property name="password"> <value>123456</value> </property> </bean>
其中 casDataSource 在后面配置 AuthenticationHandler 会被引用,添加数据库驱动程序、链接地址、数据库登陆用户名以及登陆密码。
CAS 4.0.0 提供了 3 个基于 JDBC 的 AuthenticationHandler,分别为 BindModeSearchDatabaseAuthenticationHandler, QueryDatabaseAuthenticationHandler, SearchModeSearchDatabaseAuthenticationHandler。
- BindModeSearchDatabaseAuthenticationHandler 是用所给的用户名和密码去创建数据库链接,根据链接创建是否成功来判断验证成功与否;
- QueryDatabaseAuthenticationHandler 经过配置一个 SQL 语句查出密码,与所给密码匹配;
- SearchModeSearchDatabaseAuthenticationHandler 经过配置存放用户验证信息的表、用户名字段和密码字段,构造查询语句来验证。
使用哪一个 AuthenticationHandler,须要在 deployerConfigContext.xml 中设置,默认状况下,CAS 4.0.0 使用 AcceptUsersAuthenticationHandler(上面已经提到,须要在 deployerConfigContext.xml 中查看)。
在 deployerConfigContext.xml 文件中能够找到以下配置:
<bean id="primaryAuthenticationHandler" class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler"> <property name="users"> <map> <entry key="casuser" value="Mellon"/> </map> </property> </bean>
咱们能够将其注释掉,换成咱们但愿的一个 AuthenticationHandler,好比,使用QueryDatabaseAuthenticationHandler 或 SearchModeSearchDatabaseAuthenticationHandler 能够分别选取清单 5 或清单 6 的配置。
清单 5. 使用 QueryDatabaseAuthenticationHandler
<bean id="primaryAuthenticationHandler" class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"> <property name="dataSource" ref="casDataSource " /> <property name="sql" value="select password from userTable where lower(userName) = lower(?)" /> <!-- 指定密码加密器(可选) --> <property name="passwordEncoder" ref="passwordEncoder" /> </bean> <!-- 密码加密器(能够指定本身实现的加密器) --> <bean id="passwordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder" > <constructor-arg name="encodingAlgorithm" value="MD5"/> <property name="characterEncoding" value="UTF-8"/> </bean>
清单 6. 使用 SearchModeSearchDatabaseAuthenticationHandler
<bean id="SearchModeSearchDatabaseAuthenticationHandler" class="org.jasig.cas.adaptors.jdbc.SearchModeSearchDatabaseAuthenticationHandler" abstract="false" singleton="true" lazy-init="default" autowire="default" dependency-check="default"> <property name="tableUsers"> <value>userTable</value> </property> <property name="fieldUser"> <value>userName</value> </property> <property name="fieldPassword"> <value>password</value> </property> <property name="dataSource" ref="casDataSource " /> </bean>
另外,因为存放在数据库中的密码一般是加密过的,因此 AuthenticationHandler 在匹配时须要知道使用的加密方法,在 deployerConfigContext.xml 文件中咱们能够为具体的 AuthenticationHandler 类配置一个 property,指定加密器类,好比对于 QueryDatabaseAuthenticationHandler,能够修改如清单7所示:
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"> <property name="dataSource" ref=" casDataSource " /> <property name="sql" value="select password from userTable where lower(userName) = lower(?)" /> <property name="passwordEncoder" ref="myPasswordEncoder"/> </bean>
其中 myPasswordEncoder 是对清单 8 中设置的实际加密器类的引用:
<bean id="passwordEncoder" class="org.jasig.cas.authentication.handler.MyPasswordEncoder"/>
这里 MyPasswordEncoder 是根据实际状况本身定义的加密器,实现 PasswordEncoder 接口及其 encode() 方法。
注意:DataSource 依赖于 commons-collections-3.2.jar、commons-dbcp-1.2.1.jar、commons-pool-1.3.jar、数据库驱动包、cas对jdbc的支持包cas-server-support-jdbc-4.0.0.jar,须要找到这几个jar包放进 %TOMCAT_HOME%/webapps/cas/WEB-INF/lib 目录。
而后即可以启动Tomcat使用数据库中的用户名和密码进行登陆测试了。
最直接的方法,下载官方源码,直接修改 ^_^
我想你们颇有必要具有“任何开源项目均可以本身动手,使用经过源码构建项目并运行”的能力,万一你须要在源码上加点东西呢。
CAS 4.0 默认的页面存放在 cas/WEB-INF/view/jsp/default 下面,若是是源码项目页面存放在 cas-server-webapp\src\main\webapp\WEB-INF\view\jsp\default\ui 目录中。
咱们来看一下 cas\WEB-INF\classes\default_views.properties 文件,若是是源码项目,文件位置为:cas-server-webapp\src\main\resources\default_views.properties
### Login view (/login) casLoginView.(class)=org.springframework.web.servlet.view.JstlView casLoginView.url=/WEB-INF/view/jsp/default/ui/casLoginView.jsp ### Display login (warning) messages casLoginMessageView.(class)=org.springframework.web.servlet.view.JstlView casLoginMessageView.url=/WEB-INF/view/jsp/default/ui/casLoginMessageView.jsp ### Login confirmation view (logged in, warn=true) casLoginConfirmView.(class)=org.springframework.web.servlet.view.JstlView casLoginConfirmView.url=/WEB-INF/view/jsp/default/ui/casConfirmView.jsp ### Logged-in view (logged in, no service provided) casLoginGenericSuccessView.(class)=org.springframework.web.servlet.view.JstlView casLoginGenericSuccessView.url=/WEB-INF/view/jsp/default/ui/casGenericSuccess.jsp ### Logout view (/logout) casLogoutView.(class)=org.springframework.web.servlet.view.JstlView casLogoutView.url=/WEB-INF/view/jsp/default/ui/casLogoutView.jsp ### CAS error view viewServiceErrorView.(class)=org.springframework.web.servlet.view.JstlView viewServiceErrorView.url=/WEB-INF/view/jsp/default/ui/serviceErrorView.jsp viewServiceSsoErrorView.(class)=org.springframework.web.servlet.view.JstlView viewServiceSsoErrorView.url=/WEB-INF/view/jsp/default/ui/serviceErrorSsoView.jsp ### CAS statistics view viewStatisticsView.(class)=org.springframework.web.servlet.view.JstlView viewStatisticsView.url=/WEB-INF/view/jsp/monitoring/viewStatistics.jsp ### Expired Password Error message casExpiredPassView.(class)=org.springframework.web.servlet.view.JstlView casExpiredPassView.url=/WEB-INF/view/jsp/default/ui/casExpiredPassView.jsp ### Locked Account Error message casAccountLockedView.(class)=org.springframework.web.servlet.view.JstlView casAccountLockedView.url=/WEB-INF/view/jsp/default/ui/casAccountLockedView.jsp ### Disabled Account Error message casAccountDisabledView.(class)=org.springframework.web.servlet.view.JstlView casAccountDisabledView.url=/WEB-INF/view/jsp/default/ui/casAccountDisabledView.jsp ### Must Change Password Error message casMustChangePassView.(class)=org.springframework.web.servlet.view.JstlView casMustChangePassView.url=/WEB-INF/view/jsp/default/ui/casMustChangePassView.jsp ### Bad Hours Error message casBadHoursView.(class)=org.springframework.web.servlet.view.JstlView casBadHoursView.url=/WEB-INF/view/jsp/default/ui/casBadHoursView.jsp ### Bad Workstation Error message casBadWorkstationView.(class)=org.springframework.web.servlet.view.JstlView casBadWorkstationView.url=/WEB-INF/view/jsp/default/ui/casBadWorkstationView.jsp
咱们根据文件清单对应的文件修改便可。
若是你想保留这些页面,拷贝一份default 目录,而后修改其中对应的文件后,在这个配置文件中将对应路径修改即可。
除此以外,CAS 4.0 还提供了一些协议登陆的方式,也就是在不经过登陆页面使用帐号密码的方式登陆,自动受权登陆。
好比咱们能够从其余平台直接免登录进入原来须要SSO登陆后才能够看到的系统,或者作个U盾,插入U盾的电脑,能够直接访问系统,无需登陆。
官方文档:http://jasig.github.io/cas/4.0.x/protocol/CAS-Protocol.html#cas-protocol
单点登陆的目的是为了让多个相关联的应用使用相同的登陆过程,本文在讲解过程当中构造 2个简单的应用,分别以 casTest1 和 casTest2 来做为示例,它们均只有一个页面,显示欢迎信息和当前登陆用户名。这 2 个应用使用同一套登陆信息,而且只有登陆过的用户才能访问,经过本文的配置,实现单点登陆,即只需登陆一次就能够访问这两个应用。
1. 与 CAS Server 创建信任关系
- CAS Server 部署在服务器A
- 客户端应用部署在服务B\C
因为客户端应用与 CAS Server 的通讯采用 SSL,所以须要A与B\C的jre之间创建信任关系。
CAS Server 咱们在文章的前面已经配置好的 SSL,如今咱们只须要在客户端B\C上添加服务器A的证书,操做方法:
网上找一个这个文件下载下来 InstallCert.java
而后在客户端服务器上
编译:javac InstallCert.java
运行:Java InstallCert serverA:8443 其中8443为SSL端口,serverA为服务器名称或域名
而且在接下来出现的询问中输入 1。这样,就将 A 添加到了 B 的 trust store 中(其余客户端服务器同样操做)。
这里要说一下的是 serverA:8443 这里不要写 {IP地址}:8443,要以域名的方式,如本机 localhost:8443 ,或域名方式 www.baidu.com:443
2. 配置 CAS Filter
在Client工程WEB-INF/lib下添加 cas-client-core-3.3.3.jar 包。
修改web.xml以下:
<!-- ======================== 单点登陆/登出 ======================== --> <!-- 该过滤器用于实现单点登出功能,可选配置。 --> <!-- 登出地址 https://casserver:8443/cas/logout --> <filter> <filter-name>CAS Single Sign Out Filter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> </filter> <!-- 该过滤器负责用户的认证工做,必须启用它 --> <filter> <filter-name>CAS Authentication Filter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>https://localhost:8443/cas/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://localhost:8080</param-value> </init-param> </filter> <!-- 该过滤器负责对Ticket的校验工做,必须启用它 --> <filter> <filter-name>CAS Validation Filter</filter-name> <filter-class>org.jasig.cas.client.validation.Cas10TicketValidationFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>https://localhost:8443/cas</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://localhost:8080</param-value> </init-param> <init-param> <param-name>redirectAfterValidation</param-name> <param-value>true</param-value> </init-param> </filter> <!-- 该过滤器负责实现HttpServletRequest请求的包装, 好比容许开发者经过HttpServletRequest的getRemoteUser()方法得到SSO登陆用户的登陆名,可选配置。 --> <filter> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class> </filter> <!-- 该过滤器使得开发者能够经过org.jasig.cas.client.util.AssertionHolder来获取用户的登陆名。 好比AssertionHolder.getAssertion().getPrincipal().getName()。 --> <filter> <filter-name>CAS Assertion Thread Local Filter</filter-name> <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class> </filter> <filter-mapping> <filter-name>CAS Single Sign Out Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CAS Assertion Thread Local Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <!-- ======================== 单点登陆/登出结束 ======================== -->
运行Client工程,首次访问任一页面就会跳转到https://localhost:8443/cas/login进行认证。
把你的退出连接设置为:https://localhost:8443/cas/logout 便可实现单点登出,若是须要在系统退出后返回指定页面,咱们能够修改源码在logout后面增长参数来实现。
3. 登陆用户名的获取
// 1. request.getRemoteUser(); // 2. AssertionHolder.getAssertion().getPrincipal().getName()
我建立了2个测试项目,首页访问路径分别为:
http://localhost:8080/cas-client-test1/index.jsp
http://localhost:8080/cas-client-test2/index.jsp
一、访问 http://localhost:8080/cas-client-test1/index.jsp 自动跳转到认证页面。
二、登陆成功后,自动转到 http://localhost:8080/cas-client-test1/index.jsp 页面。
三、访问 http://localhost:8080/cas-client-test2/index.jsp 由于已经被认证,因此能够直接打开不用再登陆。
四、退出后打开退出页面,实际项目中能够经过 httpclient 请求登出地址来进行登出,而后跳转到登陆页面。