Trong môi trường doanh nghiệp sử dụng Active Directory, Windows Authentication thường là lựa chọn tối ưu hơn Forms Authentication nhờ khả năng tích hợp sâu với hệ điều hành. Với cơ chế này, nhà phát triển không cần thiết kế giao diện đăng nhập hay viết logic kiểm tra mật khẩu, vì toàn bộ quá trình xác thực được IIS và hệ điều hành xử lý.
Cấu hình cơ bản
Để kích hoạt Windows Authentication trong ứng dụng ASP.NET, bạn chỉ cần sửa đổi file web.config:
<authentication mode="Windows" />
Về mặt kỹ thuật, module WindowsAuthenticationModule trong pipeline ASP.NET sẽ chịu trách nhiệm tạo đối tượng WindowsPrincipal và gán nó cho HttpContext.User. Quá trình này diễn ra tại sự kiện AuthenticateRequest, nơi module lấy Access Token từ IIS để xây dựng danh tính người dùng.
Tương tác với Active Directory
Thư viện .NET Framework cung cấp các lớp System.DirectoryServices giúp tương tác dễ dàng với Active Directory qua giao thức LDAP. Thông thường, chúng ta sử dụng DirectoryEntry để kết nối và DirectorySearcher để truy vấn dữ liệu.
Đoạn mã dưới đây minh họa cách lấy thông tin miền hiện tại và tìm kiếm người dùng dựa trên tên đăng nhập:
using System;
using System.DirectoryServices;
using System.Management;
public static class ActiveDirectoryHelper
{
public static string GetCurrentDomainName()
{
// Sử dụng WMI để lấy tên miền của máy tính đang tham gia
var query = new SelectQuery("Win32_ComputerSystem");
using (var searcher = new ManagementObjectSearcher(query))
{
foreach (ManagementObject obj in searcher.Get())
{
if ((bool)obj["partofdomain"])
{
return obj["domain"].ToString();
}
}
}
return null;
}
public static void DisplayUserInfo(string username, string domain)
{
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(domain)) return;
try
{
string path = "LDAP://" + domain;
using (DirectoryEntry entry = new DirectoryEntry(path))
using (DirectorySearcher searcher = new DirectorySearcher(entry))
{
// Bộ lọc tìm kiếm theo sAMAccountName
searcher.Filter = string.Format("(sAMAccountName={0})", username);
// Chỉ định các thuộc tính cần lấy để tối ưu hiệu năng
searcher.PropertiesToLoad.Add("displayName");
searcher.PropertiesToLoad.Add("mail");
searcher.PropertiesToLoad.Add("givenName");
SearchResult result = searcher.FindOne();
if (result != null)
{
foreach (string propName in result.Properties.PropertyNames)
{
ResultPropertyValueCollection valueColl = result.Properties[propName];
foreach (Object propValue in valueColl)
{
Console.WriteLine($"{propName}: {propValue}");
}
}
}
else
{
Console.WriteLine("Không tìm thấy người dùng.");
}
}
}
catch (Exception ex)
{
Console.WriteLine("Lỗi khi truy cập AD: " + ex.Message);
}
}
}
Lấy thông tin người dùng trong ASP.NET
Khi chạy trên môi trường Web, việc sử dụng Environment.UserName thường không chính xác vì nó trả về tài khoản chạy ứng dụng (ví dụ: Network Service) thay vì người dùng thực. Cách đúng là truy cập qua HttpContext.User.
Dưới đây là hàm hỗ trợ trích xuất tên đăng nhập từ đối tượng HttpContext:
public static string GetLoginName(HttpContext context)
{
if (context == null || !context.Request.IsAuthenticated)
return null;
// Chuỗi trả về thường có dạng "DOMAIN\Username"
string identityName = context.User.Identity.Name;
if (identityName.Contains('\\'))
{
string[] parts = identityName.Split('\\');
return parts[1]; // Trả về phần Username
}
return identityName;
}
Sau khi có tên đăng nhập, bạn có thể kết hợp với đoạn code truy vấn Active Directory ở trên để lấy thêm thông tin như Email, Tên hiển thị, v.v.
Xác thực thủ công với Active Directory
Đôi khi bạn cần viết tool độc lập (như WinForms) để kiểm tra tài khoản mà không dựa vào IIS. Bạn có thể thực hiện việc này bằng cách cố gắng kết nối đến AD với thông tin đăng nhập được cung cấp.
public bool ValidateUserCredentials(string domain, string username, string password)
{
string ldapPath = "LDAP://" + domain;
string userPrincipal = username;
if (!username.Contains(@"\"))
{
userPrincipal = domain + @"\" + username;
}
try
{
using (DirectoryEntry entry = new DirectoryEntry(ldapPath, userPrincipal, password))
{
// Thực hiện một thao tác tìm kiếm đơn giản để kích hoạt xác thực
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = "(objectclass=user)";
searcher.PropertiesToLoad.Add("cn");
SearchResult result = searcher.FindOne();
// Nếu không ném ngoại lệ, nghĩa là thông tin đăng nhập đúng
return result != null;
}
}
catch (COMException comEx)
{
// Mã lỗi 0x8007052e (Logon failure: unknown user name or bad password)
// Hoặc các lỗi liên quan đến kết nối
return false;
}
}
Mô phỏng (Impersonation) và Bảo mật
Trong Windows Authentication, có hai loại danh tính quan trọng:
1. HttpContext.User.Identity: Danh tính của người dùng truy cập Web.
2. WindowsIdentity.GetCurrent(): Danh tính của thread đang chạy mã (thường là tài khoản chạy Application Pool).
Mặc định, mã ASP.NET chạy dưới quyền tài khoản Application Pool. Để mã chạy dưới quyền của người dùng đang truy cập (để truy cập tài nguyên hệ thống file, v.v.), ta bật tính năng Impersonation:
<identity impersonate="true"/>
Khi bật tính năng này, WindowsIdentity.GetCurrent() sẽ trả về cùng đối tượng với HttpContext.User.Identity.
Cấu hình trên IIS
Để Windows Authentication hoạt động, bạn phải cấu hình IIS chính xác: 1. Disable Anonymous Authentication: Tắt xác thực ẩn danh. 2. Enable Windows Authentication: Bật xác thực Windows.
Trên IIS 7/8/10, bạn vào mục Authentication của site, chọn Windows Authentication và ấn Enable. Đảm bảo Anonymous Authentication đang ở trạng thái Disabled.
Xử lý yêu cầu từ Client (HttpWebRequest)
Khi viết một ứng dụng Client (ví dụ: Console App) để gọi API sử dụng Windows Authentication, bạn cần gửi kèm thông tin xác thực mạng hiện hành.
using System;
using System.IO;
using System.Net;
public class WebClientExample
{
public static void CallProtectedResource(string url)
{
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
// Sử dụng thông tin xác thực của người dùng đang chạy ứng dụng
request.Credentials = CredentialCache.DefaultNetworkCredentials;
// Hoặc: request.UseDefaultCredentials = true;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
string result = reader.ReadToEnd();
Console.WriteLine("Kết quả nhận được:");
Console.WriteLine(result);
}
}
catch (WebException ex)
{
Console.WriteLine($"Lỗi: {ex.Message}");
if (ex.Response != null)
{
using (StreamReader sr = new StreamReader(ex.Response.GetResponseStream()))
{
Console.WriteLine(sr.ReadToEnd());
}
}
}
}
}
Việc thiết lập Credentials hoặc UseDefaultCredentials là bắt buộc, nếu không yêu cầu sẽ bị từ chối với mã trạng thái 401 Unauthorized.