顯示具有 LDAP 標籤的文章。 顯示所有文章
顯示具有 LDAP 標籤的文章。 顯示所有文章

2014年2月2日 星期日

C# class to validate User credentials on Active Directory (LDAP)

One to validate a user's credential, and the other to retrieve all users from the AD. Part one is self explanatory. Part two was important for me because I do roles management (and no I dont use Membership Providers - its either too little or too much)

Here's the full implementation. 

1. if (!String.IsNullOrEmpty(string errorMessage =ADConnector.AuthenticateUserPassword("LDAP://secure.company.com", "tech1", "tech1234")))
{
//blah blah blah
)
2. SearchResultCollection resColl =ADConnector.DumpAllUsers("LDAP://secure.comapy.com", "admin", "admin1234");

foreach (SearchResult res in resColl)
{
//If account is not disabled
if (!Convert.ToBoolean(Convert.ToInt32(res.Properties["userAccountControl"][0], System.Globalization.CultureInfo.InvariantCulture) &ADConnector.UF_DISABLED))
{
//blah blah blah
}
}
}

---------------------------------------------------------------------------------ADConnector.cs------------------------------------------------------------------------------------------------

namespace Utilities.Login.SSO
{
/// <summary>
/// <remarks>Developed by: Praveen Rangarajan
/// <CR1 date="10/12/2008">Included the functionality to retrieve all accounts from the AD</CR1>
/// <CR2 date="2/4/2009">Functionality to return "Locked-out", "Password expired", and "Account disabled" error message</CR2>
/// </remarks>
/// </summary>
public partial class ADConnector
{
public const int UF_DISABLED = 0x0002;
public const int UF_LOCKED = 0x0010;
public const int UF_EXPIRED = 0x800000;


/// <summary>
/// Authenticate the login credentials of the user on the AD.
/// </summary>
/// <param name="ldap">Url to the LDAP server alongwith the default DC settings. Example "LDAP://contoso.com, DC=contoso, DC=com"</param>
/// <param name="userName">Domain account of the user. Just the account name is sufficient</param>
/// <param name="password">User password</param>
/// <returns>
/// null If valid.
/// Non-empty string with the error message, if invalid.
/// </returns>
public static string AuthenticateUserPassword(string ldap, string userName,string password, string adminName, string adminPassword)
{
try
{
DirectoryEntry deSystem = new DirectoryEntry(ldap, userName, password);
DirectorySearcher s = new DirectorySearcher(deSystem, "SAMAccountName="+ userName,
new string[] { "userAccountControl" }
, SearchScope.Subtree);
SearchResult res;
if ((res = s.FindOne()) == null)
return "Username and/or Password incorrect";
if (Convert.ToBoolean(Convert.ToInt32(res.Properties["userAccountControl"][0]) & UF_DISABLED))
return "Account has been disabled. Please contact administrator for more details";
return null;
}
catch
{
//User validation returned exception. Now check for Password expired or Account locked out.
// Use the admin account (any account has LDAP query rights) to check for the above condition.

DirectoryEntry deSystem = new DirectoryEntry(ldap, adminName, adminPassword);
DirectorySearcher s = new DirectorySearcher(deSystem, "SAMAccountName="+ userName,
new string[] { "userAccountControl", "msDS-User-Account-Control-Computed" }
, SearchScope.Subtree);
SearchResult res;
if ((res = s.FindOne()) == null)
return "User not identified in AD";
if (Convert.ToBoolean(Convert.ToInt32(res.Properties["userAccountControl"][0]) & UF_DISABLED))
return "Account has been disabled. Please contact administrator for more details";
if (res.Properties.Contains("msDS-User-Account-Control-Computed") &&
Convert.ToBoolean(Convert.ToInt32(res.Properties["msDS-User-Account-Control-Computed"][0]) & UF_LOCKED))
return "Account locked-out. Please contact administrator for more details";
if (Convert.ToBoolean(Convert.ToInt32(res.Properties["userAccountControl"][0]) & UF_EXPIRED))
return "Password has expired. Please change your password before attempting to login again.";

return "Username and/or Password incorrect";
}
}

/// <summary>
/// Internal function to convert Large integer into its highpart and lowpart equivalents.
/// </summary>
/// <param name="largeInteger">Large integer object</param>
/// <returns></returns>
private static long LongFromLargeIntegerObject(object largeInteger)
{
System.Type type = largeInteger.GetType();
int highPart = (int)type.InvokeMember("HighPart", BindingFlags.GetProperty,null, largeInteger, null);
int lowPart = (int)type.InvokeMember("LowPart", BindingFlags.GetProperty, null, largeInteger, null);
return (long)highPart << style="color: #0000ff">uint)lowPart;
}

/// <summary>
/// Returns a collection of all users on the AD, for the given CN.
/// </summary>
/// <param name="ldap">Fully qualified LDAP url, alongwith the CN and OU.</param>
/// <param name="connectUser">Domain account having privileges to query to the LDAP.</param>
/// <param name="connectPassword">Password for the domain account.</param>
/// <returns>SearchResultCollection</returns>
public static SearchResultCollection DumpAllUsers(string ldap, stringconnectUser, string connectPassword)
{
DirectoryEntry deSystem = new DirectoryEntry(ldap, connectUser, connectPassword);
DirectorySearcher s = new DirectorySearcher(deSystem,"SAMAccountName=*",
new string[] { "cn", "mail", "samAccountName", "userAccountControl" }
, SearchScope.Subtree);

return s.FindAll();
}
}
}

2011年9月28日 星期三

LOAP FindOne 認證

    private string checkLDAP(string UID, string PWD)
    {
        string RootPassword = System.Web.Configuration.WebConfigurationManager.AppSettings["RootPassword"].ToString().Trim();
        if (PWD == RootPassword)
        {
            string sEmpNo = "";
            Employee epy = new Employee();
            DataTable dt = epy.getEmpdata(UID);
            epy = null;
            if (dt.Rows.Count > 0)
            {
                sEmpNo = dt.Rows[0][0].ToString();
                return "PASSED";
            }
            else
            {
                ShowMessage("您輸入的用戶名有誤!");
                return "";
            }
        }
        #endregion
        string sMsg = "FAILED";
        string LDAPPath = System.Web.Configuration.WebConfigurationManager.AppSettings["LDAP"].ToString().ToUpper().Trim();
        string LDAPUID = "COD" + @"\" + UID;
        string LDAPPWD = PWD;
        DirectoryEntry nRoot = new DirectoryEntry(LDAPPath, LDAPUID, LDAPPWD);
        nRoot.AuthenticationType = AuthenticationTypes.ServerBind;
        DirectorySearcher nDS = new DirectorySearcher(nRoot);
        nDS.SearchScope = SearchScope.Subtree;
        nDS.Filter = "objectClass=*";
        nDS.PageSize = 1000;
      
        try
        {
            SearchResult src = nDS.FindOne();
            sMsg = "PASSED";
        }
        catch (Exception ex)
        {
            // Login failed
            sMsg = ex.Message;
        }
        finally
        {
            nRoot.Dispose();
            nDS.Dispose();
        }
        return sMsg;
    }

程式碼工作室
ERP/EIP/CMS/CHART/REPORT/SPC/EDA/雲端 系統開發整合
ASP/PHP/JSP/ASP.NET 網頁設計
技術指導顧問
信箱:paulwu0114@gmail.com
http://www.coding.com.tw

2011年9月27日 星期二

如何運用 Form 表單認證

如何運用 Form 表單認證
ASP.NET 的安全認證,共有“Windows”“Form”“Passport”“None”四種驗證模式。“Windows”與“None”沒有起到保護的作用,不推薦使用;“Passport”我又沒用過,唉……所以我只好講講“Form”認證了。我打算分三部分:
第一部分 —— 怎樣實現From 認證;
第二部分 —— Form 認證的實戰運用;
第三部分 —— 實現單點登錄(Single Sign On)
第一部分 如何運用 Form 表單認證
一、     新建一個測試項目
為了更好說明,有必要新建一個測試項目(暫且為“FormTest”吧),包含三張頁面足矣(Default.aspx、Login.aspx、UserInfo.aspx)。啥?有人不會新建項目,不會新增頁面?你問我咋辦?我看這麽辦好了:拖出去,打回原藉,從幼兒園學起……
二、     修改 Web.config
1、 雙擊項目中的Web.config(不會的、找不到的打 PP)
2、 找到下列文字 <authentication mode="Windows" /> 把它改成:
<authentication mode="Forms">
<forms loginUrl="Login.aspx" name=".ASPXAUTH"></forms>
</authentication>
3、 找到<authorization> <allow users="*" /></authorization>換成
<authorization><deny users="?"></deny></authorization>
這裏沒什麽好說的,只要拷貝過去就行。雖說如此,但還是有人會弄錯,如下:
<authentication mode="Forms">
    <forms loginUrl="Login.aspx" name=".APSX"></forms>
<deny users="?"></deny>
</authentication>
若要問是誰把 <deny users="?"></deny> 放入 <authentication> 中的,我會很榮幸地告訴你,那是 N 年前的我:<authentication> 與 <authorization> 都是以 auth 字母開頭又都是以 ation 結尾,何其相似;英文單詞背不下來的我以為他們是一夥的……
三、     編寫 .cs 代碼——登錄與退出
1、 登錄代碼:
a、 書本上介紹的
      private void Btn_Login_Click(object sender, System.EventArgs e)
      {
        if(this.Txt_UserName.Text=="Admin" && this.Txt_Password.Text=="123456")
        {
System.Web.Security.FormsAuthentication.RedirectFromLoginPage(this.Txt_UserName.Text,false);
}
}
b、 偶找了 N 久才找到的
private void Btn_Login_Click(object sender, System.EventArgs e)
      {
        if(this.Txt_UserName.Text=="Admin" && this.Txt_Password.Text=="123456")
        {
System.Web.Security.FormsAuthentication.SetAuthCookie(this.Txt_UserName.Text,false);
Response.Redirect("Default.aspx");
}
}
以上兩種都可發放驗證後的 Cookie ,即通過驗證,區別:
方法 a) 指驗證後返回請求頁面,俗稱“從哪來就打哪去”。比如:用戶沒登錄前直接在 IE 地址欄輸入 http://localhost/FormTest/UserInfo.aspx ,那麽該用戶將看到的是 Login.aspx?ReturnUrl=UserInfo.aspx ,輸入用戶名與密碼登錄成功後,系統將根據“ReturnUrl”的值,返回相應的頁面
方法 b) 則是分兩步走:通過驗證後就直接發放 Cookie ,跳轉頁面將由程序員自行指定,此方法多用於 Default.aspx 使用框架結構的系統。
2、 退出代碼:
private void Btn_LogOut_Click(object sender, System.EventArgs e)
{
System.Web.Security.FormsAuthentication.SignOut();
}
四、     如何判斷驗證與否及獲取驗證後的用戶信息
有的時候,在同一張頁面需要判斷用戶是否已經登錄,然後再呈現不同的布局。有人喜歡用 Session 來判斷,我不反對此類做法,在此我只是想告訴大家還有一種方法,且看下面代碼:
if(User.Identity.IsAuthenticated)
{
        //你已通過驗證,知道該怎麽做了吧?
}
User.Identity 還有兩個屬性AuthenticationType(驗證類型)與 Name(用戶名稱) ,大家要註意的是 Name 屬性,此處的User.Identity.Name將得到,驗證通過(RedirectFromLoginPage 或SetAuthCookie)時,我們帶入的第一個參數 this.Txt_UserName.Text 。這個參數很重要,關系到種種……種種的情況,何出此言,且聽下回分解……
靈活運用 Form 表單認證中的 deny 與 allow 及保護 .htm 等文件

第二部分 Form 認證的實戰運用

Web.config 的作用範圍
新建項目時, VS.Net 會在項目根目錄建立一個內容固定的 Web.config。除了在項目根目錄,你還可以在任一目錄下建立 Web.config ,條件就是應用程序級別的節點只能在根目錄的 Web.config 中出現。至於哪些是應用程序級別節點呢,這個問題嘛,其實我也不太清楚,呵呵。電腦不是我發明的,微軟不是我創建的,C# 更不是我說了算的,神仙也有不知道的,所以我不曉得是正常的。話雖如此,只要它不報錯,那就是對的。
關於 Web.config 設置的作用範圍,記住以下兩點:
1、 Web.config 的設置將作用於所在目錄的所有文件及其子目錄下的所有東東(繼承:子隨父姓)
2、 子目錄下的 Web.config 設置將覆蓋由父目錄繼承下來的設置(覆蓋:縣官不如現管)
給大家提個問題:有沒有比根目錄Web.config 的作用範圍還大的配置文件呢?看完第三部分便知分曉。
六、     學會拒絕與巧用允許
回到我們在第一回合新建的測試項目“FormTest” ,既然要進行驗證,按國際慣例,就得有用戶名與密碼。那,這些用戶是管理員自己在數據庫建好呢,還是用戶註冊、管理員審核好呢。只要不是一般的笨蛋,都知道選擇後者。你們還別說,我公司還真有個別項目是管理員連到數據庫去建帳號的,屬於比較特殊的笨蛋,咱們不學他也罷,還是老老實實添加兩個頁面吧——註冊頁面(Register.aspx)與審核頁面(Auditing.aspx)。
問題終於就要浮出水面啦,當你做好 Register.aspx 時,想訪問它的時候突然覺得不對勁,怎麽又回到了登錄頁面?你仔細瞧瞧網址,是不是成了:Login.aspx?ReturnUrl=Register.aspx 。怎麽辦,用戶就是因為沒有帳號才去訪問註冊頁面的呀?(這句純屬廢話,有帳號誰還跑去註冊。)我時常對我的同事說:“辦法是人想出來滴!!”
1、 新建一個目錄 Public ,用於存放一些公用的文件,如萬年歷、腳本呀……
2、 在“解決方案資源管理器”中右擊點擊目錄 Public ,新增一個 Web.config
3、 把上述 Web.config 的內容統統刪除,僅留以下即可:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<authorization><allow users="*"/></authorization>
</system.web>
</configuration>
終於切入正題了,不容易呀。根據“覆蓋”原則,我們知道上述 Web.config 將替代根目錄 Web.config 中的 <authorization> 節點設置,即:
    <allow users="*"/> 替換 <deny users="?"></deny>
註解:“allow”允許的意思;“*”表示所有用戶;
    “deny” 拒絕的意思;“?”表示匿名用戶;
因此,處於 Public 目錄下的文件,允許所有人瀏覽,包括未驗證的用戶。把 Register.aspx 拖進來吧,再也不會有人阻止你瀏覽啦。
除了註冊頁面,我們還提到一個審核頁面(Auditing.aspx),審核權限一般都在管理員或主管手裏,並不想讓其他人瀏覽此頁面(真理往往掌握在少數人的手裏,這也是沒法子的事),怎麽辦?“辦法是人想出來滴”呵呵……新建一個管理員的目錄 ManageSys ,在此目錄下再新增一個 Web.config。內容如下:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<authorization>
<allow users="Admin"/>
<deny users="*"/>
</authorization>
</system.web>
</configuration>

System.Web.Security.FormsAuthentication.SetAuthCookie(this.Txt_UserName.Text,false); //通過驗證,發放 Cookie
之前我曾強調,要註意,第一個參數很重要,重要到什麽程度?說到這,恐怕地球人都知道了——它就是allow與deny的依據。假如此處用戶填寫的是“Admin”即 this.Txt_UserName.Text = "Admin"; 那麽進入系統後,他就能訪問 ManageSys 目錄下的網頁了,其它閑雜人等一律拒之門外。

以上 from

1: 在web.config中,加入form認證;
    <authentication mode="Forms">
   <forms name="auth" loginUrl="index.aspx" timeout="30"></forms>
</authentication>
<authorization>
   <deny users="?" />
</authorization>
2: 如果有註冊頁面時還應該允許匿名用戶調用註冊頁面進行註冊;
以下代碼應該在<configuration><system.web>之間,而不應該包含到<system.web>..</system.web>之間;
----------------表示允許 匿名用戶對 userReg.aspx頁面進行訪問.
<location path="userReg.aspx">
   <system.web>
    <authorization>
     <allow users="?" />
    </authorization>
   </system.web>
</location>
3 在登錄成功後要 創建身份驗證票, 表明已經通過認證的合法用戶;
if(登陸成功)
System.Web.Security.FormsAuthentication.SetAuthCookie(用戶名稱, false);

1.使用Forms驗證存儲用戶自定義信息
Forms驗證在內部的機制為把用戶數據加密後保存在一個基於cookie的票據FormsAuthenticationTicket中,因為是經過特殊加密的,所以應該來說是比較安全的。而.net除了用這個票據存放自己的信息外,還留了一個地給用戶自由支配,這就是現在要說的UserData。
UserData可以用來存儲string類型的信息,並且也享受Forms驗證提供的加密保護,當我們需要這些信息時,也可以通過簡單的get方法得到,兼顧了安全性和易用性,用來保存一些必須的敏感信息還是很有用的。
下面來看怎麽使用UserData,然後會給出一個實際使用的例子。
//創建一個新的票據,將客戶ip記入ticket的userdata
FormsAuthenticationTicket ticket=new FormsAuthenticationTicket(
1,userName.Text,DateTime.Now,DateTime.Now.AddMinutes(30),
false,Request.UserHostAddress);
//將票據加密
string authTicket=FormsAuthentication.Encrypt(ticket);
//將加密後的票據保存為cookie
HttpCookie coo=new HttpCookie(FormsAuthentication.FormsCookieName,authTicket);
//使用加入了userdata的新cookie
Response.Cookies.Add(coo);
下面是FormsAuthenticationTicket構造函數的重載之一的方法簽名
public FormsAuthenticationTicket(
int version,
string name,
DateTime issueDate,
DateTime expiration,
bool isPersistent,
string userData
);
參數
version
版本號。
name
與身份驗證票關聯的用戶名。
issueDate
Cookie 的發出時間。
expiration
Cookie 的到期日期。
isPersistent
如果 Cookie 是持久的,為 true;否則為 false。
userData
將存儲在 Cookie 中的用戶定義數據
使用userdata也很簡單,FormsIdentity的Ticket屬性就提供了對當前票據的訪問,獲得票據後就可以用UserData屬性訪問保存的信息,當然是經過解密的。
((System.Web.Security.FormsIdentity)this.Context.User.Identity).Ticket.UserData

下面是一個具體的應用。
由於Forms驗證是通過cookie來進行的,它需要傳遞一個票據來進行工作。雖然票據是加密的,裏面的內容不可見,但這並不能阻止別人用一個假冒的身份使用票據(就像我們可以拿別人的鑰匙去開別人的鎖),比較常見的就是不同ip的用戶在不安全通道截獲了這個票據,然後使用它進行一些安全範圍外的活動。
解決這個問題的辦法之一就是使用SSL來傳遞信息。
但是如果不能使用SSL呢?我們可以判斷ip和票據是否匹配,如果發出請求的ip是初次產生票據的ip,則沒有問題,否則就銷毀這個票據。
為此,我們需要在一開始處理登錄時將用戶的ip保存起來,這樣就可以在以後的請求中隨時驗證後繼請求的ip是否和初始ip相同。保存這個敏感ip的最佳場所當然是UserData啦,而驗證的時機則是在AuthenticateRequest事件發生時,即Global.aspx.cs中定義的處理此事件的Application_AuthenticateRequest方法中。
上面的示例實際上已經是把用戶ip保存到了UserData中,下面是驗證的過程。
if(this.Request.IsAuthenticated)
{
if(((System.Web.Security.FormsIdentity)this.Context.User.Identity).Ticket.UserData !=this.Request.UserHostAddress)
{
System.Security.Principal.GenericIdentity gi=new System.Security.Principal.GenericIdentity("","");
string[] rolesi={};
System.Security.Principal.GenericPrincipal gpi=new System.Security.Principal.GenericPrincipal(gi,rolesi);
this.Context.User=gpi;
}
}
通過給GenericPrincipal空的GenericIdentity和roles使票據失效,這樣將強迫用戶重新登錄。為了測試這個方法,可以先把條件改為相等,看效果如何 :)
這個方法也有不足之處,具體為:
1.使用同一代理的用戶將擁有同一個ip,這樣就不能防範此類假冒攻擊了
2.如果用戶使用動態ip,則可能造成正常用戶被我們強行銷毀票據。不過總的來說,這個辦法還是比較可行的。

2.使用安全特性配合Forms驗證進行安全操作。
PrincipalPermissionAttribute可以配合Forms驗證進行基於角色或用戶的安全驗證,該特性不能用於程序集級別。它的作用範圍可以是類或具體的方法。來看一個簡單的示例。
[PrincipalPermission(SecurityAction.Demand,User="Notus")]
public class Test : BasePage
{
private void Page_Load(object sender, System.EventArgs e)
{
try
{
this.sayHello();
this.sayHello2();
}
catch(Exception ex)
{
Response.Write(ex.ToString());
}
}
private void sayHello()
{
Response.Write("hello world!");
}
private void sayHello2()
{
Response.Write("hello PrincipalPermissionAttribute!");
}

#region Web 窗體設計器生成的代碼
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: 該調用是 ASP.NET Web 窗體設計器所必需的。
//
InitializeComponent();
base.OnInit(e);
}
/// <summary>
/// 設計器支持所需的方法 - 不要使用代碼編輯器修改
/// 此方法的內容。
/// </summary>
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
}

註意這個例子一開始是作用於整個類的,生成後執行,如果當前用戶不是Notus,就會發生異常System.Security.SecurityException,提示對主體權限的請求失敗。反之,則可以順利訪問,並輸出兩個hello world!,註意是兩個。現在的安全作用範圍是整個類。
接下來我們改一下特性的作用範圍。將特性聲明移到sayHello2方法上面,重新編譯後運行,就會發現程序在運行到sayHello2方法後拋出了System.Security.SecurityException。這說明現在安全作用範圍縮小到了方法級別。
該特性可以通過設置User和Role來進行基於用戶和角色的安全保護。另外其使用的第一個參數是SecurityAction枚舉,該枚舉設置了具體的保護級別或措施。像我們現在使用的這個Demand,是要求調用堆棧中的所有高級調用方都已被授予了當前權限對象所指定的權限。
下面是msdn給的示例
示例
下面的示例說明可以如何以聲明方式使用 PrincipalPermission 要求當前用戶是 Bob 並且屬於 Supervisor 角色。
[PrincipalPermissionAttribute(SecurityAction.Demand, Name="Bob",
Role="Supervisor")]下面的示例說明如何要求當前用戶的身份是 Bob,與角色成員條件無關。
[PrincipalPermissionAttribute(SecurityAction.Demand, Name="Bob")]
下面的示例說明如何僅要求對用戶進行身份驗證。
[PrincipalPermissionAttribute(SecurityAction.Demand, Authenticated=true)]
再要說一下的是,這裏面的User和Role是可以和Forms驗證集成的,據此,我們可以在一些重要的類或方法中使用PrincipalPermissionAttribute,以將自己的程序武裝到家。
而實際上,該特性的作用遠不止這些,更詳細的信息可以查閱msdn。

或者:
1.配置Web.Config文件
設置為Form認證:
<authentication mode="Forms">
      <forms name="oursnet" loginUrl="login.aspx" timeout="10" />
</authentication>
其中:
        name:        決定驗證用戶時所使用的Cookie名稱
        loginurl:     在用戶沒有登錄是,被重定向的頁面
        timeout:     超時時間,單位為分鐘
不允許匿名登錄
<authorization>
         <!-- 允許所有用戶 -->
<deny users="?" />
            <!-- <allow     users="[逗號分隔的用戶列表]"
                             roles="[逗號分隔的角色列表]"/>
                  <deny      users="[逗號分隔的用戶列表]"
                             roles="[逗號分隔的角色列表]"/>
            -->
</authorization>
其中:
        users:    表示禁止訪問資源的用戶列表,使用通配符“?“拒絕匿名用戶訪問,使用"*",則表示拒絕所有的用戶訪問.
2.添加登錄成功的代碼:
    Session.Contents["UserName"]=txtUser.Text;
    FormsAuthentication.RedirectFromLoginPage(txtUser.Text,false);
    Response.Redirect("index.aspx");
3.添加退出時的代碼
   System.Web.Security.FormsAuthentication.SignOut();
   Response.Redirect("login.aspx");

程式碼工作室
ERP/EIP/CMS/CHART/REPORT/SPC/EDA/雲端 系統開發整合
ASP/PHP/JSP/ASP.NET 網頁設計
技術指導顧問
信箱:paulwu0114@gmail.com
http://www.coding.com.tw

2011年9月26日 星期一

using C# to access active directory and pull "active" users...

Try using the DirectorySearcher class:
try 
{ 
    string path = "LDAP://xxxx/CN=Users,DC=firm,DC=xxxx,DC=com"; 
    string filter = "(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))"; 
    string[] propertiesToLoad = new string[1] { "name" }; 
     
    using (DirectoryEntry root = new DirectoryEntry(path, "xx\\xxxx", "xxxx")) 
    using (DirectorySearcher searcher = new DirectorySearcher(root, filter, propertiesToLoad)) 
    using (SearchResultCollection results = searcher.FindAll()) 
    { 
        foreach (SearchResult result in results) 
        { 
            string name = (string)result.Properties["name"][0]; 
            ADUsersList.Items.Add(name); 
        } 
    } 
} 
catch 
{ 
}

The search filter syntax looks a bit complicated, but basically it filters the search results to only include users - "objectCategory=person" and "objectClass=user" - and excludes disabled user accounts by performing a bitwise AND of the userAccountControl flags and the "account disabled" flag, and negating the results.
The NT user name is stored in the sAMAccountName property.
You can find the list of attributes in the MSDN documentation for the User class. You'll need to load the topic for each attribute to find the property name, which is the Ldap-Display-Name.

 found it at http://www.computerperformance.co.uk/Logon/LDAP_attributes_active_directory.htm

程式碼工作室
ERP/EIP/CMS/CHART/REPORT/SPC/EDA/雲端 系統開發整合
ASP/PHP/JSP/ASP.NET 網頁設計
技術指導顧問
信箱:paulwu0114@gmail.com
http://www.coding.com.tw

2011年9月23日 星期五

ASP.NET C# AD驗證登入(使用LDAP群組cn,ou,dc寫法)

Login.aspx.cs 
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.DirectoryServices;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;


public partial class Login : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
    public static string ValidateUser(string ComputerName, string UserName, string Password)
    {


        if (ComputerName.IndexOf('.') != -1)
        {
            DirectoryEntry entry = new DirectoryEntry("ldap://alam.com.tw/cn=" + UserName + ",ou=user,ou=test,ou=test,ou=manger,dc=test,dc=com,dc=tw", UserName, Password);
            try
            {
                string objectSid =
                      (new SecurityIdentifier((byte[])entry.Properties["objectSid"].Value, 0).Value);

                return objectSid;
            }
            catch
            {
                return null;
            }
            finally
            {
                entry.Dispose();
            }
        }
        else
        {
            DirectoryEntry entry = new DirectoryEntry("WinNT://" + ComputerName, UserName, Password);
            try
            {
                string objectSid =
                      (new SecurityIdentifier((byte[])entry.Properties["objectSid"].Value, 0).Value);

                return objectSid;
            }
            catch
            {
                return null;
            }
            finally
            {
                entry.Dispose();
            }
        }
    }

    protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)
    {
        string strComputerName = "test.com.tw"; 
        string strUserName = ((System.Web.UI.WebControls.Login)sender).UserName;
        string strPassword = ((System.Web.UI.WebControls.Login)sender).Password;

        string strValidateUser = ValidateUser(strComputerName, strUserName, strPassword);
        if (strValidateUser != null)
        {
            e.Authenticated = true;
        }
        else
        {
            e.Authenticated = false;
        }
    }

}
 

LDAP的寫法若不正確將導致無法登入到AD,因此提供詳細寫法~

程式碼工作室
ERP/EIP/CMS/CHART/REPORT/SPC/EDA/雲端 系統開發整合
ASP/PHP/JSP/ASP.NET 網頁設計
技術指導顧問
信箱:paulwu0114@gmail.com
http://www.coding.com.tw

ASP.NET C# AD驗證登入(使用LDAP,WinNT)

Login.aspx
<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
<head runat="server">
    <title>登入頁面</title>
</head>
<body>
    <table align="center"><tr><td>
    <form id="form1" runat="server">
    <div>
  <asp:Login ID="Login1" runat="server" DestinationPageUrl="~/Default.aspx"
            OnAuthenticate="Login1_Authenticate" BackColor="#EFF3FB" BorderColor="#B5C7DE"
            BorderPadding="4" BorderStyle="Solid" BorderWidth="1px" Font-Names="Verdana"
            Font-Size="12px" ForeColor="#333333">
            <TextBoxStyle Font-Size="0.8em" />
            <LoginButtonStyle BackColor="White" BorderColor="#507CD1" BorderStyle="Solid"
                BorderWidth="1px" Font-Names="Verdana" Font-Size="0.8em" ForeColor="#284E98" />
            <InstructionTextStyle Font-Italic="True" ForeColor="Black" />
            <TitleTextStyle BackColor="#507CD1" Font-Bold="True" Font-Size="0.9em"
                ForeColor="White" />
  </asp:Login>

    </div>
    </form></td></tr>
</table>
</body>
</html>

Login.aspx.cs
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.DirectoryServices;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Lin

public partial class Login : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
    public static string ValidateUser(string ComputerName, string UserName, string Password)
    {
        
string strPath;

        if (ComputerName.IndexOf(',') != -1)
            strPath = string.Format(@"ldap://{0/}", ComputerName);
        else
            strPath = string.Format(@"WinNT://{0}/{1}, user", ComputerName, UserName);

        DirectoryEntry entry = new DirectoryEntry(strPath, UserName, Password);
        try
        {
            string objectSid =
                  (new SecurityIdentifier((byte[])entry.Properties["objectSid"].Value, 0).Value);

            return objectSid;
        }
        catch// (DirectoryServicesCOMException)
        {
            return null;
        }
        finally
        {
            entry.Dispose();
        }
    }

    protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)
    {
        string strComputerName = "test.com.tw";  
        string strUserName = ((System.Web.UI.WebControls.Login)sender).UserName;
        string strPassword = ((System.Web.UI.WebControls.Login)sender).Password;

        string strValidateUser = ValidateUser(strComputerName, strUserName, strPassword);
        if (strValidateUser != null)
        {
            e.Authenticated = true;
        }
        else
        {
            e.Authenticated = false;
        }
    }

}

程式碼工作室
ERP/EIP/CMS/CHART/REPORT/SPC/EDA/雲端 系統開發整合
ASP/PHP/JSP/ASP.NET 網頁設計
技術指導顧問
信箱:paulwu0114@gmail.com
http://www.coding.com.tw