如何使用Spring安全性从Active Directory LDAP填充LDAP权限?

时间:2011-08-19 14:12:39

标签: ldap spring-security roles

我们正在使用spring security在我们的应用程序中从LDAP验证用户。验证部分正常工作,但授权部分无法正常工作。

我们无法从LDAP中检索用户的角色。

Peter Mularien “Spring Security 3”一书

“这是因为Active Directory将组成员身份存储为属性 用户自己的LDAP条目。开箱即用(截至发布时), Spring Security不提供可以的LdapAuthoritiesPopulator 配置为支持典型的Active Directory LDAP树的结构。“

下面是我的spring-security配置文件。

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

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

       <http use-expressions="true" >
        <intercept-url pattern="/resources/**" filters="none" />
        <intercept-url pattern="/login" access="permitAll"/>
        <intercept-url pattern="/**" access="isAuthenticated()" />
        <form-login login-page="/login" 
                    default-target-url="/home" 
                    always-use-default-target="true"  
                    authentication-failure-url="/login?login_error=1" />
        <logout invalidate-session="true"
                logout-success-url="/"
                logout-url="/logout"/>
    </http>

    <authentication-manager alias="ldapAuthenticationManager">  
        <authentication-provider ref="ldapAuthenticationProvider"/>  
    </authentication-manager> 

    <beans:bean id="ldapAuthenticationProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">  
        <beans:constructor-arg ref="ldapBindAuthenticator"/>  
        <beans:constructor-arg ref="ldapAuthoritiesPopulator"/>  
        <beans:property name="userDetailsContextMapper" ref="ldapUserDetailsContextMapper"/>  
    </beans:bean> 

    <beans:bean id="ldapServer" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">  
        <!-- MS Active Directory -->  
        <beans:constructor-arg value="ldap://localhost:389/dc=myOrg,dc=net"/>  
        <beans:property name="userDn" value="admin"/>  
        <beans:property name="password" value="admin"/>
        <beans:property name="baseEnvironmentProperties">
            <beans:map>
                <beans:entry key="java.naming.referral" value="follow" />
            </beans:map>
        </beans:property>
    </beans:bean>  

    <beans:bean id="ldapBindAuthenticator" class="org.springframework.security.ldap.authentication.BindAuthenticator">  
        <beans:constructor-arg ref="ldapServer"/>  
        <beans:property name="userSearch" ref="ldapSearchBean"/>  
    </beans:bean>  

    <beans:bean id="ldapSearchBean" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">  
        <!-- MS Active Directory -->  
        <!-- user-search-base; relative to base of configured context source -->  
        <beans:constructor-arg value="ou=Software OU"/>  
        <!-- user-search-filter -->  
        <beans:constructor-arg value="(sAMAccountName={0})"/>  
        <beans:constructor-arg ref="ldapServer"/>  
    </beans:bean>  

    <beans:bean id="ldapAuthoritiesPopulator" class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
        <beans:constructor-arg ref="ldapServer" />
        <beans:constructor-arg value="" />
        <beans:property name="groupSearchFilter" value="(sAMAccountName={0})"/>
        <beans:property name="groupRoleAttribute" value="memberOf" />
        <beans:property name="rolePrefix" value=""/>
        <beans:property name="searchSubtree" value="true"/>
        <beans:property name="convertToUpperCase" value="false"/>
        <beans:property name="ignorePartialResultException" value="true"/>
    </beans:bean>

    <beans:bean class="org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper" id="ldapUserDetailsContextMapper"/> 

</beans:beans>

请帮忙。

1 个答案:

答案 0 :(得分:2)

您可能需要查看此处:https://jira.springsource.org/browse/SEC-876。虽然这个代码贡献被拒绝了,但是如果有合理的答案,它可能会给你提示。

我们使用以下配置:

Spring XML     

<bean id="ldapUserService" class="MyUserDetailService">
  <constructor-arg ref="ldapUserSearch"/>
  <constructor-arg ref="ldapAuthoritiesPopulator"/>
</bean>
<bean id="ldapUserSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
  <constructor-arg value="OU=FOO-Accounts,OU=FOO,OU=OU-GLOBAL"/> <!-- user search base, RELATIVE TO SERVER CONTEXT (URL & base of configured LDAP server)! -->
  <constructor-arg value="(sAMAccountName={0})"/> <!-- user search filter -->
  <constructor-arg ref="ldapServer"/>
</bean>
<bean id="ldapAuthoritiesPopulator" class="MyLdapAuthoritiesPopulator">
  <constructor-arg ref="ldapServer" />
  <constructor-arg value="=OU=SomeFooBar,OU=FOO-Global-Security,OU=FOO-Groups,OU=FOO,OU=OU-GLOBAL" /> <!-- group search base, RELATIVE TO SERVER CONTEXT (URL & base of configured LDAP server)! -->
  <constructor-arg ref="roleMappings"/>
  <property name="groupRoleAttribute" value="cn" />
  <property name="groupSearchFilter" value="(member={0})" />
</bean>

<强>填充器

我无法分享很多专有代码,因为我们的客户在AD中需要提取额外的信息。我删除了它,因为它不关心这个问题。因此,此代码将无法编译。

public class MyLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopulator {

  /**
   * Prefix assigned by Spring Security to each group/role from LDAP.
   */
  public static final String AUTHORITY_ROLE_PREFIX = "ROLE_";

  private Properties roleMappings;
  private Properties invertedRoleMappings;

  /**
   *
   * @param contextSource supplies the contexts used to search for user roles.
   * @param groupSearchBase if this is an empty string the search will be performed from the root DN
   * of the context factory. If null, no search will be performed.
   * @param roleMappings maps logical (internal) role names to names as delivered by LDAP
   */
  @SuppressWarnings("deprecation")
  public MyLdapAuthoritiesPopulator(final ContextSource contextSource,
      final String groupSearchBase,
      final Properties roleMappings) {
    super(contextSource, groupSearchBase);
    setConvertToUpperCase(false);
    setRolePrefix("");
    this.roleMappings = roleMappings;
    this.invertedRoleMappings = invertRoleMappings();
    logger.info("Processing LDAP roles based on the following mapping: {}.", roleMappings);
  }

  .....

  @Override
  public Set<GrantedAuthority> getGroupMembershipRoles(final String userDn, final String username) {
    final Set<GrantedAuthority> effectiveGroupMembershipRoles = super.getGroupMembershipRoles(
        userDn, username);
    return mapEffectiveRolesToApplicationRoles(effectiveGroupMembershipRoles);
  }

  /**
   * Maps effective LDAP roles such as 'foo_boston_dispatcher' or 'foo_boston_readonly' to
   * FOO internal roles. The internal role (i.e. the {@link GrantedAuthority}) is a combination
   * of the 'ROLE_' prefix and a {@link Role} enum value. .........
   */
  Set<GrantedAuthority> mapEffectiveRolesToApplicationRoles(final Set<GrantedAuthority> effectiveGroupMembershipRoles) {
    logger.info("Processing effective roles from LDAP: {}.", effectiveGroupMembershipRoles);
    final Set<GrantedAuthority> internalRoles = new HashSet<GrantedAuthority>();
    final List<String> effectiveRoleNames = extractRoleNamesFrom(effectiveGroupMembershipRoles);
    final List<String> unmappedGroupMembershipRoles = new ArrayList<String>();
    ......
    // in a method invoked here we do something like internalRoles.add(new GrantedAuthority(AUTHORITY_ROLE_PREFIX + role));
    ......
    logger.info("Created internal roles {}.", internalRoles);
    logger.trace(
        "The following group membership roles were not mapped to an internal equivalent: {}",
        unmappedGroupMembershipRoles);
    return internalRoles;
  }

  ......

  private List<String> extractRoleNamesFrom(final Collection<GrantedAuthority> authorities) {
    final List<String> authorityNames = new ArrayList<String>(authorities.size());
    for (GrantedAuthority authority : authorities) {
      authorityNames.add(authority.getAuthority());
    }
    return authorityNames;
  }
}