CAS 身份验证后在 JSF 托管 Bean 中获取 LDAP 属性

Posted

技术标签:

【中文标题】CAS 身份验证后在 JSF 托管 Bean 中获取 LDAP 属性【英文标题】:Getting LDAP attributes in JSF Managed Bean after CAS authentication 【发布时间】:2015-01-11 18:48:09 【问题描述】:

我正在为我的应用程序使用 CAS 身份验证。我将我的用户详细信息存储在 LDAP Active Directory 中。我在我的 Web 应用程序中使用 Spring Security 和 JSF。我发现很难将 LDAP 属性(如国家/地区代码)获取到托管 bean。我能够从 SecurityContext 检索角色、用户名、密码,但我无法从 ldap 获取登录用户的国家/地区详细信息。

我相信CAS认证后一定有办法,可以通过CAS检索到JSF托管bean的ldap属性。我尝试了以下链接,但无法获取托管 bean 的详细信息。

我的 CAS 确实从 LDAP 获取角色,但我不希望我的网络应用程序与 LDAP 对话。

可以配置spring security + CAS来获取ldap属性吗?

有人可以帮助我在 CAS 身份验证后获取国家/地区等 ldap 属性吗?

Get LDAP user attributes from CAS

我已经附加了我的 CAS deployerConfigContext.xml

            <?xml version="1.0" encoding="UTF-8"?>



            <beans xmlns="http://www.springframework.org/schema/beans"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns:p="http://www.springframework.org/schema/p"
                   xmlns:c="http://www.springframework.org/schema/c"
                   xmlns:tx="http://www.springframework.org/schema/tx"
                   xmlns:util="http://www.springframework.org/schema/util"
                   xmlns:sec="http://www.springframework.org/schema/security"
                   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
                   http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
                   http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">



                 <bean id="authenticationManager" class="xxx.cas.authentication.XXXOTPPolicyBasedAuthenticationManager">

                    <constructor-arg index="0">
                        <list value-type="org.jasig.cas.authentication.AuthenticationHandler" >
                            <ref local="ldapAuthenticationHandler"/>
                            <ref local="radiusAuthenticationHandler"/>
                        </list>
                    </constructor-arg> 


                    <property name="authenticationPolicy">
                        <bean class="xxx.cas.authentication.XXXAllAuthenticationPolicy" />
                    </property>
                </bean>

                <!-- Required for proxy ticket mechanism. -->
                <bean id="proxyAuthenticationHandler"
                      class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
                      p:httpClient-ref="httpClient" p:requireSecure="false" />



                   <!--
                   | Change principalIdAttribute to use another directory attribute,
                   | e.g. userPrincipalName, for the NetID
                   -->
                <bean id="ldapAuthenticationHandler"
                      class="org.jasig.cas.authentication.LdapAuthenticationHandler"
                      p:principalIdAttribute="sAMAccountName"
                      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="displayName" value="displayName" />
                            <entry key="facsimileTelephoneNumber" value="facsimileTelephoneNumber" />
                            <entry key="memberOf" value="memberOf" />
                            <entry key="co" value="co" />
                            <entry key="c" value="c" />
                            <entry key="mail" value="mail" />
                            <entry key="description" value="role" />
                        </map>
                    </property>
                </bean>



                <!-- Required for proxy ticket mechanism -->
                <bean id="proxyPrincipalResolver"
                      class="org.jasig.cas.authentication.principal.BasicPrincipalResolver" />


                <!-- Radius authentication -->
                <bean id="radiusClientFactory"
                      class="org.jasig.cas.adaptors.radius.RadiusClientFactory"
                      p:inetAddress="$fourtress.server"
                      p:sharedSecret="$fourtress.ss" />

                <bean id="radiusServer"
                      class="org.jasig.cas.adaptors.radius.JRadiusServerImpl"
                      c:protocol="PAP"
                      c:clientFactory-ref="radiusClientFactory" />

                <bean id="radiusAuthenticationHandler"
                      class="xxx.cas.authentication.XXXRadiusAuthenticationHandler">
                  <property name="servers">
                      <list>
                          <ref local="radiusServer" />
                      </list>
                  </property>
                </bean>




                <bean id="attributeRepository"
                  class="org.jasig.cas.persondir.LdapPersonAttributeDao"
                  p:connectionFactory-ref="pooledLdapConnectionFactory"
                  p:baseDN="ou=users,ou=egate,dc=egate-t,dc=local" p:searchControls-ref="searchControls" p:searchFilter="sAMAccountName=0">

                    <property name="requireAllQueryAttributes" value="true"/>
                    <property name="queryAttributeMapping">
                        <map>
                            <!-- Attribute mapping between principal (key) and LDAP (value) names used to perform the LDAP search -->
                            <entry key="username" value="sAMAccountName" />
                        </map>
                    </property>
                    <property name="resultAttributeMapping">
                        <map>
                            <!-- Mapping between LDAP entry attributes (key) and Principal's (value) -->
                            <entry key="memberOf" value="memberOf" />
                            <entry key="mail" value="mail" />
                            <entry key="cn" value="FullName" />
                            <entry key="sn" value="LastName" />
                            <entry key="displayName" value="displayName" />
                            <entry key="description" value="role" />
                            <entry key="facsimileTelephoneNumber" value="facsimileTelephoneNumber" />
                            <entry key="co" value="country" />
                            <entry key="c" value="countryCode" />

                        </map>
                    </property>
                </bean>

                <bean id="searchControls"
                      class="javax.naming.directory.SearchControls"
                      p:searchScope="2"
                      p:countLimit="0" p:timeLimit="0" />


                <!--
                Sample, in-memory data store for the ServiceRegistry. A real implementation
                would probably want to replace this with the JPA-backed ServiceRegistry DAO
                The name of this bean should remain "serviceRegistryDao".
                +-->
                <bean id="serviceRegistryDao" class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl"
                        p:registeredServices-ref="registeredServicesList" />

                <util:list id="registeredServicesList">

                    <bean class="org.jasig.cas.services.RegexRegisteredService">
                        <property name="id" value="1" />
                        <property name="name" value="cassimple" />
                        <property name="description" value="cassimple application 1" />
                        <property name="serviceId" value="^(http?|https?|imaps?)://localhost:7002/cassimple/.*" />
                        <property name="evaluationOrder" value="10000001" />
                    </bean>

                    <bean class="org.jasig.cas.services.RegexRegisteredService">
                        <property name="id" value="2" />
                        <property name="name" value="casldap" />
                        <property name="description" value="casldap application 2" />
                        <property name="serviceId" value="^(http?|https?|imaps?)://localhost:7002/casldap/.*" />
                        <property name="evaluationOrder" value="10000002" />
                        <property name="allowedAttributes">
                            <list>
                                <value>memberOf</value>
                                <value>LastName</value>
                                <value>FullName</value>
                                <value>displayName</value>
                                <value>role</value>
                            </list>
                        </property>            
                    </bean>

                    <bean class="org.jasig.cas.services.RegexRegisteredService">
                        <property name="id" value="3" />
                        <property name="name" value="cascir" />
                        <property name="description" value="cas cir application 3" />
                        <property name="serviceId" value="^(http?|https?|imaps?)://localhost:7002/cascir/.*" />
                        <property name="evaluationOrder" value="10000003" />
                        <property name="allowedAttributes">
                            <list>
                                <value>role</value>
                                <value>FullName</value>
                                <value>displayName</value>
                                <value>LastName</value>
                                <value>memberOf</value>
                            </list>
                        </property>            
                    </bean>

                    <bean class="org.jasig.cas.services.RegexRegisteredService">
                        <property name="id" value="4" />
                        <property name="name" value="egate" />
                        <property name="description" value="cas egate application 4" />
                        <property name="serviceId" value="^(http?|https?|imaps?)://localhost:7002/egate/.*" />
                        <property name="evaluationOrder" value="10000004" />
                        <property name="allowedAttributes">
                            <list>
                                <value>role</value>
                                <value>FullName</value>
                                <value>displayName</value>
                                <value>LastName</value>
                                <value>memberOf</value>
                            </list>
                        </property>            
                    </bean>

                    <bean class="org.jasig.cas.services.RegexRegisteredService">
                        <property name="id" value="5" />
                        <property name="name" value="cir" />
                        <property name="description" value="cas cir application 5" />
                        <property name="serviceId" value="^(http?|https?|imaps?)://localhost:7002/cir/.*" />
                        <property name="evaluationOrder" value="10000005" />
                        <property name="allowedAttributes">
                            <list>
                                <value>role</value>
                                <value>FullName</value>
                                <value>displayName</value>
                                <value>LastName</value>
                                <value>memberOf</value>
                                <value>country</value>
                                <value>countryCode</value>
                            </list>
                        </property>            
                    </bean>





                </util:list>

                <bean id="auditTrailManager" class="com.github.inspektr.audit.support.Slf4jLoggingAuditTrailManager" />

                <bean id="healthCheckMonitor" class="org.jasig.cas.monitor.HealthCheckMonitor" p:monitors-ref="monitorsList" />

                <util:list id="monitorsList">
                  <bean class="org.jasig.cas.monitor.MemoryMonitor" p:freeMemoryWarnThreshold="10" />
                  <!--
                    NOTE
                    The following ticket registries support SessionMonitor:
                      * DefaultTicketRegistry
                      * JpaTicketRegistry
                    Remove this monitor if you use an unsupported registry.
                  -->
                  <bean class="org.jasig.cas.monitor.SessionMonitor"
                      p:ticketRegistry-ref="ticketRegistry"
                      p:serviceTicketCountWarnThreshold="5000"
                      p:sessionCountWarnThreshold="100000" />
                </util:list>


                <bean id="authenticator" class="org.ldaptive.auth.Authenticator"
                      c:resolver-ref="dnResolver"
                      c:handler-ref="authHandler"
                      p:entryResolver-ref="entryResolver" />

                <!-- Active Directory UPN format. -->
                <bean id="dnResolver"
                      class="org.ldaptive.auth.FormatDnResolver"
                      c:format="$ldap.authn.format" />

                <bean id="authHandler" class="org.ldaptive.auth.PooledBindAuthenticationHandler"
                      p:connectionFactory-ref="pooledLdapConnectionFactory" />

                <bean id="pooledLdapConnectionFactory" 
                      class="org.ldaptive.pool.PooledConnectionFactory"
                      p:connectionPool-ref="connectionPool" />


                <bean id="connectionPool"
                      class="org.ldaptive.pool.BlockingConnectionPool"
                      init-method="initialize"
                      p:poolConfig-ref="ldapPoolConfig"
                      p:blockWaitTime="$ldap.pool.blockWaitTime"
                      p:validator-ref="searchValidator"
                      p:pruneStrategy-ref="pruneStrategy"
                      p:connectionFactory-ref="connectionFactory" />

                <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"
                      p:validatePeriod="$ldap.pool.validatePeriod" />

                <bean id="connectionFactory" class="org.ldaptive.DefaultConnectionFactory"
                      p:connectionConfig-ref="connectionConfig" />

                <bean id="connectionConfig" class="org.ldaptive.ConnectionConfig"
                      p:ldapUrl="$ldap.url"
                      p:connectTimeout="$ldap.connectTimeout"
                      p:useStartTLS="$ldap.useStartTLS"
                      p:sslConfig-ref="sslConfig"/>

                <bean id="sslConfig" class="org.ldaptive.ssl.SslConfig">
                    <property name="credentialConfig">
                        <bean class="org.ldaptive.ssl.X509CredentialConfig"
                              p:trustCertificates="classpath:$ldap.trustedCert" />
                    </property>
                </bean>

                <bean id="pruneStrategy" class="org.ldaptive.pool.IdlePruneStrategy"
                      p:prunePeriod="$ldap.pool.prunePeriod"
                      p:idleTime="$ldap.pool.idleTime" />

                <bean id="searchValidator" class="org.ldaptive.pool.SearchValidator" />

                <bean id="entryResolver"
                      class="org.jasig.cas.authentication.support.UpnSearchEntryResolver"
                      p:baseDn="$ldap.baseDn" />


            </beans>

我的spring security.xml

        <beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans"
            xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
            http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

            <debug />


            <global-method-security secured-annotations="enabled" />

            <http auto-config="false" use-expressions="true"
                entry-point-ref="casAuthenticationEntryPoint">

                <custom-filter position="CAS_FILTER" ref="casAuthenticationFilter" />


                <intercept-url pattern="/faces/disclaimer**" access="permitAll" />
                <intercept-url pattern="/faces/searchCreditInstitution**"
                    access="permitAll" />
                <intercept-url pattern="/faces/searchParentInstitution**"
                    access="hasAnyRole('ROLE_CIR_EDITOR','ROLE_CIR_AUTHORISER')" />
                <intercept-url pattern="/faces/createCreditInstitution**"
                    access="hasAuthority('ROLE_CIR_EDITOR')" />
                <intercept-url pattern="/faces/authorisation**"
                    access="hasAuthority('ROLE_CIR_AUTHORISER')" />
                <intercept-url pattern="/faces/rejected**" access="hasAuthority('ROLE_CIR_EDITOR')" />
                <intercept-url pattern="/faces/pendingApproval**"
                    access="hasAuthority('ROLE_CIR_EDITOR')" />
                <intercept-url pattern="/faces/auditLog**"
                    access="hasAuthority('ROLE_CIR_AUTHORISER')" />
                <intercept-url pattern="/faces/enquiry**"
                    access="hasAnyRole('ROLE_CIR_EDITOR','ROLE_CIR_AUTHORISER','ROLE_CIR_XXXOPS')" />
                <intercept-url pattern="/faces/changePassword**"
                    access="hasAnyRole('ROLE_CIR_EDITOR','ROLE_CIR_AUTHORISER','ROLE_CIR_XXXOPS')" />
                <intercept-url pattern="/faces/dashboard**" access="ROLE_CIR_XXXOPS" />
                <intercept-url pattern="/resources**" access="permitAll" />
                <intercept-url pattern="/faces/javax.faces.resource**" access="permitAll" />
                <logout logout-url="/logout" logout-success-url="https://localhost:7002/cas/logout" />

            </http>

            <beans:bean id="serviceProperties"
                class="org.springframework.security.cas.ServiceProperties">
                <beans:property name="service"
                    value="https://localhost:7002/cir/j_spring_cas_security_check" />
                <beans:property name="sendRenew" value="false" />
            </beans:bean>

            <beans:bean id="casAuthenticationEntryPoint"
                class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
                <beans:property name="loginUrl" value="https://localhost:7002/cas/login" />
                <beans:property name="serviceProperties" ref="serviceProperties" />
            </beans:bean>

            <beans:bean id="casAuthenticationFilter"
                class="org.springframework.security.cas.web.CasAuthenticationFilter">
                <beans:property name="authenticationManager" ref="casAuthenticationManager" />
            </beans:bean>

            <authentication-manager alias="casAuthenticationManager">
                <authentication-provider ref="casAuthenticationProvider" />
            </authentication-manager>

            <beans:bean id="casAuthenticationProvider"
                class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
                <beans:property name="serviceProperties" ref="serviceProperties" />
                <beans:property name="ticketValidator" ref="ticketValidator" />
                <beans:property name="authenticationUserDetailsService"
                    ref="authenticationUserDetailsService" />
                <beans:property name="key" value="cir" />
            </beans:bean>

            <beans:bean id="ticketValidator"
                class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
                <beans:constructor-arg index="0"
                    value="https://localhost:7002/cas">
                </beans:constructor-arg>
            </beans:bean>

            <beans:bean id="authenticationUserDetailsService"
                class="XXX.cir.cas.authentication.XXXCasAuthenticationUserDetailsService" />

        </beans:beans>

我可以通过这种方式获取 JSF 托管 bean 中的 Ldap 角色、用户名,但不能获取国家/地区

                SecurityContext ctx = SecurityContextHolder.getContext();

                UserDetails userDetails = (UserDetails)ctx.getAuthentication().getPrincipal();      

                System.out.println("Role of the ldaper : " + ctx.getAuthentication().getAuthorities());     

                String userRole = ctx.getAuthentication().getAuthorities(); 

【问题讨论】:

【参考方案1】:

我没有使用 Spring Security,但一个选项可能是 AttributeRepository,以便直接从 cas 获取属性,而不是从您的应用程序中检索。您可以将 attributeRepository 添加到您的 deployerContext (bean authenticationManager, property credentialsToPrincipalResolvers:

 <bean class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver" 
                p:attributeRepository-ref="attributeRepository"/>

还有以下豆类:

<bean id="attributeRepository" parent="baseAttributeRepository">
    <property name="personAttributeDaos">
        <list>
            <ref local="ldapAttributesByUid" />
        </list>
    </property>
</bean>  
<bean id="ldapAttributesByUid" parent="baseLdapAttributeRepository"
      class="org.jasig.services.persondir.support.ldap.LdapPersonAttributeDao">
    <property name="queryAttributeMapping">
        <map>
            <entry key="username" value="uid" /> 
        </map>
    </property>
</bean>    
<bean id="baseLdapAttributeRepository" abstract="true"
      p:contextSource-ref="contextSource"
      p:baseDN="o=xxxxxx,dc=xxxxx"
      p:requireAllQueryAttributes="true">
    <property name="resultAttributeMapping">
        <map>
            <entry key="accountState" value="accountState" /> 
            <entry key="authId">
                <list>
                    <value>authId</value>
                    <value>Formatted Name</value>
                </list>
            </entry>
            <entry key="groupMembership" value="groupMembership" />
            <entry key="uid" value="uid" />
            <entry key="sn" value="sn" />
            <entry key="sn2" value="sn2" />
            <entry key="givenName" value="givenName" />
        </map>
    </property>
</bean>  

AFAIK,请记住,必须使用 SAML 而不是 CAS 协议。更多信息https://wiki.jasig.org/display/CASUM/Attributes

希望对你有帮助

【讨论】:

以上是关于CAS 身份验证后在 JSF 托管 Bean 中获取 LDAP 属性的主要内容,如果未能解决你的问题,请参考以下文章

基于 JSF 表单的身份验证 + 托管 Bean 登录不起作用

托管 bean 问题中的用户输入验证(JSF 2.0)

将 JSF 托管 bean 迁移到 CDI 托管 bean

如何从非人脸请求中调用 JSF 支持 bean 方法?

CDI 托管 bean 和 JSF 托管 bean 可以相互通信吗?

Spring JSF 集成:如何在 JSF 托管 bean 中注入 Spring 组件/服务?