使用Spring LDAP对Active Directory进行身份验证的FastBind

时间:2012-07-15 16:45:05

标签: java spring authentication active-directory ldap

我有一个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>

最后,将userAuthenticationService bean注入客户端类,比如登录控制器。

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";
      }
    }
}

2 个答案:

答案 0 :(得分:1)

在JNDI中实现FastBind控件非常简单,如in this OTN forum post所述。

基本上,您为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 document所述:

  

在此模式下,连接只接受简单绑定。由于没有进行组评估,因此始终处理连接,就好像没有为所有其他LDAP操作发生绑定一样。

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


查看source code for 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,如果您想要这种行为,可以轻松添加。)

答案 1 :(得分:0)

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

  1. 服务器正在侦听和响应
  2. 存在可分辨名称,凭据与存储在服务器数据库中的凭据相匹配
  3. LDAP客户端具有足够的访问权限来验证服务器上的会话
  4. UnboundID LDAP SDK是与目录服务器交互的最佳Java实现