无法使用 ldap 获得具有弹簧安全性的权限

Posted

技术标签:

【中文标题】无法使用 ldap 获得具有弹簧安全性的权限【英文标题】:Can not get authorities with spring security with ldap 【发布时间】:2014-07-21 18:51:26 【问题描述】:

我正在尝试使用 Spring Security 授权用户。到目前为止,我能够使用 ldap 对它们进行身份验证,但还无法获得授权。

我还将ldap与Apache Directory Studio连接并查询用户信息,我可以在用户中找到权限。但是 spring security ldap 配置无法收集它们。

这里是 application-config.xml 的 ldap 部分:

<sec:authentication-manager>
  <sec:ldap-authentication-provider 
    user-dn-pattern="uid=0,ou=myComp,o=myComp,dc=myComp,dc=ldap"
    group-search-base="dc=myComp,dc=ldap"
    group-search-filter="uniqueMember=0"
    role-prefix="none" />

这里是 Apache Directory Studio 的屏幕截图以及我的信息:

如您所见,我的角色信息在我的用户的 nsRole 属性中。但是,除非我查询 nsRole 属性,否则在我的用户信息中看不到它。所以问题是,我可以通过添加/更改 ldap 配置的配置来获得这些权限,还是我必须编写自己的身份验证提供程序并连接 ldap 服务器并手动查询 nsRole 属性?

非常感谢..

注意:这是一个自定义的 java 代码,可以连接 ldap 服务器并成功查询我的用户角色。 打包 ldap;

import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;

public class LdapTest2 

    public static void main(String[] args) 
        Hashtable env = new Hashtable(11);
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldap://myCompldap.mycomp.local:389/");
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
        env.put(Context.SECURITY_PRINCIPAL, "uid=myUser,ou=myComp,o=myComp,dc=myComp,dc=ldap");
        env.put(Context.SECURITY_CREDENTIALS, "myPass");

        try 
            LdapContext ctx = new InitialLdapContext(env, null);
            ctx.setRequestControls(null);
            NamingEnumeration<?> namingEnum = ctx.search("uid=myUser,ou=myComp,o=myComp,dc=myComp,dc=ldap", "(objectClass=*)", getSimpleSearchControls());
            while (namingEnum.hasMore ()) 
                SearchResult result = (SearchResult) namingEnum.next ();
                Attributes attrs = result.getAttributes ();
                Attribute lattr = attrs.get("nsrole");

                NamingEnumeration allRoles = lattr.getAll();
                while(allRoles.hasMore()) 
                    Object role = allRoles.next();
                    System.out.println(role);
                

             
            namingEnum.close();
         catch (Exception e) 
            e.printStackTrace();
        
    

    private static SearchControls getSimpleSearchControls() 
        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        searchControls.setTimeLimit(30000);
        String[] attrIDs = "nsRole";
        searchControls.setReturningAttributes(attrIDs);
        return searchControls;
    


【问题讨论】:

【参考方案1】:

spring security 假设你的 ldap 树是这样的。

o=myComp,dc=com,dc=tr
      |
      |----ou=users (node)
      |      |
      |      |
      |      |-----uid=myuser1,dc=myComp,dc=com,dc=tr (node)
      |      |
      |      |-----uid=myuser2,dc=myComp,dc=com,dc=tr (node)
      |
      |
      |----ou=groups (node)
             |
             |
             |-----cn=myRole (node)
                       |
                  uniqueMember: uid=myuser1,dc=myComp,dc=com,dc=tr (attribute on myRole)
                  uniqueMember: uid=myuser2,dc=myComp,dc=com,dc=tr (attribute on myRole)

但是你的 ldap 树是不同的。所以它找不到角色。你的树没有错。正如@LukeTaylor 所说,您可以使用自定义的 userDetailsContextMapper 。但是角色和用户应该在不同的节点中。如果您将角色作为属性放在用户节点上,我认为这在您想要更新角色时没有用。例如您想删除角色或更改角色名称,那么您应该更新所有用户节点。但是,如果您为每个角色创建节点,那么您只需删除或重命名一个节点。

更新:

如果你更新你的 ldap 树。它应该工作

  <sec:ldap-authentication-provider 
    user-dn-pattern="uid=0,ou=myComp,o=myComp,dc=myComp,dc=ldap"
    group-search-base="ou=groups"
    group-search-filter="roleOccupant=0"
    role-prefix="none" />

注意:角色节点使用organizationalRole objectClass。因为 groupOfUniqueNames 至少需要一个 uniqueMember。但organizationalRole 不需要。

【讨论】:

谢谢,但无法更新公司 ldap 树。它已经使用了很长时间:(【参考方案2】:

由于您的用户角色是用户目录条目的一部分,您应该能够使用自定义UserDetailsContextMapper 加载它们。将您的自定义角色加载代码移动到 mapUserFromContext 方法。您可以使用NullLdapAuthoritiesPopulator 跳过正常的角色搜索。

你需要configure the authentication provider explicitly:

<bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
   <constructor-arg>
       <bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
           <constructor-arg ref="contextSource"/>
           <property name="userDnPatterns">
             <list><value>uid=0,ou=myComp,o=myComp,dc=myComp,dc=ldap</value></list>
           </property>
       </bean>
   </constructor-arg>
   <constructor-arg>
       <bean class="org.springframework.security.ldap.authentication.NullLdapAuthoritiesPopulator" />
   </constructor-arg>
   <property name="userDetailsContextMapper" ref="yourContextMapperImplementation" />
</bean>

【讨论】:

我之前试过这个,没有NullLdapAuthoritiesPopulator。根据您的建议,我再次尝试了,在userDetailContextMapper.mapUserContext() 方法中,参数DirContextOperations 参数不包含nsRole 参数。在 Apache Directory Stuido 中,如果您不查询 nsRole 参数,您将不会在那里看到它。 Arrgh 对不起,您在 userDetailsContextMapper.mapUserContext() 方法中说,手动连接 ldap(我代表的代码)并加载所需的角色? 嗯。我猜 nsRole 一定是一个特例,因为通常所有属性都会被拉回。您可以尝试在BindAuthenticator 上设置userAttributes 属性以专门定义您想要的属性并在列表中包含“nsRole”。我不是说在这里建立一个单独的连接来加载用户数据。 现在救我了 :) 非常感谢。现在我在userDetailsContextMapper 获得了 nsRole 属性。

以上是关于无法使用 ldap 获得具有弹簧安全性的权限的主要内容,如果未能解决你的问题,请参考以下文章

如何使弹簧安全允许使用休息服务

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

如何使百里香弹簧安全命名空间可用?

没有弹簧安全性的 ldap 和 JWT 身份验证

grafana ldap 权限无法保持

弹簧安全 LDAP。使用 SearchControls.OBJECT_SCOPE 代替 SearchControls.SUBTREE_SCOPE