답변:
.NET 3.5 이상에서 작업하는 경우 System.DirectoryServices.AccountManagement
네임 스페이스를 사용하고 자격 증명을 쉽게 확인할 수 있습니다.
// create a "principal context" - e.g. your domain (could be machine, too)
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "YOURDOMAIN"))
{
// validate the credentials
bool isValid = pc.ValidateCredentials("myuser", "mypassword");
}
간단하고 신뢰할 수 있으며 최종적으로 100 % C # 관리 코드입니다. 무엇을 더 요청할 수 있습니까? :-)
여기에 대한 모든 내용을 읽으십시오.
최신 정보:
이 다른 SO 질문 (및 그 답변)에 요약 된 것처럼 이 호출 True
에는 사용자의 이전 암호가 반환 될 수있는 문제가 있습니다. 이 동작을 인식하고 이런 일이 발생해도 놀라지 마십시오 :-) (이 점을 지적한 @MikeGledhill 덕분에!)
UserPrinciple.FindByIdentity
, 전달 된 사용자 ID가 먼저 존재하는지 확인 하기 위해 전화 를 걸 수 있습니다.
ContextOptions.Negotiate
.
인트라넷에서이 작업을 수행합니다
System.DirectoryServices를 사용해야합니다.
다음은 코드의 내장입니다
using (DirectoryEntry adsEntry = new DirectoryEntry(path, strAccountId, strPassword))
{
using (DirectorySearcher adsSearcher = new DirectorySearcher(adsEntry))
{
//adsSearcher.Filter = "(&(objectClass=user)(objectCategory=person))";
adsSearcher.Filter = "(sAMAccountName=" + strAccountId + ")";
try
{
SearchResult adsSearchResult = adsSearcher.FindOne();
bSucceeded = true;
strAuthenticatedBy = "Active Directory";
strError = "User has been authenticated by Active Directory.";
}
catch (Exception ex)
{
// Failed to authenticate. Most likely it is caused by unknown user
// id or bad strPassword.
strError = ex.Message;
}
finally
{
adsEntry.Close();
}
}
}
strPassword
일반 텍스트로 LDAP에 저장 되었음을 의미합니까 ?
Close()
A의 using
변수.
여기에 제시된 몇 가지 솔루션에는 잘못된 사용자 / 암호와 변경해야하는 암호를 구별 할 수있는 기능이 없습니다. 다음과 같은 방법으로 수행 할 수 있습니다.
using System;
using System.DirectoryServices.Protocols;
using System.Net;
namespace ProtocolTest
{
class Program
{
static void Main(string[] args)
{
try
{
LdapConnection connection = new LdapConnection("ldap.fabrikam.com");
NetworkCredential credential = new NetworkCredential("user", "password");
connection.Credential = credential;
connection.Bind();
Console.WriteLine("logged in");
}
catch (LdapException lexc)
{
String error = lexc.ServerErrorMessage;
Console.WriteLine(lexc);
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
}
}
}
사용자 비밀번호가 잘못되었거나 사용자가 존재하지 않으면 오류에
"8009030C : LdapErr : DSID-0C0904DC, 설명 : AcceptSecurityContext 오류, 데이터 52e, v1db1",
사용자 비밀번호를 변경해야하는 경우
"8009030C : LdapErr : DSID-0C0904DC, 설명 : AcceptSecurityContext 오류, 데이터 773, v1db1"
lexc.ServerErrorMessage
데이터 값은 Win32 오류 코드 16 진수 표현입니다. 이들은 Win32 LogonUser API 호출을 호출하여 리턴되는 동일한 오류 코드입니다. 아래 목록에는 16 진수 및 10 진수 값이있는 공통 값 범위가 요약되어 있습니다.
525 user not found (1317)
52e invalid credentials (1326)
530 not permitted to logon at this time (1328)
531 not permitted to logon at this workstation (1329)
532 password expired (1330)
533 account disabled (1331)
701 account expired (1793)
773 user must reset password (1907)
775 user account locked (1909)
System.DirectoryServices
와System.DirectoryServices.Protocols
DirectoryService를 사용하는 매우 간단한 솔루션 :
using System.DirectoryServices;
//srvr = ldap server, e.g. LDAP://domain.com
//usr = user name
//pwd = user password
public bool IsAuthenticated(string srvr, string usr, string pwd)
{
bool authenticated = false;
try
{
DirectoryEntry entry = new DirectoryEntry(srvr, usr, pwd);
object nativeObject = entry.NativeObject;
authenticated = true;
}
catch (DirectoryServicesCOMException cex)
{
//not authenticated; reason why is in cex
}
catch (Exception ex)
{
//not authenticated due to some other exception [this is optional]
}
return authenticated;
}
잘못된 사용자 / 암호를 감지하려면 NativeObject 액세스가 필요합니다.
PrincipleContext
에만 .NET 3.5에 존재 -. 그러나 .NET 3.5 이상을 사용하는 경우 다음을 사용해야합니다.PrincipleContext
불행히도 AD에서 사용자 자격 증명을 확인하는 "간단한"방법은 없습니다.
지금까지 모든 방법을 제시하면 다음과 같은 부정적 결과를 얻을 수 있습니다. 사용자의 자격 증명은 유효하지만 AD는 특정 상황에서 거짓을 반환합니다.
ActiveDirectory에서는 사용자가 비밀번호를 변경해야하거나 비밀번호가 만료되어 LDAP를 사용하여 비밀번호가 유효하지 않은지 확인할 수 없습니다.
암호 변경 또는 암호 만료를 확인하려면 Win32 : LogonUser ()를 호출하고 Windows 오류 코드에서 다음 두 상수를 확인하십시오.
아마도 가장 쉬운 방법은 LogInUser Win32 API를 PInvoke하는 것입니다.
MSDN 참조는 여기에서 ...
확실히 로그온 유형을 사용하고 싶습니다
LOGON32_LOGON_NETWORK (3)
이것은 가벼운 토큰 만 생성하며 AuthN 검사에 적합합니다. (다른 유형은 대화식 세션 등을 만드는 데 사용될 수 있습니다.)
LogonUser
API는 가지고있는 사용자가 필요로 운영 체제의 일부로 작동을 privelage을; 이는 사용자가 얻는 것이 아니라 조직의 모든 사용자에게 부여하려는 것이 아닙니다. ( msdn.microsoft.com/en-us/library/aa378184(v=vs.85).aspx )
전체 .Net 솔루션은 System.DirectoryServices 네임 스페이스의 클래스를 사용하는 것입니다. AD 서버를 직접 쿼리 할 수 있습니다. 이 작업을 수행하는 작은 샘플이 있습니다.
using (DirectoryEntry entry = new DirectoryEntry())
{
entry.Username = "here goes the username you want to validate";
entry.Password = "here goes the password";
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = "(objectclass=user)";
try
{
searcher.FindOne();
}
catch (COMException ex)
{
if (ex.ErrorCode == -2147023570)
{
// Login or password is incorrect
}
}
}
// FindOne() didn't throw, the credentials are correct
이 코드는 직접 제공 한 자격 증명을 사용하여 AD 서버에 연결합니다. 자격 증명이 유효하지 않으면 searcher.FindOne ()에서 예외가 발생합니다. ErrorCode는 "잘못된 사용자 이름 / 암호"COM 오류에 해당하는 것입니다.
AD 사용자로 코드를 실행할 필요는 없습니다. 실제로 도메인 외부의 클라이언트에서 AD 서버의 정보를 쿼리하는 데 성공적으로 사용합니다!
LDAP 자격 증명을 빠르게 인증하기위한 또 다른 .NET 호출 :
using System.DirectoryServices;
using(var DE = new DirectoryEntry(path, username, password)
{
try
{
DE.RefreshCache(); // This will force credentials validation
}
catch (COMException ex)
{
// Validation failed - handle how you want
}
}
이 코드를 사용해보십시오 (참고 : Windows Server 2000에서 작동하지 않는 것으로보고 됨)
#region NTLogonUser
#region Direct OS LogonUser Code
[DllImport( "advapi32.dll")]
private static extern bool LogonUser(String lpszUsername,
String lpszDomain, String lpszPassword, int dwLogonType,
int dwLogonProvider, out int phToken);
[DllImport("Kernel32.dll")]
private static extern int GetLastError();
public static bool LogOnXP(String sDomain, String sUser, String sPassword)
{
int token1, ret;
int attmpts = 0;
bool LoggedOn = false;
while (!LoggedOn && attmpts < 2)
{
LoggedOn= LogonUser(sUser, sDomain, sPassword, 3, 0, out token1);
if (LoggedOn) return (true);
else
{
switch (ret = GetLastError())
{
case (126): ;
if (attmpts++ > 2)
throw new LogonException(
"Specified module could not be found. error code: " +
ret.ToString());
break;
case (1314):
throw new LogonException(
"Specified module could not be found. error code: " +
ret.ToString());
case (1326):
// edited out based on comment
// throw new LogonException(
// "Unknown user name or bad password.");
return false;
default:
throw new LogonException(
"Unexpected Logon Failure. Contact Administrator");
}
}
}
return(false);
}
#endregion Direct Logon Code
#endregion NTLogonUser
"LogonException"에 대한 사용자 정의 예외를 작성해야합니다.
.NET 2.0 및 관리 코드가 붙어있는 경우 로컬 및 도메인 계정에서 작동하는 다른 방법이 있습니다.
using System;
using System.Collections.Generic;
using System.Text;
using System.Security;
using System.Diagnostics;
static public bool Validate(string domain, string username, string password)
{
try
{
Process proc = new Process();
proc.StartInfo = new ProcessStartInfo()
{
FileName = "no_matter.xyz",
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true,
LoadUserProfile = true,
Domain = String.IsNullOrEmpty(domain) ? "" : domain,
UserName = username,
Password = Credentials.ToSecureString(password)
};
proc.Start();
proc.WaitForExit();
}
catch (System.ComponentModel.Win32Exception ex)
{
switch (ex.NativeErrorCode)
{
case 1326: return false;
case 2: return true;
default: throw ex;
}
}
catch (Exception ex)
{
throw ex;
}
return false;
}
잘못된 사용자 이름 또는 암호, 잠긴 계정, 만료 된 암호 등 여러 가지 이유로 Windows 인증이 실패 할 수 있습니다. 이러한 오류를 구별하려면 P / Invoke를 통해 LogonUser API 함수를 호출하고 함수가 false
다음을 리턴하면 오류 코드를 확인하십시오 .
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
public static class Win32Authentication
{
private class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeTokenHandle() // called by P/Invoke
: base(true)
{
}
protected override bool ReleaseHandle()
{
return CloseHandle(this.handle);
}
}
private enum LogonType : uint
{
Network = 3, // LOGON32_LOGON_NETWORK
}
private enum LogonProvider : uint
{
WinNT50 = 3, // LOGON32_PROVIDER_WINNT50
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LogonUser(
string userName, string domain, string password,
LogonType logonType, LogonProvider logonProvider,
out SafeTokenHandle token);
public static void AuthenticateUser(string userName, string password)
{
string domain = null;
string[] parts = userName.Split('\\');
if (parts.Length == 2)
{
domain = parts[0];
userName = parts[1];
}
SafeTokenHandle token;
if (LogonUser(userName, domain, password, LogonType.Network, LogonProvider.WinNT50, out token))
token.Dispose();
else
throw new Win32Exception(); // calls Marshal.GetLastWin32Error()
}
}
샘플 사용법 :
try
{
Win32Authentication.AuthenticateUser("EXAMPLE\\user", "P@ssw0rd");
// Or: Win32Authentication.AuthenticateUser("user@example.com", "P@ssw0rd");
}
catch (Win32Exception ex)
{
switch (ex.NativeErrorCode)
{
case 1326: // ERROR_LOGON_FAILURE (incorrect user name or password)
// ...
case 1327: // ERROR_ACCOUNT_RESTRICTION
// ...
case 1330: // ERROR_PASSWORD_EXPIRED
// ...
case 1331: // ERROR_ACCOUNT_DISABLED
// ...
case 1907: // ERROR_PASSWORD_MUST_CHANGE
// ...
case 1909: // ERROR_ACCOUNT_LOCKED_OUT
// ...
default: // Other
break;
}
}
참고 : LogonUser는 확인하려는 도메인과의 신뢰 관계가 필요합니다.
내 간단한 기능
private bool IsValidActiveDirectoryUser(string activeDirectoryServerDomain, string username, string password)
{
try
{
DirectoryEntry de = new DirectoryEntry("LDAP://" + activeDirectoryServerDomain, username + "@" + activeDirectoryServerDomain, password, AuthenticationTypes.Secure);
DirectorySearcher ds = new DirectorySearcher(de);
ds.FindOne();
return true;
}
catch //(Exception ex)
{
return false;
}
}
여기 내 참조를위한 완벽한 인증 솔루션.
먼저 다음 네 가지 참조를 추가하십시오.
using System.DirectoryServices;
using System.DirectoryServices.Protocols;
using System.DirectoryServices.AccountManagement;
using System.Net;
private void AuthUser() {
try{
string Uid = "USER_NAME";
string Pass = "PASSWORD";
if (Uid == "")
{
MessageBox.Show("Username cannot be null");
}
else if (Pass == "")
{
MessageBox.Show("Password cannot be null");
}
else
{
LdapConnection connection = new LdapConnection("YOUR DOMAIN");
NetworkCredential credential = new NetworkCredential(Uid, Pass);
connection.Credential = credential;
connection.Bind();
// after authenticate Loading user details to data table
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, Uid);
DirectoryEntry up_User = (DirectoryEntry)user.GetUnderlyingObject();
DirectorySearcher deSearch = new DirectorySearcher(up_User);
SearchResultCollection results = deSearch.FindAll();
ResultPropertyCollection rpc = results[0].Properties;
DataTable dt = new DataTable();
DataRow toInsert = dt.NewRow();
dt.Rows.InsertAt(toInsert, 0);
foreach (string rp in rpc.PropertyNames)
{
if (rpc[rp][0].ToString() != "System.Byte[]")
{
dt.Columns.Add(rp.ToString(), typeof(System.String));
foreach (DataRow row in dt.Rows)
{
row[rp.ToString()] = rpc[rp][0].ToString();
}
}
}
//You can load data to grid view and see for reference only
dataGridView1.DataSource = dt;
}
} //Error Handling part
catch (LdapException lexc)
{
String error = lexc.ServerErrorMessage;
string pp = error.Substring(76, 4);
string ppp = pp.Trim();
if ("52e" == ppp)
{
MessageBox.Show("Invalid Username or password, contact ADA Team");
}
if ("775" == ppp)
{
MessageBox.Show("User account locked, contact ADA Team");
}
if ("525" == ppp)
{
MessageBox.Show("User not found, contact ADA Team");
}
if ("530" == ppp)
{
MessageBox.Show("Not permitted to logon at this time, contact ADA Team");
}
if ("531" == ppp)
{
MessageBox.Show("Not permitted to logon at this workstation, contact ADA Team");
}
if ("532" == ppp)
{
MessageBox.Show("Password expired, contact ADA Team");
}
if ("533" == ppp)
{
MessageBox.Show("Account disabled, contact ADA Team");
}
if ("533" == ppp)
{
MessageBox.Show("Account disabled, contact ADA Team");
}
} //common error handling
catch (Exception exc)
{
MessageBox.Show("Invalid Username or password, contact ADA Team");
}
finally {
tbUID.Text = "";
tbPass.Text = "";
}
}