ฉันจะตรวจสอบชื่อผู้ใช้และรหัสผ่านกับ Active Directory ได้อย่างไร ฉันแค่ต้องการตรวจสอบว่าชื่อผู้ใช้และรหัสผ่านถูกต้องหรือไม่
ฉันจะตรวจสอบชื่อผู้ใช้และรหัสผ่านกับ Active Directory ได้อย่างไร ฉันแค่ต้องการตรวจสอบว่าชื่อผู้ใช้และรหัสผ่านถูกต้องหรือไม่
คำตอบ:
หากคุณทำงานกับ. 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()
บน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 รหัส เหล่านี้เป็นรหัสข้อผิดพลาดเดียวกันซึ่งจะถูกส่งกลับโดยมิฉะนั้นเรียกการเรียก Win32 LogonUser API รายการด้านล่างสรุปช่วงของค่าทั่วไปที่มีค่าฐานสิบหกและทศนิยม:
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
วิธีง่ายๆในการใช้ DirectoryServices:
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
น่าเสียดายที่ไม่มีวิธี "ง่าย" ในการตรวจสอบข้อมูลรับรองผู้ใช้ในโฆษณา
ด้วยวิธีการทุกวิธีที่นำเสนอคุณอาจได้รับ false-positive: เครดิตของผู้ใช้จะถูกต้อง แต่โฆษณาจะกลับเท็จภายใต้สถานการณ์บางอย่าง:
ActiveDirectory จะไม่อนุญาตให้คุณใช้ LDAP เพื่อตรวจสอบว่ารหัสผ่านไม่ถูกต้องเนื่องจากผู้ใช้ต้องเปลี่ยนรหัสผ่านหรือรหัสผ่านหมดอายุ
ในการตรวจสอบการเปลี่ยนรหัสผ่านหรือรหัสผ่านหมดอายุคุณสามารถเรียก Win32: LogonUser () และตรวจสอบรหัสข้อผิดพลาด windows สำหรับค่าคงที่ 2 ต่อไปนี้:
วิธีที่ง่ายที่สุดน่าจะเป็น PInvoke LogonUser Win32 API.eg
การอ้างอิง MSDN ที่นี่ ...
ต้องการใช้ประเภทการเข้าสู่ระบบแน่นอน
LOGON32_LOGON_NETWORK (3)
สิ่งนี้สร้างโทเค็นที่มีน้ำหนักเบาเท่านั้น - เหมาะสำหรับการตรวจสอบ AuthN (สามารถใช้ประเภทอื่นเพื่อสร้างเซสชันแบบโต้ตอบเป็นต้น)
LogonUser
API ต้องการให้ผู้ใช้มีพระราชบัญญัติเป็นส่วนหนึ่งของระบบปฏิบัติการ privelage; ซึ่งไม่ใช่สิ่งที่ผู้ใช้ได้รับและไม่ใช่สิ่งที่คุณต้องการให้กับผู้ใช้ทุกคนในองค์กร ( msdn.microsoft.com/en-us/library/aa378184(v=vs.85).aspx )
โซลูชัน. Net แบบเต็มคือการใช้คลาสจาก System.DirectoryServices เนมสเปซ พวกเขาอนุญาตให้สอบถามเซิร์ฟเวอร์โฆษณาโดยตรง นี่คือตัวอย่างเล็ก ๆ ที่จะทำสิ่งนี้:
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
รหัสนี้เชื่อมต่อโดยตรงกับเซิร์ฟเวอร์โฆษณาโดยใช้ข้อมูลประจำตัวที่ให้ไว้ หากข้อมูลรับรองไม่ถูกต้อง searcher.FindOne () จะส่งข้อยกเว้น ErrorCode เป็นรหัสที่สอดคล้องกับข้อผิดพลาด COM "ชื่อผู้ใช้ / รหัสผ่านไม่ถูกต้อง"
คุณไม่จำเป็นต้องเรียกใช้รหัสในฐานะผู้ใช้ AD ในความเป็นจริงฉันใช้มันเพื่อสอบถามข้อมูลบนเซิร์ฟเวอร์โฆษณาจากไคลเอนต์นอกโดเมน!
อีกการเรียก. NET อีกครั้งเพื่อรับรองความถูกต้องของข้อมูลรับรอง LDAP อย่างรวดเร็ว:
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 อาจล้มเหลวได้ด้วยสาเหตุหลายประการ: ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้องบัญชีที่ถูกล็อครหัสผ่านที่หมดอายุและอื่น ๆ หากต้องการแยกแยะข้อผิดพลาดเหล่านี้ให้เรียกฟังก์ชันLogonUser API ผ่าน P / Invoke และตรวจสอบรหัสข้อผิดพลาดหากฟังก์ชันส่งคืน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 = "";
}
}