1. 程式人生 > >cas server 實現LDAP、資料庫認證

cas server 實現LDAP、資料庫認證

cas server(4.2.7) + client相關的配置 

  1. 支援資料庫驗證使用者
  2. 支援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();
            }