1

我有一个 Spring MVC 应用程序(使用 3.0.5 版本),并且需要使用 Spring LDAP 绑定到 Active Directory,以便简单且仅验证用户的凭据。该应用程序托管在 Linux 服务器上,因此我需要一个跨平台的解决方案。并且该应用程序使用 Spring Security。

在此设置中实施用户身份验证的有效方法是什么?Active Directory 支持FastBind控制 (id= 1.2.840.113556.1.4.1781),所以我想利用它,因为我只需要验证输入凭据,不需要从 AD 返回其他信息。

谢谢!

更新(2012 年 7 月 16 日):我将继续更新我的问题,提供解决方案的详细信息。

根据@ig0774 的回答,我编写了以下connection control课程:

package com.company.authentication;

import javax.naming.ldap.Control;

public class FastBindConnectionControl implements Control {

    @Override
    public String getID() {
        return "1.2.840.113556.1.4.1781";
    }

    @Override
    public boolean isCritical() {
        return true;
    }

    @Override
    public byte[] getEncodedValue() {
        return null;
    }
}

然后,我扩展AbstractContextSource了,使用FastBind连接控制类:

package com.company.authentication;

import java.util.Hashtable;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import org.springframework.ldap.core.support.AbstractContextSource;

public class FastBindActiveDirectoryContextSource extends AbstractContextSource {

    @Override
    protected DirContext getDirContextInstance(Hashtable env) throws NamingException {
        return new InitialLdapContext(env, new Control[] { new FastBindConnectionControl() });
    }
}

最后,一个服务类来封装认证机制:

package com.company.authentication;

import javax.naming.AuthenticationException;
import javax.naming.directory.DirContext;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.support.LdapUtils;

public class ActiveDirectoryAuthService implements IAuthenticate {

    private ContextSource contextSource;
    public void setContextSource(ContextSource contextSource) {
        this.contextSource = contextSource;
    }

    @Override
    public boolean authenticate(final String login, String password) {
        try {
            DirContext ctx = contextSource.getContext(login, password);
            LdapUtils.closeContext(ctx);
            return true;
        }
        catch (Exception e) {
            return false;
        }
    }
}

在我的 Spring 应用程序上下文配置文件中,我添加了以下内容:

<bean id="ADContextSource" class="com.company.authentication.FastBindActiveDirectoryContextSource">
    <property name="url" value="ldaps://x.x.x.x:636" />
</bean>

<bean id="userAuthenticationService" class="com.company.authentication.ActiveDirectoryAuthService">
    <property name="contextSource" ref="ADContextSource" />
</bean>

最后,userAuthenticationServicebean 被注入到客户端类中,比如登录控制器。

package com.company.web;

import com.company.authentication;

@Controller
public class LoginController {

    @Autowired
    private IAuthenticate userAuthenticationService;

    public String authenticateUser(String login, String password) {
      if (this.userAuthenticationService.authenticate(login, password)) {
          return "welcome";
      }
      else {
        return "login";
      }
    }
}
4

2 回答 2

1

正如此 OTN 论坛帖子中所讨论的,在 JNDI 中实现FastBind控制非常简单。

基本上,您Control为控件创建了一个新类FastBind

class FastBindConnectionControl implements Control {
    public byte[] getEncodedValue() {
            return null;
    }
    public String getID() {
        return "1.2.840.113556.1.4.1781";
    }
    public boolean isCritical() {
        return true;
    }
}

然后使用它来创建您的 ldap 上下文(错误处理和其他所有内容都被忽略):

LdapContext ctx = new InitialLdapContext(env, new Control[] {new FastBindConnectionControl()});

理想情况下,这很容易插入 Spring-LDAP,它是 LDAP 的 JNDI API 的包装器;但是,似乎内置接口LdapContextSource不接受处理连接控件的参数,因此您显然需要创建自己的子类AbstractContextSource来处理它,看起来应该是直接的-足够向前:

class FastBindLdapContextSource extends AbstractContextSource {
    protected DirContext getDirContextInstance(Hashtable env) {
        return new InitialLdapContext(env, new Control[] {new FastBindConnectionControl()});
    }
}

然后,您只需将当前LdapContextSource的实例替换为FastBindLdapContextSource.

但是请注意,此上下文源只能用于BIND操作。正如我在对 Terry Gardner 的评论中链接到的MSDN 文档中所述:

在此模式下,连接上只接受简单绑定。因为没有完成任何组评估,所以始终处理连接,就好像没有发生任何其他 LDAP 操作的绑定一样。

这意味着您可能正在考虑维护两种类型的 LDAP 上下文,一种用于绑定,另一种用于实际执行您可能需要执行的任何查找。


查看 LdapTemplate的源代码,我发现该authenticate方法看起来不仅仅是一个简单的绑定。更具体地说,它会搜索用户,然后尝试绑定。因为,如果您使用FastBind启用的上下文,则不太可能执行搜索(通常 AD 不允许搜索匿名连接)。基本上,这意味着您可能必须避免LdapTemplate.

但是,假设您获得了对 ADContextSource bean 的引用,它应该很简单,可以执行类似的操作

boolean authenticate(String username, String password) {
    try {
        DirContext ctx = contextSource.getContext(username, password);
        LdapUtils.closeContext(ctx);
        return true;
    } catch (Exception e) {
        // note: this means an exception was thrown by #getContext() above
        return false;
    }
}

它相当接近地模仿了LdapTemplate无论如何都会做的事情(唯一缺少的东西是AuthenticatedLdapEntryContextCallback,在这种情况下没有任何价值,而AuthenticationErrorCallback,如果你想要这种行为,可以很容易地添加它)。

于 2012-07-16T13:44:00.640 回答
0

由于FastBind唯一的“检查用户凭据”并且不执行组确定,因此最好使用可分辨名称和凭据简单地将 BIND 请求传输到服务器。任何简单的 BIND 请求都应通过安全连接传输。结果代码为0(零)的响应表示:

  1. 服务器正在侦听并响应
  2. 可分辨名称存在且凭据与存储在服务器数据库中的凭据匹配
  3. LDAP 客户端有足够的访问权限来验证服务器上的会话

UnboundID LDAP SDK是与目录服务器交互的最佳 Java 实现

于 2012-07-16T12:47:37.253 回答