재우니의 블로그

 

 

ASP .NET의 Active Directory 도메인 서비스 인증

이 항목에서는 ASP.NET 응용 프로그램이 폼 인증을 사용하여 사용자가 LDAP(Lightweight Directory Access Protocol)를 통해 Active Directory 도메인 서비스에 대한 인증을 받을 수 있도록 하는 방법을 보여 줍니다. 사용자가 인증되고 리디렉션되면 Global.asax 파일의 Application_AuthenticateRequest 메서드를 사용하여 요청 전체로 이동되는 HttpContext.User 속성에 GenericPrincipal 개체를 저장할 수 있습니다.
새 ASP.NET 웹 응용 프로그램을 만들려면
  1. Microsoft Visual Studio .NET을 시작합니다.

  2. 파일 메뉴에서 새로 만들기를 선택한 다음 프로젝트를 클릭합니다.

  3. 프로젝트 형식에서 Visual C# 프로젝트를 클릭한 후 템플릿에서 ASP.NET 웹 응용 프로그램을 클릭합니다.

  4. 이름 상자에 FormsAuthAd를 입력합니다.

  5. 로컬 서버를 사용하고 있는 경우 서버 상자에 기본 http://localhost를 그대로 둡니다. 그렇지 않으면 서버에 경로를 추가합니다. 확인을 클릭합니다.

  6. 솔루션 탐색기에서 참조 노드를 마우스 오른쪽 단추로 클릭한 후 참조 추가를 클릭합니다.

  7. 참조 추가 대화 상자의 .NET 탭에서 System.DirectoryServices.dll을 클릭한 후 선택을 클릭하고 확인을 클릭합니다.

System.DirectoryServices 인증 코드를 추가하려면
  1. 솔루션 탐색기에서 프로젝트 노드를 마우스 오른쪽 단추로 클릭한 후 추가를 가리킨 다음 새 항목 추가를 클릭합니다.

  2. 템플릿에서 클래스를 클릭합니다.

  3. 이름 상자에 LdapAuthentication.cs를 입력한 후 열기를 클릭합니다.

  4. LdapAuthentication.cs 파일의 기존 코드를 다음 코드로 바꿉니다.

    C#
    using System;
    using System.Text;
    using System.Collections;
    using System.Web.Security;
    
    using System.Security.Principal;    
    using System.DirectoryServices;
    
    
    namespace FormsAuth
    {
      public class LdapAuthentication
      {
        private string _path;
        private string _filterAttribute;
    
        public LdapAuthentication(string path)
        {
          _path = path;
        }
    
        public bool IsAuthenticated(string domain, string username, string pwd)
        {
          string domainAndUsername = domain + @"\" + username;
          DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
    
          try
          {
            //Bind to the native AdsObject to force authentication.
            object obj = entry.NativeObject;
    
            DirectorySearcher search = new DirectorySearcher(entry);
    
            search.Filter = "(SAMAccountName=" + username + ")";
            search.PropertiesToLoad.Add("cn");
            SearchResult result = search.FindOne();
    
            if(null == result)
            {
              return false;
            }
    
            //Update the new path to the user in the directory.
            _path = result.Path;
            _filterAttribute = (string)result.Properties["cn"][0];
          }
          catch (Exception ex)
          {
            throw new Exception("Error authenticating user. " + ex.Message);
          }
    
          return true;
        }
    
        public string GetGroups()
        {
          DirectorySearcher search = new DirectorySearcher(_path);
          search.Filter = "(cn=" + _filterAttribute + ")";
          search.PropertiesToLoad.Add("memberOf");
          StringBuilder groupNames = new StringBuilder();
    
          try
          {
            SearchResult result = search.FindOne();
            int propertyCount = result.Properties["memberOf"].Count;
            string dn;
            int equalsIndex, commaIndex;
    
            for(int propertyCounter = 0; propertyCounter < propertyCount; propertyCounter++)
            {
              dn = (string)result.Properties["memberOf"][propertyCounter];
           equalsIndex = dn.IndexOf("=", 1);
              commaIndex = dn.IndexOf(",", 1);
              if(-1 == equalsIndex)
              {
                return null;
              }
              groupNames.Append(dn.Substring((equalsIndex + 1), (commaIndex - equalsIndex) - 1));
              groupNames.Append("|");
            }
          }
        catch(Exception ex)
        {
          throw new Exception("Error obtaining group names. " + ex.Message);
        }
        return groupNames.ToString();
      }
    }
    }
    

앞의 절차에서 인증 코드는 Active Directory 도메인 서비스의 도메인, 사용자 이름, 암호 및 트리 경로를 수락합니다. 이 코드는 LDAP 디렉터리 공급자를 사용합니다. Logon.aspx 페이지의 코드는 LdapAuthentication.IsAuthenticated 메서드를 호출하고 사용자로부터 수집한 자격 증명을 전달합니다. 그런 다음 디렉터리 트리, 사용자 이름 및 암호에 대한 경로로 DirectoryEntry 개체가 만들어집니다. 사용자 이름은 도메인\사용자 이름 형식이어야 합니다.

DirectoryEntry 개체는 NativeObject 속성을 가져와 AdsObject가 강제로 바인딩되도록 합니다. 이 작업이 성공적으로 수행되면 DirectorySearcher 개체를 만들고 sAMAccountName을 필터링하여 사용자의 CN 특성을 가져옵니다. Active Directory 도메인 서비스 스키마의 에 대한 자세한 내용은 sAMAccountName에 대한 자세한 내용은 MSDN Library의 "sAMAccountName" 또는 "SAM-Account-Name 특성"을 참조하십시오. 사용자가 인증되면 IsAuthenticated 메서드는 true로 반환됩니다. 사용자가 속한 그룹 목록을 가져오기 위해 이 코드는 LdapAuthentication.GetGroups 메서드를 호출합니다. LdapAuthentication.GetGroups 메서드는 DirectorySearcher 개체를 만들고 memberOf 특성에 따라 필터링하여 사용자가 속한 보안 및 메일 그룹 목록을 가져옵니다. Active Directory 도메인 서비스 스키마의 에 대한 자세한 내용은 memberOf에 대한 자세한 내용은 MSDN Library의 "memberOf" 또는 "Is-Member-Of-DL 특성"을 참조하십시오. 이 메서드는 파이프(|)로 구분된 그룹 목록을 반환합니다. LdapAuthentication.GetGroups 메서드는 문자열을 조작하고 자릅니다. 이 메서드는 인증 쿠키에 저장된 문자열 길이를 줄입니다. 이 문자열이 잘리지 않은 경우 각 그룹의 형식은 다음과 같습니다.

 

 

CN=...,...,DC=domain,DC=com

 

LdapAuthentication.GetGroups 메서드는 매우 긴 문자열을 반환할 수 있습니다. 이 문자열의 길이가 쿠키의 길이보다 길면 인증 쿠키가 만들어지지 않을 수도 있습니다. 이 문자열이 쿠키의 길이를 초과할 수 있는 경우 그룹 정보를 ASP.NET 캐시 개체 또는 데이터베이스에 저장하려고 할 수 있습니다. 또는 그룹 정보를 암호화한 후 숨겨진 폼 필드에 저장할 수도 있습니다.

Global.asax 파일의 코드는 Application_AuthenticateRequest 이벤트 처리기를 제공합니다. 이 이벤트 처리기는 Context.Request.Cookies 컬렉션에서 인증 쿠키를 검색하고, 쿠키의 암호를 해독하고, FormsAuthenticationTicket.UserData 속성에 저장될 그룹 목록을 검색합니다. 그룹은 Logon.aspx 페이지에서 만들어진 파이프로 구분된 목록으로 표시됩니다. 코드는 문자열 배열의 문자열 구문을 분석하여 GenericPrincipal 개체를 만듭니다. GenericPrincipal 개체는 만들어진 후에 HttpContext.User 속성에 배치됩니다.

Global.asax 코드를 쓰려면
  1. 솔루션 탐색기에서 Global.asax를 마우스 오른쪽 단추로 클릭한 후 코드 보기를 클릭합니다.

  2. Global.asax.cs 파일의 코드 숨김 부분 맨 위에 다음 코드를 추가합니다.

    C#
    using System.Web.Security;
    using System.Security.Principal;
    
  3. Application_AuthenticateRequest에 대한 기존의 빈 이벤트 처리기를 다음 코드로 바꿉니다.

    C#
    void Application_AuthenticateRequest(object sender, EventArgs e)
    {
      string cookieName = FormsAuthentication.FormsCookieName;
      HttpCookie authCookie = Context.Request.Cookies[cookieName];
    
      if(null == authCookie)
      {
        //There is no authentication cookie.
        return;
      }
      FormsAuthenticationTicket authTicket = null;
      try
      {
        authTicket = FormsAuthentication.Decrypt(authCookie.Value);
      }
      catch(Exception ex)
      {
        //Write the exception to the Event Log.
        return;
      }
    if(null == authTicket)
      {
        //Cookie failed to decrypt.
        return;
      }
      //When the ticket was created, the UserData property was assigned a
      //pipe-delimited string of group names.
      string[] groups = authTicket.UserData.Split(new char[]{'|'});
      //Create an Identity.
      GenericIdentity id = new GenericIdentity(authTicket.Name, "LdapAuthentication");
      //This principal flows throughout the request.
      GenericPrincipal principal = new GenericPrincipal(id, groups);
      Context.User = principal;
    }
    
    

이 섹션에서 Web.config 파일의 <forms>, <authentication> 및 <authorization> 요소를 구성합니다. 이와 같이 변경하면 인증된 사용자만 응용 프로그램에 액세스할 수 있으며 인증되지 않은 요청은 Logon.aspx 페이지로 리디렉션됩니다. 특정 사용자 및 그룹만 응용 프로그램에 액세스할 수 있도록 이 구성을 수정할 수 있습니다.

Web.config 파일을 수정하려면
  1. 메모장에서 Web.config를 엽니다.

  2. 기존 코드를 다음 코드로 바꿉니다.

    XML
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <system.web>
        <authentication mode="Forms">
          <forms loginUrl="logon.aspx" name="adAuthCookie" timeout="10" path="/">
          </forms>
        </authentication>
        <authorization>
          <deny users="?"/>
          <allow users="*"/>
        </authorization>
        <identity impersonate="true"/>
      </system.web>
     </configuration>
    

다음 구성 요소를 확인하십시오.

XML
<identity impersonate="true"/>

이 요소를 사용하면 ASP.NET은 Microsoft IIS(인터넷 정보 서비스)에서 익명 계정으로 구성된 계정을 가장합니다. 이러한 구성 결과 이 응용 프로그램에 대한 모든 요청은 구성된 계정의 보안 컨텍스트에서 실행됩니다. 사용자는 Active Directory 도메인 서비스에 대해 인증을 받기 위한 자격 증명을 제공하지만 Active Directory 도메인 서비스에 액세스하는 계정은 구성된 계정입니다.

익명 인증에 대해 IIS를 구성하려면
  1. IIS 관리자(관리 도구) 또는 IIS용 MMC 스냅인에서 인증을 구성할 웹 사이트를 마우스 오른쪽 단추로 클릭한 후 속성을 클릭합니다.

  2. 디렉터리 보안 탭을 클릭하고 인증 및 액세스 제어에서 편집을 클릭합니다.

  3. 익명 인증 확인란Windows Server 2003의 (익명 액세스 사용)을 선택합니다.

  4. 응용 프로그램의 익명 계정을 Active Directory 도메인 서비스에 대해 사용 권한이 있는 계정으로 만듭니다.

  5. IIS에서 암호 제어 허용 확인란이 있으면 선택 취소합니다. 기본 IUSR_<컴퓨터 이름> 계정에는 Active Directory 도메인 서비스에 대한 사용 권한이 없습니다.

Logon.aspx 페이지를 만들려면
  1. 솔루션 탐색기에서 프로젝트 노드를 마우스 오른쪽 단추로 클릭한 후 추가를 가리킨 다음 Web Form 추가를 클릭합니다.

  2. 이름 상자에 Logon.aspx를 입력한 후 열기를 클릭합니다.

  3. 솔루션 탐색기에서 Logon.aspx를 마우스 오른쪽 단추로 클릭한 후 뷰 디자이너를 클릭합니다.

  4. 디자이너에서 HTML 탭을 클릭합니다.

  5. 기존 코드를 다음 코드로 바꿉니다.

    XML
    <%@ Page language="c#" AutoEventWireup="true" %>
    <%@ Import Namespace="FormsAuth" %>
    <html>
      <body>
        <form id="Login" method="post" runat="server">
          <asp:Label ID="Label1" Runat=server >Domain:</asp:Label>
          <asp:TextBox ID="txtDomain" Runat=server ></asp:TextBox><br>    
          <asp:Label ID="Label2" Runat=server >Username:</asp:Label>
          <asp:TextBox ID=txtUsername Runat=server ></asp:TextBox><br>
          <asp:Label ID="Label3" Runat=server >Password:</asp:Label>
          <asp:TextBox ID="txtPassword" Runat=server TextMode=Password></asp:TextBox><br>
          <asp:Button ID="btnLogin" Runat=server Text="Login" OnClick="Login_Click"></asp:Button><br>
          <asp:Label ID="errorLabel" Runat=server ForeColor=#ff3300></asp:Label><br>
          <asp:CheckBox ID=chkPersist Runat=server Text="Persist Cookie" />
        </form>
      </body>
    </html>
    <script runat=server>
    void Login_Click(object sender, EventArgs e)
    {
      string adPath = "LDAP://" + txtDomain.Text;
    
      LdapAuthentication adAuth = new LdapAuthentication(adPath);
      try
      {
        if(true == adAuth.IsAuthenticated(txtDomain.Text, txtUsername.Text, txtPassword.Text))
        {
          string groups = adAuth.GetGroups(txtDomain.Text, txtUsername.Text, txtPassword.Text);
    
    
          //Create the ticket, and add the groups.
          bool isCookiePersistent = chkPersist.Checked;
          FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1, 
                    txtUsername.Text,DateTime.Now, DateTime.Now.AddMinutes(60), isCookiePersistent, groups);
    
          //Encrypt the ticket.
          string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
    
          //Create a cookie, and then add the encrypted ticket to the cookie as data.
          HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
    
          if(true == isCookiePersistent)
          authCookie.Expires = authTicket.Expiration;
    
          //Add the cookie to the outgoing cookies collection.
          Response.Cookies.Add(authCookie);
    
          //You can redirect now.
          Response.Redirect(FormsAuthentication.GetRedirectUrl(txtUsername.Text, false));
        }
        else
        {
          errorLabel.Text = "Authentication did not succeed. Check user name and password.";
        }
      }
      catch(Exception ex)
      {
        errorLabel.Text = "Error authenticating. " + ex.Message;
      }
    }
    </script>
    
  6. Logon.aspx 페이지의 경로를 LDAP 디렉터리 서버를 가리키도록 수정합니다.

Logon.aspx 페이지는 사용자로부터 정보를 수집하고 LdapAuthentication 클래스에 대해 메서드를 호출하는 페이지입니다. 해당 코드는 사용자를 인증하고 그룹 목록을 가져온 후에 FormsAuthenticationTicket 개체를 만들고, 티켓을 암호화하고, 암호화된 티켓을 쿠키에 추가하고, 쿠키를 HttpResponse.Cookies 컬렉션에 추가한 다음 원래 요청된 URL로 요청을 리디렉션합니다.

WebForm1.aspx 페이지는 원래 요청된 페이지입니다. 사용자가 이 페이지를 요청하면 요청은 Logon.aspx 페이지로 리디렉션됩니다. 요청은 인증되면 WebForm1.aspx 페이지로 리디렉션됩니다.

WebForm1.aspx 페이지를 수정하려면
  1. 솔루션 탐색기에서 WebForm1.aspx를 마우스 오른쪽 단추로 클릭한 후 뷰 디자이너를 클릭합니다.

  2. 디자이너에서 HTML 탭을 클릭합니다.

  3. 기존 코드를 다음 코드로 바꿉니다.

    XML
    <%@ Page language="c#" AutoEventWireup="true" %>
    <%@ Import Namespace="System.Security.Principal" %>
    <html>
      <body>
        <form id="Form1" method="post" runat="server">
          <asp:Label ID="lblName" Runat=server /><br>
          <asp:Label ID="lblAuthType" Runat=server />
        </form>
      </body>
    </html>
    <script runat=server>
    void Page_Load(object sender, EventArgs e)
    {
      lblName.Text = "Hello " + Context.User.Identity.Name + ".";
      lblAuthType.Text = "You were authenticated using " + Context.User.Identity.AuthenticationType + ".";
    }
    </script>
    
  4. 모든 파일을 저장한 후 프로젝트를 컴파일합니다.

  5. WebForm1.aspx 페이지를 요청합니다. Logon.aspx로 리디렉션됩니다.

  6. 로그온 자격 증명을 입력한 후 전송을 클릭합니다. WebForm1.aspx로 리디렉션되면 사용자 이름이 뜨고 LdapAuthentication은 Context.User.AuthenticationType 속성에 대한 인증 형식입니다.

참고:
폼 인증을 사용할 때는 SSL(Secure Sockets Layer) 암호화를 사용하는 것이 좋습니다. 사용자는 인증 쿠키를 기반으로 식별하고 이 응용 프로그램의 SSL 암호화를 통해 다른 사용자가 인증 쿠키 및 전송 중인 다른 중요한 정보를 손상시키지 못하기 때문입니다.

 

 

참고 항목

Send comments about this topic to Microsoft.

Copyright © 2007 by Microsoft Corporation. All rights reserved.