检查 Active DIrectory 中的用户身份验证
Check if user authentication in Active DIrectory
我想知道用户是否为其 Active Directory 用户输入了正确的域、用户和密码组合。
我试图制作一个无法连接的非常简单的程序,但通过阅读错误消息我可以知道 user/password 是否正确。
这是基于技巧的(逻辑是读取异常消息),无论如何我在 2 个服务器上测试了这个原型,我注意到异常消息在服务器之间变化,所以这是不可靠的。
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;
如果消息包含 "server" 我设置 "Connected" 的原因是在我的
本地机器(实际上在我公司的 ldap 服务器上)如果一切正常(域、用户和密码)服务器回复 "The server requires a safer authentication",所以 "server" 字在那里,而在其他情况下它说"wrong user or password"。因为这必须在意大利语和英语服务器上工作,所以我将 "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;
当我使用错误的密码时,我得到 (HRESULT) 0x8007052e (2147943726) "unknown user name or bad password"。而且没有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')
i would like to know whether a user inputs the correct combination of
Domain, User and Password for his Active Directory user.
您可以使用 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
才能使用域登录,否则该功能将始终returnThe 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;
How can i check if the user set the correct password or not in a more
reliable way using a similar technique?
尝试使用FormatMessage函数
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);
您需要检查 ADsOpenObject
return 的错误代码。不要将错误检查基于 returned 异常 messages.
如果函数成功则returnS_OK
,否则需要参考ADSI Error Codes, specifically, the LDAP error codes for ADSI
When an LDAP server generates an error and passes the error to the
client, the error is then translated into a string by the LDAP client.
This method is similar to Win32 error codes for ADSI. In this example,
the client error code is the WIN32 error 0x80072020.
To Determine the LDAP error codes for ADSI
- Drop the 8007 from the WIN32 error code. In the example, the remaining hex value is 2020.
- Convert the remaining hex value to a decimal value. In the example, the remaining hex value 2020 converts to the decimal value
8224.
- Search in the WinError.h file for the definition of the decimal value. In the example, 8224L corresponds to the error
ERROR_DS_OPERATIONS_ERROR.
- Replace the prefix ERROR_DS with LDAP_. In the example, the new definition is LDAP_OPERATIONS_ERROR.
- Search in the Winldap.h file for the value of the LDAP error definition. In the example, the value of LDAP_OPERATIONS_ERROR in
the Winldap.h file is 0x01.
对于 0x8007052e
结果 (0x052e
= 1326
) 例如你会得到 ERROR_LOGON_FAILURE
来自您的评论:
Since the function always raises an exception i am not able to read
the code
你得到一个 EOleException
因为你的 ADsOpenObject
函数是用 safecall
调用约定定义的。而其他实现可能使用 stdcall
。当使用 safecall
时 Delphi 会引发一个 EOleException
而 HResult
将反映在 EOleException.ErrorCode
中,否则 (stdcall
) 不会引发一个异常,HResult
将被 ADsOpenObject
函数 return 编辑。
我想知道用户是否为其 Active Directory 用户输入了正确的域、用户和密码组合。
我试图制作一个无法连接的非常简单的程序,但通过阅读错误消息我可以知道 user/password 是否正确。
这是基于技巧的(逻辑是读取异常消息),无论如何我在 2 个服务器上测试了这个原型,我注意到异常消息在服务器之间变化,所以这是不可靠的。
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;
如果消息包含 "server" 我设置 "Connected" 的原因是在我的 本地机器(实际上在我公司的 ldap 服务器上)如果一切正常(域、用户和密码)服务器回复 "The server requires a safer authentication",所以 "server" 字在那里,而在其他情况下它说"wrong user or password"。因为这必须在意大利语和英语服务器上工作,所以我将 "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;
当我使用错误的密码时,我得到 (HRESULT) 0x8007052e (2147943726) "unknown user name or bad password"。而且没有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')
i would like to know whether a user inputs the correct combination of Domain, User and Password for his Active Directory user.
您可以使用 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
才能使用域登录,否则该功能将始终returnThe 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;
How can i check if the user set the correct password or not in a more reliable way using a similar technique?
尝试使用FormatMessage函数
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);
您需要检查 ADsOpenObject
return 的错误代码。不要将错误检查基于 returned 异常 messages.
如果函数成功则returnS_OK
,否则需要参考ADSI Error Codes, specifically, the LDAP error codes for ADSI
When an LDAP server generates an error and passes the error to the client, the error is then translated into a string by the LDAP client.
This method is similar to Win32 error codes for ADSI. In this example, the client error code is the WIN32 error 0x80072020.
To Determine the LDAP error codes for ADSI
- Drop the 8007 from the WIN32 error code. In the example, the remaining hex value is 2020.
- Convert the remaining hex value to a decimal value. In the example, the remaining hex value 2020 converts to the decimal value 8224.
- Search in the WinError.h file for the definition of the decimal value. In the example, 8224L corresponds to the error ERROR_DS_OPERATIONS_ERROR.
- Replace the prefix ERROR_DS with LDAP_. In the example, the new definition is LDAP_OPERATIONS_ERROR.
- Search in the Winldap.h file for the value of the LDAP error definition. In the example, the value of LDAP_OPERATIONS_ERROR in the Winldap.h file is 0x01.
对于 0x8007052e
结果 (0x052e
= 1326
) 例如你会得到 ERROR_LOGON_FAILURE
来自您的评论:
Since the function always raises an exception i am not able to read the code
你得到一个 EOleException
因为你的 ADsOpenObject
函数是用 safecall
调用约定定义的。而其他实现可能使用 stdcall
。当使用 safecall
时 Delphi 会引发一个 EOleException
而 HResult
将反映在 EOleException.ErrorCode
中,否则 (stdcall
) 不会引发一个异常,HResult
将被 ADsOpenObject
函数 return 编辑。