Как заполнить полномочия LDAP из Active Directory LDAP с помощью безопасности Spring? - PullRequest
5 голосов
/ 19 августа 2011

Мы используем Spring Security для аутентификации пользователей из LDAP в нашем приложении.Часть аутентификации работает нормально, но часть авторизации не работает.

Мы не можем получить роли пользователя из LDAP.

Из книги "Spring Security3 " by Peter Mularien

" Это связано с тем, что Active Directory сохраняет членство в группах в качестве атрибутов записей LDAP самих пользователей. Из коробки (на момент публикации), Spring Security не предлагает LdapAuthoritiesPopulator, который можно настроить для поддержки структуры типичного дерева LDAP Active Directory. "

Ниже приведен мой файл конфигурации 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 Ответ

2 голосов
/ 14 октября 2011

Возможно, вы захотите взглянуть здесь: 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>

Populator

Существует много проприетарного кода, которым я не могу поделиться, потому что у нашего клиента есть дополнительная информация в 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;
  }
}
...