cas server(4.2.7) + client相关的配置
- 支持数据库验证用户
- 支持LDAP验证用户(LDAP相关安装请相见:https://blog.csdn.net/u011196623/article/details/82502570)
cas server 端 数据库验证
- 支持数据库验证用户,在deployerConfigContext.xml中配置
<!--数据库认证 开始-->
<alias name="personDirectoryPrincipalResolver" alias="primaryPrincipalResolver" />
<bean id="myauthhandler" class="org.jasig.cas.jdbc.hander.JdbcUsernamePasswordAuthHandlerImpl">
<property name="dataSource" ref="dataSource" /><!--指定数据源-->
<!--设置加密方式,这里要看数据库中存储的密码的加密方式是什么,要配置相应的加密器-->
<property ref="MyPasswordEncoder" name="passwordEncoder"></property>
<!--<property name="maxFailureTimes" value="3" /> -->
<!-- 这里对应JdbcUsernamePasswordAuthHandlerImpl类里的maxFailureTimes属性 -->
</bean>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:/comp/env/DBSERVER" />
<property name="lookupOnStartup" value="true"/>
<property name="resourceRef" value="false" />
</bean>
<bean id="MyPasswordEncoder" class="org.jasig.cas.authentication.handler.MyPasswordEncoder"></bean>
<!--数据库认证 结束->
<util:map id="authenticationHandlersResolvers">
<!-- proxyPrincipalResolver对应的类为BasicPrincipalResolver
proxyAuthenticationHandler对应的类 HttpBasedServiceCredentialsAuthenticationHandler
primaryPrincipalResolver对应的类为personDirectoryPrincipalResolver -->
<entry key-ref="myauthhandler" value-ref="proxyPrincipalResolver" />
<entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" />
<entry key-ref="ldapAuthHandler" value-ref="proxyPrincipalResolver" /> <!--新增ldap认证的入口 -->
<!--primaryAuthenticationHandler为类QueryDatabaseAuthenticationHandler,primaryPrincipalResolver为类 PersonDirectoryPrincipalResolver <entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" /> -->
</util:map>
- 重写JdbcUsernamePasswordAuthHandlerImpl实现自定义的功能,例如账号密码错误多少次后账号封停等。
- 生成证书 在 sso-cas-server-client\cas-server\webapps\cas\tools\gen-cer-store.sh下面由对应的示例
HOST_NAME=`hostname` // 需要与cas client中的cas.properties的casserver.hostname一直
SERVER_DN="CN=sso, OU=Linkage, O=Linkage, L=Nanjing, S=Jiangsu, C=CN"
KS_PASS="-storepass changeit"
KEYINFO="-keyalg RSA"
LIB_PATH="/home/soft/jdk/jre/lib/security/cacerts"
keytool -genkey -alias casserver -dname "$SERVER_DN" $KS_PASS -keystore server.keystore $KEYINFO -keypass changeit -validity 3600
keytool -export -trustcacerts -alias casserver -file server.cer $KS_PASS -keystore server.keystore
keytool -import -trustcacerts -alias casserver -file server.cer $KS_PASS -keystore $LIB_PATH
此时会生成server.keystore和server.cer 2个证书,
server.keystore将文件复制到tomcat/conf下面,此证书放在服务端。
将server.cer文件复制到tomcat/bin下面,此证书放在客户端,然后在客户端的tomcat/bin下执行
$JAVA_HOME是jdk路径,防止意外先执行删除
keytool -delete -alias casserver -keystore "$JAVA_HOME/jre/lib/security/cacerts" -storepass changeit
在执行导入
keytool -import -alias casserver -file "server.cer" -keystore "$JAVA_HOME/jre/lib/security/cacerts"
示例
keytool -import -alias casserver -file "server.cer" -keystore "/home/soft/jdk/jre/lib/security/cacerts"
- 在服务端的tomcat的conf/server.xml 配置https,在<GlobalNamingResources>里面配置Resource ,注意此处的name ="DBSERVER" 应与deployerConfigContext.xml配置中的jndiName对应的value,java:/comp/env/DBSERVER 一致
<Connector port="443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" keystoreFile="./conf/server.keystore" keyAlias="casserver" keystorePass="changeit" sslProtocol="TLS" />
<Resource name="DBSERVER"
type="javax.sql.DataSource"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://192.168.11.144:3306/demo?useSSL=true"
username="xxx"
password="xxx"
maxIdle="40"
maxWait="4000"
maxActive="250"
removeAbandoned="true"
removeAbandonedTimeout="180"
logAbandoned="true"
factory="org.apache.commons.dbcp.BasicDataSourceFactory" />
- 由于生成证书是依赖与域名,gen-cer-store.sh 脚本中的 SERVER_DN="CN=sso ...", CN即是域名。那么此时需要在客户端的hosts文件中添加sso域名解析的IP
-
在tomcat的conf/context.xml.里面添加
<ResourceLink global=" DBSERVER " name=" DBSERVER" type="javax.sql.DataSource"/>
cas server 端 LDAP验证
- 在 deployerConfigContext.xml配置中 将数据库认证注释掉,然后再authenticationHandlersResolvers中配置
<util:map id="authenticationHandlersResolvers">
<!-- proxyPrincipalResolver对应的类为BasicPrincipalResolver
proxyAuthenticationHandler对应的类 HttpBasedServiceCredentialsAuthenticationHandler
primaryPrincipalResolver对应的类为personDirectoryPrincipalResolver -->
<!-- <entry key-ref="myauthhandler" value-ref="proxyPrincipalResolver" />--> <!--数据库认证-->
<entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" />
<entry key-ref="ldapAuthHandler" value-ref="proxyPrincipalResolver" /> <!--新增ldap认证的入口 -->
<!--primaryAuthenticationHandler为类QueryDatabaseAuthenticationHandler,primaryPrincipalResolver为类 PersonDirectoryPrincipalResolver <entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" /> -->
</util:map>
<!--LDAP begin-->
<bean id="ldapAuthHandler" class="org.jasig.cas.authentication.handler.LdapAuthentication"
p:principalIdAttribute="uid"
c:authenticator-ref="authenticator">
<property name="principalAttributeMap">
<map>
<!-- | This map provides a simple attribute resolution mechanism. | Keys
are LDAP attribute names, values are CAS attribute names. | Use this facility
instead of a PrincipalResolver if LDAP is | the only attribute source. -->
<entry key="uid" value="username" />
<entry key="cn" value="commonname" />
<entry key="mail" value="mail" />
<entry key="userPassword" value="password" />
<!--
<entry key="member" value="member" />
<entry key="displayName" value="displayName" />
-->
</map>
</property>
</bean>
<bean id="authenticator" class="org.ldaptive.auth.Authenticator"
c:resolver-ref="dnResolver" c:handler-ref="authHandler" />
<bean id="dnResolver" class="org.ldaptive.auth.PooledSearchDnResolver"
p:baseDn="${ldap.authn.baseDn}"
p:subtreeSearch="true"
p:allowMultipleDns="false"
p:connectionFactory-ref="searchPooledLdapConnectionFactory"
p:userFilter="${ldap.authn.searchFilter}" />
<bean id="searchPooledLdapConnectionFactory" class="org.ldaptive.pool.PooledConnectionFactory"
p:connectionPool-ref="searchConnectionPool" />
<bean id="searchConnectionPool" parent="abstractConnectionPool"
p:connectionFactory-ref="searchConnectionFactory" />
<bean id="searchConnectionFactory" class="org.ldaptive.DefaultConnectionFactory"
p:connectionConfig-ref="searchConnectionConfig" />
<bean id="searchConnectionConfig" parent="abstractConnectionConfig"
p:connectionInitializer-ref="bindConnectionInitializer" />
<bean id="bindConnectionInitializer" class="org.ldaptive.BindConnectionInitializer"
p:bindDn="${ldap.authn.managerDN}">
<property name="bindCredential">
<bean class="org.ldaptive.Credential" c:password="${ldap.authn.managerPassword}" />
</property>
</bean>
<bean id="abstractConnectionPool"
abstract="true"
class="org.ldaptive.pool.BlockingConnectionPool"
init-method="initialize"
destroy-method="close"
p:poolConfig-ref="ldapPoolConfig"
p:validator-ref="searchValidator"
/> <!-- p:pruneStrategy-ref="pruneStrategy" p:blockWaitTime="${ldap.pool.blockWaitTime}"-->
<bean id="abstractConnectionConfig" abstract="true" class="org.ldaptive.ConnectionConfig"
p:ldapUrl="${ldap.url}"
p:useStartTLS="${ldap.useStartTLS}"/>
<!-- p:connectTimeout="${ldap.connectTimeout}"
p:sslConfig-ref="sslConfig" />-->
<bean id="ldapPoolConfig" class="org.ldaptive.pool.PoolConfig"
p:minPoolSize="${ldap.pool.minSize}" p:maxPoolSize="${ldap.pool.maxSize}"
p:validateOnCheckOut="${ldap.pool.validateOnCheckout}"
p:validatePeriodically="${ldap.pool.validatePeriodically}"
/>
<!-- <bean id="sslConfig" class="org.ldaptive.ssl.SslConfig">
<property name="credentialConfig">
<bean class="org.ldaptive.ssl.X509CredentialConfig"
p:trustCertificates="${ldap.trustedCert}" />
</property>
</bean>-->
<bean id="searchValidator" class="org.ldaptive.pool.SearchValidator" />
<bean id="authHandler" class="org.ldaptive.auth.PooledBindAuthenticationHandler"
p:connectionFactory-ref="bindPooledLdapConnectionFactory" />
<bean id="bindPooledLdapConnectionFactory" class="org.ldaptive.pool.PooledConnectionFactory"
p:connectionPool-ref="bindConnectionPool" />
<bean id="bindConnectionPool" parent="abstractConnectionPool"
p:connectionFactory-ref="bindConnectionFactory" />
<bean id="bindConnectionFactory" class="org.ldaptive.DefaultConnectionFactory"
p:connectionConfig-ref="bindConnectionConfig" />
<bean id="bindConnectionConfig" parent="abstractConnectionConfig" />
<!--LDAP end-->
- LdapAuthentication为自定义验证用户 extends LdapAuthenticationHandler,然后再此类中实现了账号密码错误3次,账号封停20分钟,如不需要自定义,则直接配置
<bean id="ldapAuthHandler" class="org.jasig.cas.authentication.LdapAuthenticationHandler"
cas client 端 集成
- 在pom文件中添加
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.5.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas-client</artifactId>
<version>3.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
- 新增cas.properties 配置文件
casserver.ip=192.168.2.1 // sso server的ip
casserver.port=443 // sso server的端口号
casserver.hostname=hostname // sso server的hostname,生成证书的时候用到
casclient.url=192.168.11.1/applicatioon
- 新增 springsecurity.xml 文件内容如下
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/beans" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd">
<!--下面是不需要验证,不会通过cas server验证-->
<security:http pattern="/css/**" security="none" />
<security:http pattern="/js/**" security="none" />
<security:http pattern="/images/**" security="none" />
<security:http pattern="/bootstrap/**" security="none" />
<security:http pattern="/fonts/**" security="none" />
<security:http pattern="/jquery/**" security="none" />
<security:http pattern="/ligerUI/**" security="none" />
<security:http pattern="/sound/**" security="none" />
<security:http pattern="/vender/**" security="none" />
<security:http entry-point-ref="casProcessingFilterEntryPoint">
<!-- <security:intercept-url pattern="/**" access="hasRole('ROLE_USER')" /> -->
<security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />
<!-- <security:intercept-url pattern="/" access="permitAll" /> -->
<security:custom-filter ref="requestCasLogoutFilter" before="LOGOUT_FILTER" />
<security:custom-filter ref="singleLogoutFilter" before="CAS_FILTER" />
<security:custom-filter ref="casFilter" position="CAS_FILTER"/>
<!-- <security:logout invalidate-session="true" logout-success-url="https://${casserver.ip}:${casserver.port}/cas/logout?service=${casclient.url}" logout-url="/j_spring_security_logout" /> -->
<security:csrf disabled="true"/>
<!-- <security:logout logout-success-url="https://${casserver.ip}:${casserver.port}/cas/logout?service=${casclient.url}" invalidate-session="true" /> -->
</security:http>
<beans:bean id="casProcessingFilterEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<beans:property name="loginUrl" value="https://${casserver.ip}:${casserver.port}/cas/login" />
<beans:property name="serviceProperties" ref="serviceProperties" />
</beans:bean>
<beans:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<beans:property name="service" value="${casclient.url}/j_spring_cas_security_check" />
<beans:property name="sendRenew" value="false" />
</beans:bean>
<beans:bean id="casFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="authenticationFailureHandler">
<beans:bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/WEB-INF/view/caserror.jsp"/>
</beans:bean>
</beans:property>
<beans:property name="filterProcessesUrl" value="/j_spring_cas_security_check"/>
</beans:bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="casAuthenticationProvider"/>
</security:authentication-manager>
<beans:bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<!-- <custom-authentication-provider /> -->
<beans:property name="authenticationUserDetailsService">
<beans:bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<beans:constructor-arg ref="userDetailsService" />
</beans:bean>
</beans:property>
<beans:property name="serviceProperties" ref="serviceProperties" />
<beans:property name="ticketValidator">
<beans:bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<!-- <beans:constructor-arg index="0" value="http://${casserver.hostname}:${casserver.port}/cas" />-->
<beans:constructor-arg index="0" value="https://${casserver.hostname}:${casserver.port}/cas" />
</beans:bean>
</beans:property>
<beans:property name="key" value="an_id_for_this_auth_provider_only" />
</beans:bean>
<beans:bean id="userDetailsService"
class="com.ais.esns.service.SyUserDetailsService">
</beans:bean>
<beans:bean id="loggerListener" class="org.springframework.security.authentication.event.LoggerListener" />
<!-- <bean id="casLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/> -->
<!-- 认证提供者 -->
<beans:bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter" />
<beans:bean id="requestCasLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<!-- 指定登出成功后需要跳转的地址,这里指向Cas Server的登出URL,以实现单点登出 -->
<beans:constructor-arg value="https://${casserver.ip}:${casserver.port}/cas/logout?service=${casclient.url}"/>
<beans:constructor-arg>
<beans:bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
</beans:constructor-arg>
<!-- 该Filter需要处理的地址,默认是Spring Security的默认登出地址“/j_spring_security_logout”-->
<beans:property name="filterProcessesUrl" value="/j_spring_security_logout"/>
</beans:bean>
<!-- <beans:property name="casServerUrlPrefix" value="https://${casserver.ip}:${casserver.port}/cas"></beans:property> -->
<!-- </beans:bean> -->
</beans:beans>
- 在客户端的web.xml中配置
<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 客户端如何获取sso的登录名
// request 是HttpServletRequest
Object securityContext= request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
if(securityContext!=null&&securityContext instanceof SecurityContextImpl){
SecurityContextImpl sContextImpl=(SecurityContextImpl)securityContext;
userName=sContextImpl.getAuthentication().getName();
}
来源:oschina
链接:https://my.oschina.net/u/3370769/blog/2032409