cas server 實現LDAP、資料庫認證
阿新 • • 發佈:2018-11-25
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();
}