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

Posted

技术标签:

【中文标题】如何使用 Spring 安全性从 Active Directory LDAP 填充 LDAP 权限?【英文标题】:How to populate LDAP authorities from Active Directory LDAP using Spring security? 【发布时间】:2011-10-30 16:09:54 【问题描述】:

我们在我们的应用程序中使用 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>

请帮忙。

【问题讨论】:

您的代码中缺少什么?接受提供的解决方案/超链接是一回事,但指出缺失的部分对于帮助其他人(比如我)遇到完全相同的问题非常有用。感谢您分享详细的解决方案。 @CharlesMorin 我意识到我的回答低于标准,抱歉。为 AD 添加了我们的 Spring 配置。 @MarcelStör 谢谢。你用的是什么应用服务器?我试图在 JBoss AS 7.2 上进行同样的工作,但没有任何成功。将查看您的配置。 @CharlesMorin,我们使用 Tomcat。但是,我不明白为什么这会有所作为。容器不应该真正参与 Spring 和 AD 之间的通信。 @MarcelStör 是的,它有所不同,因为 JBoss 是一个 Java EE 容器,与 Tomcat 等标准 servlet 容器相比,它倾向于预先验证并增加一些其他复杂层。谢谢您的回答。我将尝试使其在 JBoss 上运行,然后在完成后在此处提供解决方案。 【参考方案1】:

您可能想看看这里: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;
  

【讨论】:

您介意分享您的 MyLdapAuthoritiesPopulator 课程吗?谢谢。

以上是关于如何使用 Spring 安全性从 Active Directory LDAP 填充 LDAP 权限?的主要内容,如果未能解决你的问题,请参考以下文章

spring.profiles.active 不能从命令行工作

SpringBoot中获取spring.profiles.active的值

TestCafe 如何在 Internet Explorer 中验证 Active Directory Windows 安全提示

使用 Active Directory 令牌从 Angular 到 Web API 应用程序进行安全 API 调用

从位置“类路径资源 [application-dev.yml]”导入的属性“spring.profiles.active”无效

使用 Active Directory 中的安全组的 Asp.Net 基于角色的身份验证