原创apereo cas 4.2从0开始
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原创apereo cas 4.2从0开始相关的知识,希望对你有一定的参考价值。
转载请声明来自:http://www.cnblogs.com/chixinzei/p/7504272.html 谢谢!
目前已实现功能
数据库验证,druid数据源,页面定制,restful登录,shibboleth idp saml2.0整合
cas相关参考文档
http://www.cnblogs.com/xwdreamer/archive/2011/11/10/2296939.html spring webflow
https://apereo.github.io/cas/4.2.x/ CAS官方文档
http://docs.spring.io/spring-webflow/docs/2.4.5.RELEASE/reference/html Spring webflow 官方文档
与Shibboleth IDP端 SAML2.0整合文档:
https://apereo.github.io/cas/4.2.x/integration/Shibboleth.html 官网cas和Shibboleth IDP 整合
http://wwwcomy.iteye.com/blog/2236016 Shibboleth IDP安装中文博客
https://wiki.shibboleth.net/confluence/display/SHIB2/IdPInstall Shibboleth IDP安装
工作环境准备
运行基础:jdk8.0.131,cas-server:4.2.1(基于gradle),cas-client:3.3.3(基于maven),win7 64,tomcat8.5,mysql 5.6.21,idea-2017.2
ssl配置相关准备
1.http访问配置
取消cas https验证:http://www.cnblogs.com/xiaojf/p/6617693.html
2.https访问配置
准备本地tomcat证书生成与配置:文档参考:http://www.bug315.com/article/412.htm
若要在本地使用SSL访问cas,则只需要修改cas-server-webapp\\src\\main\\resources\\services\\HTTPSandIMAPS-10000001.json的属性tgc.secure=false即可,意思是cas的cookie是否只在ssl下生成(如果为true,则登陆后cookie也不会存放登录信息和票据,依然重定向到登录界面)
默认jdk密匙库口令:changeit
1)cmd 移动到tomcat目录下,生成server key :
keytool -genkey -alias tomcat -keyalg RSA -storepass changeit -keystore server.keystore -validity 3600
解释:
-alias 表示证书的别名,一个keystore文件中可以存放多个alias。
-keyalg RSA 表示密钥算法的名称为RSA算法
-keypass changeit 表示密钥的口令是changeit
-storepass changeit 表示密钥库(生成的keystore文件)的密钥是changeit
-keystore server.keystore 表示指定密匙库的名称
-validity 3600 表示证书有效期3600天,也就是大概10年
删除证书命令:
先移动到jdk的密匙总库目录:
如:D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\security
执行删除jdk密匙库证书:
keytool -delete -alias tomcat -keystore cacerts
删除后可以再次将证书导入jdk密匙库
2)输入参数:
名字和姓氏:149p874e84.51mypc.cn (域名)
组织单位名称:149p874e84.51mypc.cn
组织名称:149p874e84.51mypc.cn
所在城市:jinjiang
所在省:fujian
国家/地区代码:zh
3)证书导入的JDK的证书信任库中
keytool -export -trustcacerts -alias tomcat -file server.cer -keystore server.keystore -storepass changeit keytool -import -trustcacerts -alias tomcat -file server.cer -keystore "jdk目录/jre/lib/security/cacerts" -storepass changeit
4)修改tomcat server.xml增加https支持,不需要删除原来的http连接
<!--开启https连接,如果是非外部ide启动,则keystoreFile配置为server.keystore即可!--> <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="D:/Program Files (x86)/apache-tomcat-8.5.16(8086)/server.keystore" keystorePass="changeit"/>
3.支持配置多个验证拦截器。
简单用户名和密码配置修改:/cas-server-support-x509/src/test/resources/deployerConfigContext.xml
casuser,Mellon。
另外此用户名密码也可以配置在cas.properties中:
两者都存在时优先使用properties的配置
xml存在,而properties不存在时,则读取xml。
CAS服务端配置修改
⒈jdbc简单查询校验:
1.1mysql建表:
CREATE TABLE `t_user` ( `id` bigint(15) NOT NULL COMMENT‘主键‘, `account` varchar(30) DEFAULT NULL COMMENT‘账号‘, `password` varchar(255) DEFAULT NULL COMMENT‘密码‘, `valid` tinyint(1) DEFAULT NULL COMMENT ‘是否有效‘, PRIMARY KEY(`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; --插入1条数据: INSERT INTO `t_user` (`id`, `account`, `password`, `valid`) VALUES (1, ‘admin‘, ‘md5加密后的密码‘, 1);
1.2 cas-server-webapp\\build.gradle 引入jdbc支持包依赖:
compile project(‘:cas-server-support-jdbc‘) compile group: ‘mysql‘, name: ‘mysql-connector-java‘, version: mysqlConnectorJavaVersion
1.3 deployerConfigContext.xml
关闭简单用户密码校验,改为jdbc校验,密码MD5加密,配合druid
<!--修改登录方式为jdbc验证,注意,需要注释掉QueryAndEncodeDatabaseAuthenticationHandler的注解注入,有冲突--> <!--<alias name="acceptUsersAuthenticationHandler" alias="primaryAuthenticationHandler" />--> <alias name="queryDatabaseAuthenticationHandler" alias="primaryAuthenticationHandler"/> <bean id="queryDatabaseAuthenticationHandler" class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"> <!--<property name="dataSource" ref="queryDatabaseDataSource"></property>--> <!--<property name="sql" value="select password from t_user where account=?"></property>--> <property name="passwordEncoder" ref="MD5PasswordEncoder"></property> </bean> <!-- 添加MD5密码加密功能 --> <bean id="MD5PasswordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder"> <constructor-arg index="0"> <value>MD5</value> </constructor-arg> </bean> <!-- 数据源配置 --> <alias name="dataSource" alias="queryDatabaseDataSource"/> <!-- 阿里 druid 数据库连接池 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <!-- 数据库基本信息配置 --> <property name="url" value="${cas.druid.database.url}"/> <property name="username" value="${cas.druid.database.username}"/> <property name="password" value="${cas.druid.database.password}"/> <property name="driverClassName" value="${cas.druid.database.driverClassName}"/> <property name="filters" value="${cas.druid.database.filters}"/> <!-- 最大并发连接数 --> <property name="maxActive" value="${cas.druid.database.maxActive}"/> <!-- 初始化连接数量 --> <property name="initialSize" value="${cas.druid.database.initialSize}"/> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="${cas.druid.database.maxWait}"/> <!-- 最小空闲连接数 --> <property name="minIdle" value="${cas.druid.database.minIdle}"/> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="${cas.druid.database.timeBetweenEvictionRunsMillis}"/> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="${cas.druid.database.minEvictableIdleTimeMillis}"/> <property name="validationQuery" value="${cas.druid.database.validationQuery}"/> <property name="testWhileIdle" value="${cas.druid.database.testWhileIdle}"/> <property name="testOnBorrow" value="${cas.druid.database.testOnBorrow}"/> <property name="testOnReturn" value="${cas.druid.database.testOnReturn}"/> <property name="maxOpenPreparedStatements" value="${cas.druid.database.maxOpenPreparedStatements}"/> <!-- 打开 removeAbandoned 功能 --> <property name="removeAbandoned" value="${cas.druid.database.removeAbandoned}"/> <!-- 1800 秒,也就是 30 分钟 --> <property name="removeAbandonedTimeout" value="${cas.druid.database.removeAbandonedTimeout}"/> <!-- 关闭 abanded 连接时输出错误日志 --> <property name="logAbandoned" value="${cas.druid.database.logAbandoned}"/> <property name="proxyFilters"> <list> <ref bean="log-filter"/> </list> </property> </bean> <bean id="log-filter" class="com.alibaba.druid.filter.logging.Slf4jLogFilter"> <property name="connectionLogEnabled" value="${cas.druid.database.connectionLogEnabled}"/> <property name="statementLogEnabled" value="${cas.druid.database.statementLogEnabled}"/> <property name="resultSetLogEnabled" value="${cas.druid.database.resultSetLogEnabled}"/> <property name="statementExecutableSqlLogEnable" value="${cas.druid.database.statementExecutableSqlLogEnable}"/> </bean>
1.4 cas.propertie中新增配置:
#sql通过用户名查询密码 cas.jdbc.authn.query.sql=select password from t_user where account=? and valid=true #druid config by xbwu cas.druid.database.url=jdbc:mysql://localhost:3306/xbwudb?characterEncoding=utf8 cas.druid.database.username=root cas.druid.database.password=root cas.druid.database.driverClassName=com.mysql.jdbc.Driver cas.druid.database.filters=stat,wall cas.druid.database.maxActive=20 cas.druid.database.initialSize=1 cas.druid.database.maxWait=60000 cas.druid.database.minIdle=10 cas.druid.database.timeBetweenEvictionRunsMillis=60000 cas.druid.database.minEvictableIdleTimeMillis=300000 cas.druid.database.validationQuery=SELECT ‘x‘ cas.druid.database.testWhileIdle= true cas.druid.database.testOnBorrow=false cas.druid.database.testOnReturn=false cas.druid.database.maxOpenPreparedStatements=20 cas.druid.database.removeAbandoned=true cas.druid.database.removeAbandonedTimeout=1800 cas.druid.database.logAbandoned=true cas.druid.database.connectionLogEnabled=false cas.druid.database.statementLogEnabled=false cas.druid.database.resultSetLogEnabled=true cas.druid.database.statementExecutableSqlLogEnable=true
1.5 web.xml新增druid控制台:
<!-- 连接池 启用 Web 监控统计功能 start--> <filter> <filter-name> DruidWebStatFilter </filter-name> <filter-class> com.alibaba.druid.support.http.WebStatFilter </filter-class> <init-param > <param-name> exclusions </param-name> <param-value> *. js ,*. gif ,*. jpg ,*. png ,*. css ,*. ico ,/ druid /* </param-value> </init-param> </filter > <filter-mapping> <filter-name> DruidWebStatFilter </filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet > <servlet-name> DruidStatView </servlet-name> <servlet-class> com.alibaba.druid.support.http.StatViewServlet </servlet-class> </servlet > <servlet-mapping> <servlet-name> DruidStatView </servlet-name> <url-pattern>/druid/console/*</url-pattern> </servlet-mapping> <!-- 连接池 启用 Web 监控统计功能 end—>
1.6 cas-server-webapp\\src\\main\\resources\\log4j2.xml新增druid日志配置
<?xml version="1.0" encoding="UTF-8" ?> <!-- Specify the refresh internal in seconds. --> <!-- OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <Configuration monitorInterval="60"> <Appenders> <Console name="console" target="SYSTEM_OUT"> <PatternLayout pattern="%d %p [%c] - %n<%m>%n"/> </Console> <RollingFile name="file" fileName="../logs/cas.log" append="true" filePattern="cas-%d{yyyy-MM-dd-HH}-%i.log"> <PatternLayout pattern="%d %p [%c] - %n<%m>%n"/> <Policies> <OnStartupTriggeringPolicy /> <SizeBasedTriggeringPolicy size="10 MB"/> <TimeBasedTriggeringPolicy /> </Policies> </RollingFile> <RollingFile name="auditlogfile" fileName="../logs/cas_audit.log" append="true" filePattern="cas_audit-%d{yyyy-MM-dd-HH}-%i.log"> <PatternLayout pattern="%d %p [%c] - %m%n"/> <Policies> <OnStartupTriggeringPolicy /> <SizeBasedTriggeringPolicy size="10 MB"/> <TimeBasedTriggeringPolicy /> </Policies> </RollingFile> <RollingFile name="perfFileAppender" fileName="../logs/perfStats.log" append="true" filePattern="perfStats-%d{yyyy-MM-dd-HH}-%i.log"> <PatternLayout pattern="%m%n"/> <Policies> <OnStartupTriggeringPolicy /> <SizeBasedTriggeringPolicy size="10 MB"/> <TimeBasedTriggeringPolicy /> </Policies> </RollingFile> </Appenders> <Loggers> <AsyncLogger name="org.jasig" level="info" additivity="false" includeLocation="true"> <AppenderRef ref="console"/> <AppenderRef ref="file"/> </AsyncLogger> <!--不配置appender(输出形式)默认使用AsyncRoot的appender--> <AsyncLogger name="org.springframework" level="warn" /> <AsyncLogger name="org.springframework.webflow" level="warn" /> <AsyncLogger name="org.springframework.web" level="warn" /> <AsyncLogger name="org.pac4j" level="warn" /> <!-- <AsyncLogger name="org.opensaml" level="debug" additivity="false"><AppenderRef ref="console"/><AppenderRef ref="file"/></AsyncLogger><AsyncLogger name="org.ldaptive" level="debug" additivity="false"><AppenderRef ref="console"/><AppenderRef ref="file"/></AsyncLogger><AsyncLogger name="com.hazelcast" level="debug" additivity="false"><AppenderRef ref="console"/><AppenderRef ref="file"/></AsyncLogger> --> <AsyncLogger name="perfStatsLogger" level="info" additivity="false" includeLocation="true"> <AppenderRef ref="perfFileAppender"/> </AsyncLogger> <AsyncLogger name="org.jasig.cas.web.flow" level="info" additivity="true" includeLocation="true"> <AppenderRef ref="file"/> </AsyncLogger> <AsyncLogger name="org.jasig.inspektr.audit.support" level="info" includeLocation="true"> <AppenderRef ref="console"/> <AppenderRef ref="auditlogfile"/> <AppenderRef ref="file"/> </AsyncLogger> <!--druid连接池信息打印--> <AsyncLogger name="druid.sql.Statement" level="debug" additivity="false" includeLocation="true"> <AppenderRef ref="console"/> <AppenderRef ref="file"/> </AsyncLogger> <!--<AsyncLogger name="druid.sql.DataSource" level="debug" additivity="false" includeLocation="true"><AppenderRef ref="console"/></AsyncLogger><AsyncLogger name="druid.sql.Connection" level="debug" additivity="false" includeLocation="true"><AppenderRef ref="console"/></AsyncLogger>--> <!--<AsyncLogger name="druid.sql.ResultSet" level="debug" additivity="false" includeLocation="true"><AppenderRef ref="console"/></AsyncLogger>--> <AsyncLogger name="druid.sql" level="error" additivity="false" includeLocation="true"> <AppenderRef ref="console"/> </AsyncLogger> <!--根输出配置,输出等级--> <!-- OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!--如果上面的日志输出配置了appender,并且输出等级跟根配置有重叠--> <!--比如上面配置debug,则两者的info级别以上日志都会在相同的输出appender中(console)输出,也就是说控制台会输出2次--> <!--所以尽量根输出配置高一点,比如error等级--> <!--如果我们确实有这种需求(不想遵循父类的Appender),可以在自定义配置中加上additivity="false"参数--> <AsyncRoot level="info"> <AppenderRef ref="console"/> <AppenderRef ref="file"/> </AsyncRoot> </Loggers> </Configuration>
1.7 页面样式定制化修改配置:
1.7.1 复制cas-server-webapp\\src\\main\\webapp\\WEB-INF下的view文件夹一份,命名为:view_custom
1.7.2 修改cas-server-webapp\\src\\main\\webapp\\WEB-INF\\cas.properties内容:
1.7.3 样式文件-修改cas-server-webapp\\src\\main\\resources\\cas-theme-default.properties的内容:
4.复制css和js各一份出来,重命名为上面的名字:
2.服务器支持restfull调用登录
cas-server-webapp\\build.gradle 增加依赖:compile project(‘:cas-server-support-rest‘)
cas-server-webapp\\src\\main\\webapp\\WEB-INF\\cas.properties 加长一次性票据有效期:st.timeToKillInSeconds=20 20秒有效
cas单点登录restfull请求登录范例:
参考官方文档:https://apereo.github.io/cas/5.1.x/protocol/REST-Protocol.html
2.1 请求获取TGT
请求方式:POST
head设置:Content-Type: application/x-www-form-urlencoded
范例:{cas服务器地址}/cas/v1/tickets?username=battags&password=password&additionalParam1=paramvalue
demo: http://localhost:8080/cas/v1/tickets?username=admin&password=1234qwer
正确返回:
返回头:
HTTP/1.1 201
Cache-Control: no-store
Location: http://localhost:8080/cas/v1/tickets/TGT-1-fHlelcwDXNYOiFpiiGwnTtyArXD4rjNdtelyBaBaBWWNHHHvav-www.casServer.com
Content-Type: text/html;charset=UTF-8
Content-Length: 376
Date: Mon, 07 Aug 2017 01:42:35 GMT
2.2 拿到TGT,请求一次性票据ST
请求方式:POST
head设置:Content-Type: application/x-www-form-urlencoded
范例:{cas服务器地址}/cas/v1/tickets/{TGT id}?service={form encoded parameter for the service url}
demo:http://localhost:8080/cas/v1/tickets/TGT-1-fHlelcwDXNYOiFpiiGwnTtyArXD4rjNdtelyBaBaBWWNHHHvav-www.casServer.com?service=http%3A%2F%2FcasTest02.com%3A8082%2F
正确返回:
返回头:
HTTP/1.1 200
Cache-Control: no-store
Content-Disposition: inline;filename=f.txt
Content-Type: application/x-msdownload;charset=UTF-8
Content-Length: 43
Date: Mon, 07 Aug 2017 01:45:56 GMT
返回内容:
ST-1-0wjyNMlwOK3kevi5Fa6w-www.casServer.com
2.3 拿着一次性票据,请求服务器验证
请求方式:GET
范例:{cas服务器地址}/cas/p3/serviceValidate?service={service url}&ticket={service ticket}
demo:http://localhost:8080/cas/p3/serviceValidate?ticket=ST-1-0wjyNMlwOK3kevi5Fa6w-www.casServer.com&service=http%3A%2F%2FcasTest02.com%3A8082%2F
正确返回:
<cas:serviceResponse xmlns:cas=‘http://www.yale.edu/tp/cas‘>
<cas:authenticationSuccess>
<cas:user>admin</cas:user>
<cas:attributes>
<cas:longTermAuthenticationRequestTokenUsed>false</cas:longTermAuthenticationRequestTokenUsed>
<cas:isFromNewLogin>true</cas:isFromNewLogin>
<cas:authenticationDate>2017-08-07T10:09:02.521+08:00</cas:authenticationDate>
</cas:attributes>
</cas:authenticationSuccess>
</cas:serviceResponse>
一次性票据过期返回:
<cas:serviceResponse xmlns:cas=‘http://www.yale.edu/tp/cas‘>
<cas:authenticationFailure code=‘INVALID_TICKET‘>
未能够识别出目标 'ST-1-0wjyNMlwOK3kevi5Fa6w-www.casServer.com'票根
</cas:authenticationFailure>
</cas:serviceResponse>
CAS客户端
1.单点登录拦截器配置:web.xml
<!-- ========================单点登录开始 ======================== --> <!--用于单点退出,该过滤器用于实现单点登出功能,可选配置 --> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <!--该过滤器用于实现单点登出功能,可选配置。并且需要服务端的cas.properties修改属性:cas.logout.followServiceRedirects=true --> <filter> <filter-name>CASSingle Sign OutFilter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> </filter> <filter-mapping> <filter-name>CASSingle Sign OutFilter</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>http://localhost:8080/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://casTest01.com:8081</param-value> </init-param> </filter> <filter-mapping> <filter-name>CASFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!--该过滤器负责对Ticket的校验工作,必须启用它 --> <filter> <filter-name>CASValidationFilter</filter-name> <filter-class> org.jasig.cas.client.validation.Cas20ProxyReceivingTi<!-- ========================单点登录开始 ======================== --> <!--用于单点退出,该过滤器用于实现单点登出功能,可选配置 --> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <!--该过滤器用于实现单点登出功能,可选配置。并且需要服务端的cas.properties修改属性:cas.logout.followServiceRedirects=true --> <filter> <filter-name>CASSingle Sign OutFilter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> </filter> <filter-mapping> <filter-name>CASSingle Sign OutFilter</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> <!--cas server登录地址完整url --> <param-value>http://localhost:8080/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <!--用于cas server记录登录的 client访问域名,以及重定向返回 --> <param-value>http://casTest01.com:8081</param-value> </init-param> </filter> <filter-mapping> <filter-name>CASFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!--该过滤器负责对Ticket的校验工作,必须启用它 --> <filter> <filter-name>CASValidationFilter</filter-name> <filter-class> org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter </filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <!--用于cas server访问域名--> <param-value>http://localhost:8080/</param-value> </init-param> <init-param> <param-name>serverName</param-name> <!--用于cas client访问域名--> <param-value>http://casTest01.com:8081</param-value> </init-param> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CASValidationFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 该过滤器负责实现HttpServletRequest请求的包裹, 比如允许开发者通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户的登录名,可选配置。 --> <filter> <filter-name>CASHttpServletRequest WrapperFilter</filter-name> <filter-class> org.jasig.cas.client.util.HttpServletRequestWrapperFilter </filter-class> </filter> <filter-mapping> <filter-name>CASHttpServletRequest WrapperFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 该过滤器使得开发者可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。 比如AssertionHolder.getAssertion().getPrincipal().getName()。 --> <filter> <filter-name>CASAssertion Thread LocalFilter</filter-name> <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class> </filter> <filter-mapping> <filter-name>CASAssertion Thread LocalFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- ========================单点登录结束 ======================== --> cketValidationFilter </filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>http://localhost:8080/</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://casTest01.com:8081</param-value> </init-param> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CASValidationFilter</fi<!-- ========================单点登录开始 ======================== --> <!--用于单点退出,该过滤器用于实现单点登出功能,可选配置 --> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <!--该过滤器用于实现单点登出功能,可选配置。并且需要服务端的cas.properties修改属性:cas.logout.followServiceRedirects=true --> <filter> <filter-name>CASSingle Sign OutFilter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> </filter> <filter-mapping> <filter-name>CASSingle Sign OutFilter</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> <!--cas server登录地址完整url --> <param-value>http://localhost:8080/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <!--用于cas server记录登录的 client访问域名,以及重定向返回 --> <param-value>http://casTest01.com:8081</param-value> </init-param> </filter> <filter-mapping> <filter-name>CASFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!--该过滤器负责对Ticket的校验工作,必须启用它 --> <filter> <filter-name>CASValidationFilter</filter-name> <filter-class> org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter </filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <!--用于cas server访问域名--> <param-value>http://localhost:8080/</param-value> </init-param> <init-param> <param-name>serverName</param-name> <!--用于cas client访问域名--> <param-value>http://casTest01.com:8081</param-value> </init-param> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CASValidationFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 该过滤器负责实现HttpServletRequest请求的包裹, 比如允许开发者通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户的登录名,可选配置。 --> <filter> <filter-name>CASHttpServletRequest WrapperFilter</filter-name> <filter-class> org.jasig.cas.client.util.HttpServletRequestWrapperFilter </filter-class> </filter> <filter-mapping> <filter-name>CASHttpServletRequest WrapperFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 该过滤器使得开发者可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。 比如AssertionHolder.getAssertion().getPrincipal().getName()。 --> <filter> <filter-name>CASAssertion Thread LocalFilter</filter-name> <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class> </filter> <filter-mapping> <filter-name>CASAssertion Thread LocalFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- ========================单点登录结束 ======================== --> lter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 该过滤器负责实现HttpServletRequest请求的包裹, 比如允许开发者通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户的登录名,可选配置。 --> <filter> <filter-name>CASHttpServletRequest WrapperFilter</filter-name> <filter-class> org.jasig.cas.client.util.HttpServletRequestWrapperFilter </filter-class> </filter> <filter-mapping> <filter-name>CASHttpServletRequest WrapperFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 该过滤器使得开发者可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。 比如AssertionHolder.getAssertion().getPrincipal().getName()。 --> <filter> <filter-name>CASAssertion Thread LocalFilter</filter-name> <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class> </filter> <filter-mapping> <filter-name>CASAssertion Thread LocalFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- ========================单点登录结束 ======================== -->
番外篇!--与shibboleth idp saml2.0 联合配置
1.工作环境准备
1.1 运行基础
jdk8.0.131 tomcat8.5.16 cas-4.2.1(基于gradle) ,shibboleth-identityprovider-2.4.5,win7 64,mysql 5.6.21,花生壳免费域名映射(否则自己需要再下一个shibboleth的sp服务器用于测试)
1.2 ssl配置
tomcat必须配置ssl连接,此tomcat用于启动cas-server和shib-idp(我一个是用idea启动,ssl访问端口设置不同而已),参考上面的《本地tomcat证书生成与配置》
2. 安装shibboleth-identityprovider-2.4.5
参考文档:https://wiki.shibboleth.net/confluence/display/SHIB2/IdPInstall
2.1 添加cas client 包支持
把cas的客户端jar包(cas-client-core-3.4.1.jar)丢到idp解压的lib目录下,例如:D:\\Program Files (x86)\\shibboleth-identityprovider-2.4.5\\lib,这样安装好的idp就会有这个jar包
2.2 执行install.bat安装idp
2.3 修改idp的元数据文件
D:\\Program Files (x86)\\shibboleth-idp-server\\metadata\\idp-metadata.xml
这里面的几个idp接口访问端口要改一下,因为我用的花生壳所以端口号是指定好了的,把文件里面的:8443全部替换成空串即可。
3.cas-server所需配合部分
上面先告一段落,开始修改cas-server的整合部分:
参考:https://apereo.github.io/cas/4.2.x/integration/Shibboleth.html
3.1 修改idp安装目录配置文件
idp安装目录/conf/handler.xml
把里面的ph:LoginHandler xsi:type="ph:RemoteUser"节点替换成以下内容:
<!-- Remote User handler for CAS support --> <ph:LoginHandler xsi:type="ph:RemoteUser"> <ph:AuthenticationMethod> urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified </ph:AuthenticationMethod> <ph:AuthenticationMethod> urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport </ph:AuthenticationMethod> </ph:LoginHandler>
3.2 修改idp.war包的web.xml文件
增加以下内容:
<!-- For CAS client support --> <context-param> <param-name>serverName</param-name> <param-value>${idp域名+端口}</param-value> </context-param> <!-- CAS client filters --> <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>${cas服务器域名加端口加项目名}/login</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/Authn/RemoteUser</url-pattern> </filter-mapping> <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>${cas服务器域名加端口加项目名}</param-value> </init-param> <init-param> <param-name>redirectAfterValidation</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/Authn/RemoteUser</url-pattern> </filter-mapping> <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>/Authn/RemoteUser</url-pattern> </filter-mapping>
3.3 cas-server增加saml2.0支持包 cas-server-support-saml-mdui
3.4 cas-server-webapp的deployerConfigContext.xml添加:
<!--saml2.0支持--> <bean id="samlMetadataUIParserAction" class="org.jasig.cas.support.saml.web.flow.mdui.SamlMetadataUIParserAction" c:entityIdParameterName="entityId" c:metadataAdapter-ref="metadataAdapter"/> <!--元数据配置--> <bean id="metadataAdapter" class="org.jasig.cas.support.saml.web.flow.mdui.StaticMetadataResolverAdapter" c:metadataResources-ref="metadataResources" p:refreshIntervalInMinutes="300" p:requireValidMetadata="true" /> <!--元数据源配置,动态获取idp上的元数据--> <util:map id="metadataResources"> <entry key="${完整idp域名https访问地址包括项目名}/entities/"> <bean class="org.opensaml.saml.metadata.resolver.filter.impl.MetadataFilterChain"> <property name="filters"> <list /> </property> </bean> </entry> </util:map> <!--元数据过滤器配置--> <bean id="metadataFilters" class="org.opensaml.saml.metadata.resolver.filter.impl.MetadataFilterChain"> <property name="filters"> <list> <!-- <bean class="org.opensaml.saml.metadata.resolver.filter.impl.RequiredValidUntilFilter" c:maxValidity="0" /> --> <bean class="org.opensaml.saml.metadata.resolver.filter.impl.SignatureValidationFilter" c:engine-ref="trustEngine" p:requireSignature="false" /> </list> </property> </bean> <bean id="trustEngine" class="org.opensaml.xmlsec.signature.support.impl.ExplicitKeySignatureTrustEngine" c:keyInfoResolver-ref="keyInfoResolver" c:resolver-ref="credentialResolver" /> <bean id="keyInfoResolver" class="org.opensaml.xmlsec.keyinfo.impl.BasicProviderKeyInfoCredentialResolver"> <constructor-arg name="keyInfoProviders"> <list> <bean class="org.opensaml.xmlsec.keyinfo.impl.provider.RSAKeyValueProvider" /> <bean class="org.opensaml.xmlsec.keyinfo.impl.provider.DSAKeyValueProvider" /> <bean class="org.opensaml.xmlsec.keyinfo.impl.provider.DEREncodedKeyValueProvider" /> <bean class="org.opensaml.xmlsec.keyinfo.impl.provider.InlineX509DataProvider" /> </list> </constructor-arg> </bean> <bean id="credentialResolver" class="org.opensaml.security.credential.impl.StaticCredentialResolver" c:credential-ref="credentialFactoryBean" /> <bean id="credentialFactoryBean" class="net.shibboleth.idp.profile.spring.relyingparty.security.credential.BasicResourceCredentialFactoryBean" p:publicKeyInfo="classpath:inc-md-pub.pem" ></bean>
3.5 修改cas-server-webapp的login-webflow.xml内容
在view-state id="viewLoginForm" 节点提交动作新增saml2.0校验,修改大致如下:
<view-state id="viewLoginForm" view="casLoginView" model="credential"> <binder> <binding property="username" required="true"/> <binding property="password" required="true"/> <!-- <binding property="rememberMe" /> --> </binder> <on-entry> <set name="viewScope.commandName" value="‘credential‘"/> <!--进入该视图节点时,进行saml2.0元数据校验--> <evaluate expression="samlMetadataUIParserAction" /> </on-entry> <transition on="submit" bind="true" validate="true" to="realSubmit"/> </view-state>
4.使用testshib作为sp端免费测试
参考教程:https://www.testshib.org/register.html
4.1 将我们的cas-server和idp.war都部署到开启了ssl的tomcat上进行启动吧!然后开启我们的花生壳内网映射!
4.2 开始上传idp端元数据文件
4.3 修改idp端的元数据提供者配置
在idp安装目录下找到D:\\Program Files (x86)\\shibboleth-idp-server\\conf\\relying-party.xml
在节点<metadata:MetadataProvider id="ShibbolethMetadata" xsi:type="metadata:ChainingMetadataProvider">内插入testshib的idp元数据提供,插入后是这样子:
<metadata:MetadataProvider id="ShibbolethMetadata" xsi:type="metadata:ChainingMetadataProvider"> <!-- Load the IdP‘s own metadata. This is necessary for artifact support. --> <metadata:MetadataProvider id="IdPMD" xsi:type="metadata:FilesystemMetadataProvider" metadataFile="D:\\Program Files (x86)\\shibboleth-idp-server/metadata/idp-metadata.xml" maxRefreshDelay="P1D" /> <!--test shib--> <metadata:MetadataProvider id="HTTPMetadataTESTSHIB" xsi:type="metadata:FileBackedHTTPMetadataProvider" backingFile="D:\\Program Files (x86)\\shibboleth-idp-server/metadata/testshib-providers.xml" metadataURL="http://www.testshib.org/metadata/testshib-providers.xml"/>
4.4 开始测试
访问测试sp地址:https://sp.testshib.org/,在修改下方的idp访问地址,go!
以上是关于原创apereo cas 4.2从0开始的主要内容,如果未能解决你的问题,请参考以下文章
史上最详细的 Apereo CAS 5.3开发教程:二Apereo CAS 5.3 Server环境搭建,登录名,密码从数据库中获取
CAS 5.x搭建常见问题系列.Failure to find org.apereo.cas:cas-server-support-pm-jdbc:jar:5.1.9