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 登录不起作用