1. 下载 cas server 源码
https://github.com/Jasig/cas/releases
我下载的是 4.0.1。你也可以直接checkout
cas client : http://downloads.jasig.org/cas-clients/
版本是 3.3.3
2. 将下载的 cas-4.0.1.zip 解压, 在根目录 执行
- mvn package install -Dmaven.test.skip=true
执行完成后,可将 cas-server-webapp\target\cas.war 部署到tomcat
3. 生成证书
参考: http://kai2008.iteye.com/blog/1134238
生成服务器端证书
为客户端生成证书
keytool -genkey -v -alias myKey -keyalg RSA -storetype PKCS12 -keystore d:/my1.p12 -dname "cn=localhost,ou=cas,o=cas,c=CN" -storepass 123456 -keypass 123456 -validity 3650
让服务器信任客户端证书
keytool -export -alias myKey -keystore d:/my1.p12 -storetype PKCS12 -rfc -file d:/my1.cer
将客户端证书导入到服务器的证书库
keytool -import -v -file d:/my1.cer -keystore d:/tomcat1.keystore -storepass 123456
让客户端信任服务器证书
keytool -keystore d:/tomcat1.keystore -export -alias tomcat1 -file d:/tomcat1.cer
在浏览器中导入服务器和客户端证书。
双击 tomcat1.cer 即可导入服务器证书。按照提示安装证书,将证书填入到“受信任的根证书颁发机构”。
双击 my1.p12 即可导入服务器证书。按照提示安装证书,将证书填入到“个人”。
注意,cn要设置为服务器地址,如 localhost,方便调试,否则后面cas server 回调 cas client 会出错:
- java.security.cert.CertificateException: No name matching localhost found
为客户端的JVM导入密钥
keytool -import -file d:/tomcat1.cer -alias tomcat1 -keystore "%java_home%\jre\lib\security\cacerts"
查看证书
- keytool -list -v -keystore "%java_home%\jre\lib\security\cacerts" -alias localhost
修改 tomcat conf server.xml :
- <Connector SSLEnabled="true" clientauth="false" keystoreFile="conf/tomcat1.keystore" keystorePass="123456" maxThreads="150"
- port="8443" protocol="HTTP/1.1" scheme="https" secure="true" sslProtocol="TLS" />
4. 开发 cas server
第一种:下载 cas server 源码后,执行
- mvn eclipse:eclipse
- Plugin execution not covered by lifecycle configuration:xxx plugin
解决方法:
在 cas-4.0.1\pom.xml 里的 build - pluginManagement - plugins 节点加入:
- <plugin>
- <groupId>org.eclipse.m2e</groupId>
- <artifactId>lifecycle-mapping</artifactId>
- <version>1.0.0</version>
- <configuration>
- <lifecycleMappingMetadata>
- <pluginExecutions>
- <pluginExecution>
- <pluginExecutionFilter>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-checkstyle-plugin</artifactId>
- <versionRange>2.10</versionRange>
- <goals>
- <goal>checkstyle</goal>
- </goals>
- </pluginExecutionFilter>
- <action>
- <ignore />
- </action>
- </pluginExecution>
- <pluginExecution>
- <pluginExecutionFilter>
- <groupId>com.mycila.maven-license-plugin</groupId>
- <artifactId>maven-license-plugin</artifactId>
- <versionRange>1.9.0</versionRange>
- <goals>
- <goal>check</goal>
- </goals>
- </pluginExecutionFilter>
- <action>
- <ignore />
- </action>
- </pluginExecution>
- <pluginExecution>
- <pluginExecutionFilter>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>aspectj-maven-plugin</artifactId>
- <versionRange>1.4</versionRange>
- <goals>
- <goal>compile</goal>
- </goals>
- </pluginExecutionFilter>
- <action>
- <ignore />
- </action>
- </pluginExecution>
- </pluginExecutions>
- </lifecycleMappingMetadata>
- </configuration>
- </plugin>
第二种方法:
参考: http://jasig.github.io/cas/4.0.x/installation/Maven-Overlay-Installation.html
下载maven 模板: https://github.com/UniconLabs/simple-cas4-overlay-template/archive/master.zip
导入eclipse,import - maven - existing maven projects,在pom.xml加入依赖,支持访问数据库验证密码:
- <dependencies>
- <dependency>
- <groupId>org.jasig.cas</groupId>
- <artifactId>cas-server-webapp</artifactId>
- <version>${cas.version}</version>
- <type>war</type>
- <scope>runtime</scope>
- </dependency>
- <dependency>
- <groupId>org.jasig.cas</groupId>
- <artifactId>cas-server-core</artifactId>
- <version>${cas.version}</version>
- </dependency>
- <dependency>
- <groupId>org.jasig.cas</groupId>
- <artifactId>cas-server-support-jdbc</artifactId>
- <version>${cas.version}</version>
- </dependency>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>5.1.35</version>
- </dependency>
- <dependency>
- <groupId>c3p0</groupId>
- <artifactId>c3p0</artifactId>
- <version>${c3p0.version}</version>
- </dependency>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-core</artifactId>
- <version>${spring.version}</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>javax.validation</groupId>
- <artifactId>validation-api</artifactId>
- <version>${javax.validation.version}</version>
- <scope>compile</scope>
- </dependency>
- </dependencies>
- <properties>
- <cas.version>4.0.1</cas.version>
- <maven.compiler.source>1.7</maven.compiler.source>
- <maven.compiler.target>1.7</maven.compiler.target>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <spring.version>3.2.6.RELEASE</spring.version>
- <javax.validation.version>1.0.0.GA</javax.validation.version>
- <c3p0.version>0.9.1.2</c3p0.version>
- </properties>
修改 deployerConfigContext.xml :
- <bean id="authenticationManager" class="org.jasig.cas.authentication.PolicyBasedAuthenticationManager">
- <constructor-arg>
- <map>
- <entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" />
- <!--<entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" /> -->
- <entry key-ref="dbAuthHandler" value-ref="primaryPrincipalResolver"/>
- </map>
- </constructor-arg>
- <property name="authenticationPolicy">
- <bean class="org.jasig.cas.authentication.AnyAuthenticationPolicy" />
- </property>
- </bean>
- <!--
- <bean id="primaryAuthenticationHandler"
- class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler">
- <property name="users">
- <map>
- <entry key="casuser" value="Mellon"/>
- </map>
- </property>
- </bean>
- -->
- <bean id="dataSource"
- class="com.mchange.v2.c3p0.ComboPooledDataSource"
- p:driverClass="com.mysql.jdbc.Driver" p:jdbcUrl="jdbc:mysql://localhost:3306/portal_230?useUnicode=true&characterEncoding=UTF8&noAccessToProcedureBodies=true&autoReconnect=true&zeroDateTimeBehavior=convertToNull"
- p:user="root"
- p:password="root" />
- <!-- 密码加密方式-->
- <bean id="passwordEncoder"
- class="com.my.cas.authentication.handler.SelfPasswordEncoder"
- c:encodingAlgorithm="SHA1"
- p:characterEncoding="UTF-8" />
- <bean id="dbAuthHandler"
- class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"
- p:dataSource-ref="dataSource"
- p:sql="select password from test_user where username=? "
- p:passwordEncoder-ref="passwordEncoder"
- />
其中的
- com.my.cas.authentication.handler.SelfPasswordEncoder
为自定义的密码加密类,实现接口
- org.jasig.cas.authentication.handler.PasswordEncoder
5. 客户端
引入依赖
- <dependency>
- <groupId>org.jasig.cas.client</groupId>
- <artifactId>cas-client-core</artifactId>
- <version>3.2.1</version>
- </dependency>
- <!-- ======================== 单点登录开始 ======================== -->
- <!-- 用于单点退出,该过滤器用于实现单点登出功能,可选配置 -->
- <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>
- <param-name>casServerLoginUrl</param-name>
- <param-value>https://sso.cas.com:8443/cas/login</param-value>
- <!--这里的server是服务端的IP -->
- </init-param>
- <init-param>
- <param-name>serverName</param-name>
- <param-value>http://localhost:8080</param-value><span style="color:#FF0000;"> ①</span>
- </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://sso.cas.com:8443/cas</param-value>
- </init-param>
- <init-param>
- <param-name>serverName</param-name>
- <param-value>http://localhost:8080</param-value> <span style="color:#FF0000;">②</span>
- </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>
- <!-- ======================== 单点登录结束 ======================== -->
写一个controller测试
- @Controller
- public class TestController {
- @RequestMapping("/test")
- public void test(HttpServletRequest request,
- HttpServletResponse response) throws IOException{
- String username = AssertionHolder.getAssertion().getPrincipal().getName();
- response.getWriter().print("hello, " + username );
- }
- }
通过
- AssertionHolder.getAssertion().getPrincipal().getName()
6. 登录成功后返回更多用户信息
cas4.0 没有了 UsernamePasswordCredentialsToPrincipalResolver,而是提供了 org.jasig.cas.authentication.principal.PersonDirectoryPrincipalResolver,需要配置 attributeRepository 属性
- <bean id="primaryPrincipalResolver"
- class="org.jasig.cas.authentication.principal.PersonDirectoryPrincipalResolver" >
- <property name="attributeRepository" ref="attributeRepository" />
- </bean>
- <bean id="attributeRepository" class="org.jasig.services.persondir.support.jdbc.SingleRowJdbcPersonAttributeDao" >
- <constructor-arg index="0" ref="dataSource"/>
- <constructor-arg index="1" value="select * from test_user where {0}"/>
- <property name="queryAttributeMapping">
- <map>
- <!-- // 这里的key需写username,value对应数据库用户名字段 -->
- <entry key="username" value="username"/>
- </map>
- </property>
- <property name="resultAttributeMapping">
- <map>
- <entry key="id" value="id"/>
- <entry key="password" value="password"/>
- <entry key="mobile" value="mobile"/>
- <entry key="email" value="email"/>
- </map>
- </property>
- </bean>
然后将 RegexRegisteredService 的配置改为
- <bean class="org.jasig.cas.services.RegexRegisteredService">
- <property name="id" value="1" />
- <property name="name" value="HTTP and IMAP on example.com" />
- <property name="description" value="Allows HTTP(S) and IMAP(S) protocols on example.com" />
- <property name="serviceId" value="^(https?|imaps?)://.*" />
- <property name="evaluationOrder" value="0" />
- <!-- <property name="attributeFilter"> -->
- <!-- <bean class="org.jasig.cas.services.support.RegisteredServiceRegexAttributeFilter" c:regex="^\w{3}$" /> -->
- <!-- </property> -->
- <!-- // 客户端需要使用的对象的属性名称 -->
- <property name="allowedAttributes">
- <list>
- <value>id</value>
- <value>email</value>
- <value>mobile</value>
- </list>
- </property>
- </bean>
最后,修改 WEB-INF\view\jsp\protocol\2.0\casServiceValidationSuccess.jsp,加入
- <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>
客户端改为
- @Controller
- public class TestController {
- @RequestMapping("/test")
- public void test(HttpServletRequest request,
- HttpServletResponse response) throws IOException{
- String username = AssertionHolder.getAssertion().getPrincipal().getName();
- AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();
- Map<String, Object> attributes = principal.getAttributes();
- String email= (String)attributes.get("email");
- response.getWriter().print("hello, " + username + " . email:" + email);
- }
- }
搞定!
参考:
http://zxs19861202.iteye.com/blog/890965
http://blog.csdn.net/small_love/article/details/6664831
http://jasig.github.io/cas/4.0.x/installation/Maven-Overlay-Installation.html