Jawaban:
Jika Anda bekerja dengan .NET 3.5 atau yang lebih baru, Anda bisa menggunakan System.DirectoryServices.AccountManagement
namespace dan dengan mudah memverifikasi kredensial Anda:
// 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");
}
Sederhana, dapat diandalkan, 100% kode dikelola # C di ujung Anda - apa lagi yang bisa Anda minta? :-)
Baca semua tentang ini di sini:
Memperbarui:
Sebagaimana diuraikan dalam pertanyaan SO lainnya ini (dan jawabannya) , ada masalah dengan panggilan ini yang mungkin mengembalikan True
kata sandi lama pengguna. Berhati-hatilah dengan perilaku ini dan jangan terlalu terkejut jika ini terjadi :-) (terima kasih kepada @MikeGledhill karena telah menunjukkan ini!)
UserPrinciple.FindByIdentity
untuk melihat apakah ID pengguna yang lulus ada terlebih dahulu.
ContextOptions.Negotiate
.
Kami melakukan ini di Intranet kami
Anda harus menggunakan System.DirectoryServices;
Berikut adalah nyali kode
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
disimpan dalam LDAP dalam teks biasa?
Close()
pada using
variabel.
Beberapa solusi yang disajikan di sini tidak memiliki kemampuan untuk membedakan antara pengguna / kata sandi yang salah, dan kata sandi yang perlu diubah. Itu bisa dilakukan dengan cara berikut:
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);
}
}
}
}
Jika kata sandi pengguna salah, atau pengguna tidak ada, kesalahan akan berisi
"8009030C: LdapErr: DSID-0C0904DC, komentar: Kesalahan acceptSecurityContext, data 52e, v1db1",
jika kata sandi pengguna perlu diubah, itu akan berisi
"8009030C: LdapErr: DSID-0C0904DC, komentar: Kesalahan acceptSecurityContext, data 773, v1db1"
Nilai lexc.ServerErrorMessage
data adalah representasi hex Kode Kesalahan Win32. Ini adalah kode kesalahan yang sama yang akan dikembalikan dengan cara memanggil panggilan API LogonUser Win32. Daftar di bawah ini merangkum berbagai nilai umum dengan nilai hex dan desimal:
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
danSystem.DirectoryServices.Protocols
solusi yang sangat sederhana menggunakan 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;
}
akses NativeObject diperlukan untuk mendeteksi pengguna / kata sandi yang buruk
PrincipleContext
- yang hanya ada di .NET 3.5. Tetapi jika Anda menggunakan .NET 3.5 atau yang lebih baru, Anda harus menggunakanPrincipleContext
Sayangnya tidak ada cara "sederhana" untuk memeriksa kredensial pengguna pada AD.
Dengan setiap metode yang disajikan sejauh ini, Anda mungkin mendapatkan false-negative: Kredit pengguna akan valid, namun AD akan mengembalikan false dalam keadaan tertentu:
ActiveDirectory tidak akan mengizinkan Anda menggunakan LDAP untuk menentukan apakah kata sandi tidak valid karena fakta bahwa pengguna harus mengubah kata sandi atau jika kata sandi mereka telah kedaluwarsa.
Untuk menentukan perubahan kata sandi atau kata sandi kedaluwarsa, Anda dapat menghubungi Win32: LogonUser (), dan memeriksa kode kesalahan windows untuk 2 konstanta berikut:
Cara termudah adalah dengan PInvoke LogonUser Win32 API.eg
Referensi MSDN di sini ...
Pasti ingin menggunakan tipe masuk
LOGON32_LOGON_NETWORK (3)
Ini membuat token yang ringan saja - sempurna untuk pemeriksaan AuthN. (tipe lain dapat digunakan untuk membuat sesi interaktif, dll.)
LogonUser
API mengharuskan pengguna untuk memiliki Bertindak sebagai bagian dari sistem operasi privelage; yang bukan sesuatu yang didapat pengguna - dan bukan sesuatu yang ingin Anda berikan kepada setiap pengguna dalam organisasi. ( msdn.microsoft.com/en-us/library/aa378184(v=vs.85).aspx )
Solusi .Net penuh adalah dengan menggunakan kelas-kelas dari namespace System.DirectoryServices. Mereka memungkinkan untuk meminta server AD secara langsung. Berikut adalah contoh kecil yang akan melakukan ini:
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
Kode ini langsung terhubung ke server AD, menggunakan kredensial yang disediakan. Jika kredensial tidak valid, searcher.FindOne () akan melempar pengecualian. ErrorCode adalah yang sesuai dengan kesalahan COM "kesalahan nama pengguna / kata sandi".
Anda tidak perlu menjalankan kode sebagai pengguna AD. Bahkan, saya berhasil menggunakannya untuk menanyakan informasi pada server AD, dari klien di luar domain!
Namun .NET call lain untuk mengautentikasi kredensial LDAP dengan cepat:
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
}
}
Coba kode ini (CATATAN: Dilaporkan tidak berfungsi di 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
kecuali Anda harus membuat pengecualian khusus untuk "LogonException"
Jika Anda terjebak dengan .NET 2.0 dan kode terkelola, berikut adalah cara lain yang berfungsi dengan akun lokal dan domain:
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;
}
Otentikasi Windows dapat gagal karena berbagai alasan: nama pengguna atau kata sandi salah, akun terkunci, kata sandi kedaluwarsa, dan banyak lagi. Untuk membedakan antara kesalahan ini, panggil fungsi LogonUser API melalui P / Invoke dan periksa kode kesalahan jika fungsinya kembali 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()
}
}
Penggunaan sampel:
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;
}
}
Catatan: LogonUser membutuhkan hubungan kepercayaan dengan domain yang Anda validasi.
Fungsi Sederhana Saya
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;
}
}
Di sini solusi otentikasi lengkap saya untuk referensi Anda.
Pertama, tambahkan empat referensi berikut
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 = "";
}
}