准备工作
下载cas客户端与服务器端包,这里介绍的版本为
client : 3.2.1
server : 3.5.2
cas部署
CAS Server 是一套基于 Java 实现的服务,该服务以一个 Java Web Application 单独部署在与 servlet2.3 兼容的 Web 服务器上,另外,如果 Client 与 CAS Server 之间的交互采用 Https 协议通信的话,部署 CAS Server 的服务器还需要支持 SSL 协议。当 SSL 配置成功过后,像普通 Web 应用一样将 CAS Server 部署在服务器上就能正常运行了。
证书生成
1,我们用java的keytool工具生成store文件, 命令如下,按提示操作输入相关信息
keytool -genkey -keystore d:/tomcat.store -alias tomcat
2,生成cert文件
keytool -export -file D:/tomcat.cert -alias tomcat -keystore d:/tomcat.store
3,导入我们jdk
keytool -import -keystore D:\Java\jdk1.6.0_31\jre\lib\security\cacerts -file d:/tomcat.cert -alias tomcat
tomcat server.xml文件中打开https支持
在tomcat的config server.xml中打开 Connector结点
<Connector port="443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" keystoreFile="yourPath/xx.keystore" keystorePass="yourPwd" clientAuth="false" sslProtocol="TLS" />
这里注意下,在tomcat6某些版本下
protocol="HTTP/1.1 "
改为
protocol="org.apache.coyote.http11.Http11Protocol"
否则起动会报错
cas server客户端配制
这里通过mysql数据库认证,所以事先加入mysql的依赖包和cas 对jdbc支持的jar包
配制deployerConfigContext.xml
1,我们配制一个数据源:
<!-- 配制数据源 --> <bean id="casDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://localhost:3306/xxx</value> </property> <property name="username"> <value>root</value> </property> <property name="password"> <value>xxx</value> </property> </bean>
2,密码一般都采用md5或是其它算法加密,所以还要配制一个加密器(这里用内置的当然你也可以自定义)
<!-- 加密器 --> <bean id="MD5PasswordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder"> <constructor-arg index="0"> <value>MD5</value> </constructor-arg> </bean>
3,AuthenticationHandler
【http://www.ibm.com/developerworks/cn/opensource/os-cn-cas/ 文章中的原话】
在 cas-server-support-jdbc-3.1.1.jar 包中,提供了 3 个基于 JDBC 的 AuthenticationHandler,分别为 BindModeSearchDatabaseAuthenticationHandler, QueryDatabaseAuthenticationHandler, SearchModeSearchDatabaseAuthenticationHandler。其中 BindModeSearchDatabaseAuthenticationHandler 是用所给的用户名和密码去建立数据库连接,根据连接建立是否成功来判断验证成功与否;QueryDatabaseAuthenticationHandler 通过配置一个 SQL 语句查出密码,与所给密码匹配;SearchModeSearchDatabaseAuthenticationHandler 通过配置存放用户验证信息的表、用户名字段和密码字段,构造查询语句来验证。
使用哪个 AuthenticationHandler,需要在 deployerConfigContext.xml 中设置,默认情况下,CAS 使用一个简单的 username=password 的 AuthenticationHandler,在文件中可以找到如下一行:<bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePassword
AuthenticationHandler" />,我们可以将其注释掉,换成我们希望的一个 AuthenticationHandler,比如,使用QueryDatabaseAuthenticationHandler 或 SearchModeSearchDatabaseAuthenticationHandler
我们这里配制为:
<!-- 注释掉简单的登录验证 替换成下面bean <bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" /> --> <bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"> <property name="dataSource" ref="casDataSource" /> <property name="sql" value="select PASSWORD from xxx where lower(LOGINNAME) = lower(?)" /> <property name="passwordEncoder" ref="MD5PasswordEncoder" /> </bean>
4,这里是反回用户的更多信息,所以还需要配制如下信息
<!-- 反回数据字段 --> <bean class="org.jasig.services.persondir.support.jdbc.SingleRowJdbcPersonAttributeDao" id="attributeRepository"> <constructor-arg index="0" ref="casDataSource" /> <constructor-arg index="1" value="select * from xxx where {0}" /> <property name="queryAttributeMapping"> <map> <!-- 这里的key需写username,value对应数据库用户名字段 --> <entry key="username" value="LOGINNAME" /> </map> </property> <!-- sql执行完毕后返回的结构属性, key对应数据库字段,value对应客户端获取参数 --> <property name="resultAttributeMapping"> <map> <entry key="USERID" value="id" /> <entry key="LOGINNAME" value="loginName" /> <entry key="USERNAME" value="name" /> </map> </property> </bean>
注: 这里反回*时,对于日期字段为'0000-00-00'这样的格式会报错,所以数据库数据要规范下,或是不用*反回所有而是直接写你需要的字段
5,这里还没完,对于反回多字段信息在需要如下配制
<bean class="org.jasig.cas.services.RegexRegisteredService"> <property name="id" value="0" /> <property name="name" value="HTTP and IMAP" /> <property name="description" value="Allows HTTP(S) and IMAP(S) protocols" /> <property name="serviceId" value="^(https?|imaps?)://.*" /> <property name="evaluationOrder" value="10000001" /> <!-- 3.5返回自定义数据字段新添加属性--> <property name="ignoreAttributes" value="true" /> <!-- 3.4属性 <property name="allowedAttributes"> <list> <value>....</value> </list> </property> --> </bean>
jsp页面的扩展
因为反回多个字段,所以还要对反回数据的WEB-INF/view/jsp/protocol/2.0/casServiceValidationSuccess.jsp页面做扩展
1,页面顶部加入<%@page pageEncoding="UTF-8"%>(对中文处理)
2,对生成数据交互的xml做扩展
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'> <cas:authenticationSuccess> <cas:user>${fn:escapeXml(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.id)}</cas:user> <!-- 扩展部份开始 --> <c:if test="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes) > 0}"> <cas:attributes> <c:forEach var="attr" items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}"> <cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}> </c:forEach> </cas:attributes> </c:if> <!-- 扩展部份结束 --> <c:if test="${not empty pgtIou}"> <cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket> </c:if> <c:if test="${fn:length(assertion.chainedAuthentications) > 1}"> <cas:proxies> <c:forEach var="proxy" items="${assertion.chainedAuthentications}" varStatus="loopStatus" begin="0" end="${fn:length(assertion.chainedAuthentications)-2}" step="1"> <cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy> </c:forEach> </cas:proxies> </c:if> </cas:authenticationSuccess> </cas:serviceResponse>
登录中文乱码解决
好了现在服务器端配制基本结束,但对于登录过程带中文乱码的还末解决,这时在服务器端的web.xml中配制个过滤器
<!-- 防提交参数中文乱码 --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
客户端配制:
这里直接贴配制
<!-- 用于单点退出,该过滤器用于实现单点登出功能 --> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <filter> <filter-name>CAS Single Sign Out Filter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> </filter> <filter-mapping> <filter-name>CAS Single Sign Out Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 该过滤器负责用户的认证工作,必须启用 --> <filter> <filter-name>CASFilter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <!--认证服务端url --> <param-name>casServerLoginUrl</param-name> <param-value>https://localhost:8443/cas/login</param-value> </init-param> <init-param> <!-- 客户端url --> <param-name>serverName</param-name> <param-value>http://localhost:8080</param-value> </init-param> </filter> <filter-mapping> <filter-name>CASFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 该过滤器负责对Ticket的校验工作,必须启用 --> <filter> <filter-name>CAS Validation Filter</filter-name> <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</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>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 该过滤器负责实现HttpServletRequest请求的包裹,比如允许开发者 通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户的登录名,可选配置。 --> <filter> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class> </filter> <filter-mapping> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 该过滤器使得开发者可以通过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 Assertion Thread Local Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 自动根据单点登录的结果设置本系统的用户信息,AutoSetUserAdapterFilter就是一个自定义过过滤器,它主要做一些用户数据的本系统的处理工作(如用户信息session的放入) --> <filter> <display-name>AutoSetUserAdapterFilter</display-name> <filter-name>AutoSetUserAdapterFilter</filter-name> <filter-class> com.common.filter.AutoSetUserAdapterFilter</filter-class> </filter> <filter-mapping> <filter-name>AutoSetUserAdapterFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
客户端获取用户信息:
AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal(); Map<String, Object> user = principal.getAttributes();
这里的user的map中就是你反回的用户信息字段数据