如何在Python中对Active Directory服务器进行双重身份验证?

1 投票
2 回答
1144 浏览
提问于 2025-04-16 03:11

我在一台运行FreeBSD的机器上使用Python 2.6,想要实现一种叫做“两步验证”的功能,目的是对一个活动目录进行身份验证。

简单来说,用户 'myuserid' 登录的过程是这样的:

  1. 首先,用一个为这个目的创建的系统账户连接到AD LDAP服务器(我们叫它 DOMAIN\gatekeeper
  2. 然后,检查 myuserid 的密码是否和AD中存储的该用户的凭据匹配。

我有以下代码,看起来和这个问题中的代码很相似。

l = ldap.initialize(Server)
l.protoco_version = 3
l.set_option(ldap.OPT_REFERRALS, 0)
l.simple_bind_s('cn=gatekeeper,dc=DOMAIN,dc=COMPANY,dc=TLD', 'gatekeeper_password')

但是运行后出现了这个错误:

=> LDAPError - INVALID_CREDENTIALS: {'info': '80090308: LdapErr: DSID-0C090334, comment: AcceptSecurityContext error, data 525, vece', 'desc': 'Invalid credentials'}
---------------------------------------------------------------------------
INVALID_CREDENTIALS                       Traceback (most recent call last)

/Users/crose/projects/ldap-auth/9163_saas/webservices/aws/model/aw_registry/<ipython console> in <module>()

/Users/crose/virtualenv/ldap-auth/lib/python2.6/site-packages/ldap/ldapobject.pyc in simple_bind_s(self, who, cred, serverctrls, clientctrls)
    205     """
    206     msgid = self.simple_bind(who,cred,serverctrls,clientctrls)
--> 207     return self.result(msgid,all=1,timeout=self.timeout)
    208 
    209   def bind(self,who,cred,method=ldap.AUTH_SIMPLE):

/Users/crose/virtualenv/ldap-auth/lib/python2.6/site-packages/ldap/ldapobject.pyc in result(self, msgid, all, timeout)
    420         polling (timeout = 0), in which case (None, None) is returned.
    421     """
--> 422     res_type,res_data,res_msgid = self.result2(msgid,all,timeout)
    423     return res_type,res_data
    424 

/Users/crose/virtualenv/ldap-auth/lib/python2.6/site-packages/ldap/ldapobject.pyc in result2(self, msgid, all, timeout)
    424 
    425   def result2(self,msgid=ldap.RES_ANY,all=1,timeout=None):
--> 426     res_type, res_data, res_msgid, srv_ctrls = self.result3(msgid,all,timeout)
    427     return res_type, res_data, res_msgid
    428  

/Users/crose/virtualenv/ldap-auth/lib/python2.6/site-packages/ldap/ldapobject.pyc in result3(self, msgid, all, timeout)
    430     if timeout is None:
    431       timeout = self.timeout
--> 432     ldap_result = self._ldap_call(self._l.result3,msgid,all,timeout)
    433     if ldap_result is None:
    434       rtype, rdata, rmsgid, decoded_serverctrls = (None,None,None,None)

/Users/crose/virtualenv/ldap-auth/lib/python2.6/site-packages/ldap/ldapobject.pyc in _ldap_call(self, func, *args, **kwargs)
     94     try:
     95       try:
---> 96         result = func(*args,**kwargs)
     97         if __debug__ and self._trace_level>=2:
     98           if func.__name__!="unbind_ext":

INVALID_CREDENTIALS: {'info': '80090308: LdapErr: DSID-0C090334, comment: AcceptSecurityContext error, data 525, vece', 'desc': 'Invalid credentials'}

我看到的每个教程似乎都假设我是在Windows上操作,但实际上我并不是。我该如何在Unix系统上实现这个功能呢?

2 个回答

0

当你在使用LDAP时,如果看到错误代码49和子代码525,这并不是说你的密码错了,而是说绑定的DN(区分名)有问题。子代码52e则表示凭证不正确。你需要检查一下你是否输入了正确的门控用户的DN。

1

你遇到了几个问题:

  1. 在活动目录(AD)中,简单认证(SIMPLE Auth)通常是禁用的,尤其是没有SSL的情况下(即使是有SSL的版本也常常是关闭的)。
  2. 简单认证并没有明确规定密码的编码方式(不过通常使用utf-8是可以的)。
  3. 简单认证可能会在处理引用时出现问题。
  4. 你的AD用户在Gatekeeper\DOMAIN下的CN(通用名称)可能是不同的,通常像是cn=Gatekeeper,dc=Users,dc=DOMAIN,dc=COMPANY,dc=TLD这样的格式(Gatekeeper的名字来自sAMAccountName属性,而cn可能完全不相关)。

所以通常你需要做至少以下几件事才能让它工作:

  • 确保你的AD支持简单认证。
  • 用你的Gatekeeper账户绑定,并在AD中找到用户名的DN(通常通过搜索sAMAccountName或userPrincipalName这样的字段)。
  • 尝试用你找到的DN和用户提供的密码进行绑定。
  • 如果绑定成功,你就可以认为用户已经通过认证了……

不过如果你已经做到这些,使用PAM或Kerberos也不会花你太多时间。

撰写回答