7

在 LDAP 身份验证的情况下,通常用于身份验证的参数是什么。我想使用 DN 对于通过 ldap 登录的用户来说会很头疼,因为它太大而无法记住。使用uidsAMAccountName进行身份验证的选项如何,在我的实现中,我检索相应 uid 或 sAMAccountName 的 dn 并继续进行身份验证。

我走对路了吗?

4

3 回答 3

10

在 LDAP 中,可以对连接会话进行身份验证。当 LDAP 客户端与 LDAP 目录服务器建立新连接时,该连接的授权状态为匿名。LDAP 客户端可以使用 BIND 请求来请求更改授权状态。

BIND 请求有两种形式:简单和 SASL。Simple 使用专有名称和密码,SASL 使用一种机制选择,例如 PLAIN、LOGIN、CRAM-MD5、DIGEST-MD5、GSSAPI 和 EXTERNAL - 除了 GSSAPI 和 EXTERNAL 之外,所有这些机制都太弱,无法用于生产场景或关键任务领域。

要使用简单的 BIND,请构造一个 BIND 请求并将其传输到 LDAP 目录服务器。LDAP 目录服务器将使用包含结果代码的 BIND 响应进行响应。结果代码是一个整数,任何其他零都表示 BIND 请求失败。如果结果代码为零,则 BIND 请求成功并且会话授权状态已更改为 BIND 请求中使用的专有名称的状态。

同一连接/会话上的每个后续 BIND 请求都会将授权状态设置为匿名,并且同一连接/会话上的每个后续成功 BIND 请求都会将授权状态设置为与身份验证 ID 关联的授权状态,即在简单 BIND 的情况下是专有名称,但在使用 SASL 的地方可能完全是其他东西 - 现代专业质量服务器可以将传入名称映射到不同的 DN。

无论使用哪种语言,构造一个 BIND 请求,将其传输到服务器,并解释响应。

更新:

如果不知道可分辨名称,或者太麻烦(通常情况下,Web 应用程序用户不知道他们是如何进行身份验证的,也不关心他们是否知道),LDAP 应用程序应该搜索该用户的目录。成功的搜索响应始终包含可分辨名称,然后在简单的 BIND 中使用该名称。

搜索至少包含以下内容:

  • 基础对象:优于用户的专有名称,例如,dc=example,dc=com
  • 范围:基础级别、基础以下一级或基础以下的子树。例如,如果用户位于 下属ou=people,dc=example,dc=com,则使用基础对象ou=people,dc=example,dc=comone-level. 这些搜索参数可查找以下条目:uid=user1,ou=people,dc=example,dc=com
  • 过滤器:缩小返回给客户端的可能搜索结果的范围,例如(objectClass=inetOrgPerson)
  • 请求的属性列表:从条目返回给客户端的属性。在这种情况下, use 1.1,这意味着没有属性并返回 DN(可分辨名称),这是简单 BIND 所需的全部内容。

也可以看看

关于此处的链接

于 2012-08-30T05:13:18.227 回答
6

LDAP 服务器只理解 LDAP 查询;他们没有你我习惯的“用户名”。

对于 LDAP,要对某人进行身份验证,您需要在 LDAP 中发送该人(或实体)条目的专有名称;连同他们的密码。

既然您提到了sAMAccountName,我假设您正在使用 Active Directory。Active Directory 允许匿名绑定——这意味着您无需提供任何凭据即可连接到它;但不能在不提供凭据的情况下进行任何查找。

如果您使用的是python-ldapCython(而不是 IronPython,它可以访问使此过程非常容易的各种 .NET API);然后你按照这些步骤。

通常,您使用对树具有适当权限的预设用户,并首先使用该用户连接到目录,然后使用该用户的访问权限进行其余的身份验证过程;通常是这样的:

  1. 使用预设用户连接到 AD。
  2. 使用预设用户的凭据查询活动目录,并根据用户将在表单中作为“用户名”输入的sAMAccountName搜索可分辨名称。
  3. 尝试使用步骤 2 中的可分辨名称以及用户在其表单中输入的密码再次连接到 Active Directory。
  4. 如果此连接成功,则用户通过身份验证。

所以你需要两个主要的东西:

  1. 登录属性(这是 LDAP 理解的“用户名”)
  2. 为您的用户获取信息的 LDAP 查询

以下是一些可以为您执行此操作的粗略代码:

AD_USER = 'your super user'
AD_PASSWORD = 'your super user password'

AD_BIND_ATTR = 'userPrincipalName' # this is the "login" for AD
AD_URL = "ldap://your-ad-server"
AD_DN = "DC=DOMAIN,DC=COM"
AD_LOGIN_ATTR = 'sAMAccountName' # this is what you user will enter in the form
                                 # as their "login" name,
                                 # this is what they use to login to Windows

# A listing of attributes you want to fetch for the user
AD_ATTR_SEARCH = ['cn',
                  'userPrincipalName',
                  'distinguishedName',
                  'mail',
                  'telephoneNumber','sAMAccountName']

def _getbinduser(user):
    """ This method returns the bind user string for the user"""
    user_dn = AD_DN
    login_attr = '(%s=%s)' % (AD_LOGIN_ATTR,user)
    attr_search = AD_ATTR_SEARCH

    conn = ldap.initialize(AD_URL)
    conn.set_option(ldap.OPT_REFERRALS,0)
    conn.set_option(ldap.OPT_PROTOCOL_VERSION,3)
    try:
        conn.bind(AD_USER,AD_PASSWORD)
        conn.result()
    except:
        exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
        # Exit the script and print an error telling what happened.
        sys.exit("LDAP Error (Bind Super User)\n ->%s" % exceptionValue)
    try:
        result = conn.search_s(user_dn,
                               ldap.SCOPE_SUBTREE,
                               login_attr, attr_search)
    except:
        exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
        # Exit the script and print an error telling what happened.
        sys.exit("LDAP Error (Search)\n ->%s" % exceptionValue)

    # Return the user's entry from AD, which includes
    # their 'distinguished name'
    # we use this to authenticate the credentials the
    # user has entered in the form
    return result[0][1]

def authenticate(user,password):

    bind_attr = AD_BIND_ATTR
    user_dn = AD_DN
    login_attr = '(%s=%s)' % (AD_LOGIN_ATTR,user)
    data = _getbinduser(user)

    if len(data) == 1:
        return None

    # Information we want to return from the directory
    # for each user, season to taste.

    info = {}
    info['name'] = data['cn'][0]
    info['email'] = data['mail'][0]
    try:
        info['phone'] = data['telephoneNumber'][0]
    except KeyError:
        info['phone'] = 'Not Available'

    conn = ldap.initialize(Config.AD_URL)
    conn.set_option(ldap.OPT_REFERRALS,0)
    conn.set_option(ldap.OPT_PROTOCOL_VERSION,3)

    try:
        # Now we have the "bind attribute" (LDAP username) for our user
        # we try and connect to see if LDAP will authenticate
        conn.bind(data[bind_attr][0],password)
        conn.search(user_dn,ldap.SCOPE_SUBTREE,login_attr,None)
        conn.result()
        return info
    except (ldap.INVALID_CREDENTIALS,ldap.OPERATIONS_ERROR):
        return None
于 2012-08-30T05:39:25.113 回答
0

特里出色评论的一个小扩展。如果您将所有用户存储在 DIT 的同一部分,并使用相同的属性来识别他们,您可以以编程方式构造 DN,而不是搜索它。

于 2012-08-30T12:40:20.257 回答