检查Active DIrectory中的用户身份验证

时间:2017-05-10 15:06:25

标签: delphi ldap delphi-10-seattle

我想知道用户是否为其Active Directory用户输入了域名,用户名和密码的正确组合。

我尝试制作一个无法连接的非常简单的程序但是通过阅读错误消息我可以知道用户/密码是否正确。

这是基于技巧的(逻辑是在读取Exception消息),无论如何我在2台服务器上测试了这个原型,我注意到excpetion消息在服务器之间发生了变化,所以这是不可靠的。

uses adshlp, ActiveDs_TLB;
// 3 TEdit and a TButton

procedure TForm4.Button1Click(Sender: TObject);
Var
 aUser : IAdsUser;
 pDomain, pUser, pPassword : string;
 myResult : HRESULT;
 Counter: integer;
begin
  pDomain := edtDomain.Text;
  pUser:= edtUser.Text;
  pPassword := edtPwd.Text;
  Counter := GetTickCount;


 Try
    myResult := ADsOpenObject(Format('LDAP://%s',[pDomain]),Format('%s\%s',[pDomain,pUser]),pPassword,
    ADS_READONLY_SERVER,
    IAdsUser,aUser);
 except
    On E : EOleException do
    begin
    if (GetTickCount - Counter > 3000) then  ShowMessage ('Problem with connection') else
    if Pos('password',E.Message) > 0  then ShowMessage ('wrong username or password') else
     if Pos('server',E.Message) > 0 then ShowMessage ('Connected') else
     ShowMessage('Unhandled case');
    memLog.Lines.Add(E.Message);
    end;

 end
end;

我设置" Connected"如果邮件包含" server"是我的 本地机器(实际上在我公司的ldap服务器上)万一服务器回复"服务器需要更安全的身份验证"所以"服务器"字在那里,而在其他情况下,它说'#34;错误的用户或密码"。这必须适用于我设置的itlian和英语服务器" server"和" pasword"作为可靠的话语。无论如何,我在另一台提供不同错误的服务器上测试过。

我从对this question的回复开始,执行上述操作。

如何使用类似技术以更可靠的方式检查用户是否设置了正确的密码?

更新(找到解决方案)

感谢回复,我设法编写了这个能够满足我需要的功能。到目前为止看起来很可靠,我写在这里分享,希望它可以帮助别人:

// This function returns True if the provided parameters are correct
// login credentials for a user in the specified Domain
// From empirical tests it seems reliable
function UserCanLogin(aDomain, aUser, aPassword: string): Boolean;
var
  hToken: THandle;
begin
  Result := False;
  if (LogonUser(pChar(aUser), pChar(aDomain), pChar(aPassword), LOGON32_LOGON_INTERACTIVE,
   LOGON32_PROVIDER_DEFAULT, hToken)) then
  begin
    CloseHandle(hToken);
    Result := True;
  end;
end;

3 个答案:

答案 0 :(得分:3)

您需要检查ADsOpenObject返回的错误代码。不要将错误检查基于返回的异常消息

如果函数成功,它将返回S_OK,否则您需要引用ADSI Error Codes,特别是LDAP error codes for ADSI

  

当LDAP服务器生成错误并将错误传递给   客户端,然后由LDAP客户端将错误转换为字符串。

     

此方法类似于ADSI的Win32错误代码。在这个例子中,   客户端错误代码是WIN32错误0x80072020。

     

确定ADSI的LDAP错误代码

     
      
  1. 从WIN32错误代码中删除8007。在该示例中,剩余的十六进制值是2020。
  2.   
  3. 将剩余的十六进制值转换为十进制值。在该示例中,剩余的十六进制值2020转换为十进制值   8224。
  4.   
  5. 在WinError.h文件中搜索十进制值的定义。在示例中,8224L对应于错误   的 ERROR_DS_OPERATIONS_ERROR 即可。
  6.   
  7. 将前缀 ERROR_DS 替换为 LDAP _ 。在示例中,新定义为 LDAP_OPERATIONS_ERROR
  8.   
  9. 在Winldap.h文件中搜索LDAP错误定义的值。在示例中, LDAP_OPERATIONS_ERROR 的值为   Winldap.h文件是0x01。
  10.   

对于0x8007052e结果(0x052e = 1326),您将获得ERROR_LOGON_FAILURE

来自你的评论:

  

由于该函数总是引发异常,因此无法读取   代码

您收到EOleException,因为您的ADsOpenObject函数是使用safecall调用约定定义的。而其他实现可能正在使用stdcall。当使用safecall时,Delphi会引发EOleExceptionHResult将反映在EOleException.ErrorCode中,否则(stdcall)将不会引发异常而HResult {1}}将由ADsOpenObject函数返回。

答案 1 :(得分:1)

我得到(HRESULT)0x8007052e(2147943726)"未知的用户名或密码错误"当我使用错误的密码。并且没有EOleException,请尝试:

      hr := ADsOpenObject('LDAP://'+ ADomain + '/OU=Domain Controllers,' + APath,
                       AUser, APwd,
                       ADS_SECURE_AUTHENTICATION or ADS_READONLY_SERVER,
                       IID_IADs, pObject);
   if (hr=HRESULT(2147943726)) then ShowMessage ('wrong username or password')

答案 2 :(得分:1)

  

我想知道用户是否输入了正确的组合   他的Active Directory用户的域,用户和密码。

您可以使用LogonUser功能验证用户登录信息,例如:

  if (LogonUser(pChar(_Username), pChar(_ADServer), pChar(_Password), LOGON32_LOGON_INTERACTIVE,
   LOGON32_PROVIDER_DEFAULT, hToken)) then
  begin
    CloseHandle(hToken);
    //...
    //DoSomething
  end
  else raise Exception.Create(SysErrorMessage(GetLastError));

注意:您必须在域计算机中使用LogonUser才能使用域登录,否则该函数将始终返回The user name or password is incorrect

另一种方法是使用TLDAPSend,例如:

function _IsAuthenticated(const lpszUsername, lpszDomain, lpszPassword: string): Boolean;
var
  LDAP : TLDAPSend;
begin
  Result := False;
  if ( (Length(lpszUsername) = 0) or (Length(lpszPassword) = 0) )then Exit;
  LDAP := TLDAPSend.Create;
  try
    LDAP.TargetHost := lpszDomain;
    LDAP.TargetPort := '389';
    ....
    LDAP.UserName := lpszUsername + #64 + lpszDomain;;
    LDAP.Password := lpszPassword;
    Result := LDAP.Login;
  finally
    LDAP.Free;
  end;
end;
  

如何检查用户是否设置了正确的密码   使用类似技术的可靠方式?

尝试使用FormatMessage function

 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM,
                nil,
                myResult,
                LANG_ENGLISH or SUBLANG_ENGLISH_US,
                lpMsg,
                0,
                nil);
 MessageBox(0, lpMsg, 'Msg', 0);