2010-03-12 7 views
22

Windows 인증 또는 폼 인증을 사용하여 ASP.NET MVC 웹 응용 프로그램에 대해 사용자가 인증 할 수 있도록하는 시나리오가 있습니다. 사용자가 내부 네트워크에있는 경우 Windows 인증을 사용하고 외부에서 연결하는 경우 폼 인증을 사용합니다. 꽤 많은 사람들이이 질문에 ASP.NET MVC 웹 응용 프로그램을 구성하는 방법을 묻는 것을 보았습니다. 그러나 완전한 설명을 찾지 못했습니다.ASP.NET MVC 및 혼합 모드 인증

다른 사람이 코드 예제와 함께 어떻게 수행되는지에 대한 자세한 설명을 제공 할 수 있습니까?

감사합니다.

앨런 T

답변

14

mixed authentication mode라고합니다. 기본적으로 응용 프로그램에서이 작업을 수행 할 수 없습니다. IIS에서 가상 디렉터리에 대한 Windows 인증을 설정하면 다른 도메인의 사용자를 더 이상 허용하지 않기 때문입니다. 따라서 기본적으로 Windows 인증을 사용하는 두 개의 응용 프로그램과 Forms 인증을 사용하는 두 번째 응용 프로그램 (기본 응용 프로그램)이 필요합니다. 첫 번째 응용 프로그램은 도메인 사용자에 대한 인증 티켓을 발급하여 기본 응용 프로그램으로 간단히 리디렉션 할 단일 주소로 구성됩니다.

+0

정보 및 링크를 제공해 주셔서 감사합니다. 나는 그것을 시도 할 것이다. –

+0

통합 모드를 사용하는 IIS7에서는 작동하지 않습니다. http://stackoverflow.com/questions/289317/iis7-and-authentication-problems –

+0

너무 빠릅니다. 나는 아직도이 기능이 필요한 두 개의 MVC 응용 프로그램을 가지고 있으므로이 솔루션을 찾고있다. –

13

이 작업을 수행 할 수 있습니다. 구성을 반대로하고 익명 및 폼 인증을 사용하도록 app/root를 설정합니다. 이렇게하면 동일한 웹 응용 프로그램 내에서 혼합 인증을 구성 할 수 있지만 까다 롭습니다. 먼저 loginUrl = "~/WinLogin/WinLogin2.aspx"를 사용하여 양식 인증을위한 응용 프로그램을 구성하십시오. MVC에서 라우팅은 IIS에서 설정 한 인증 규칙을 무시하므로 IIS에서 파일에 대한 인증을 설정할 수 있으므로 aspx 페이지를 사용해야합니다. 루트 웹 응용 프로그램에서 익명 및 폼 인증을 사용합니다. Windows 인증을 사용하고 root/WinLogin 디렉토리에서 익명 인증을 사용하지 않도록 설정하십시오. 사용자 정의 401 및 401.2 오류 페이지를 추가하여 계정/서명 URL로 다시 리디렉션하십시오.

이렇게하면 통과 할 수있는 모든 브라우저에서 Windows 통합 인증을 사용하여 자동 서명 할 수 있습니다. 일부 기기는 iPhone과 같은 자격증 명과 블랙 베리와 같은 기타 기기에 로그인 페이지로 리디렉션되는 메시지가 표시됩니다.

이것은 또한 역할을 명시 적으로 추가하는 쿠키를 생성하고 역할 기반 인증을 사용할 수 있도록 일반 원칙을 만듭니다.

WinLogin2.aspx (IIS의 "루트"웹 응용 프로그램에서 WinLogin 디렉터리에 있고 Windows 인증, 익명 해제 및 양식 사용 가능으로 구성됨 (해제 할 수 없음) note IIS는 Windows 인증을 활성화 단지) 무시 :

var logonUser = Request.ServerVariables["LOGON_USER"]; 
     if (!String.IsNullOrWhiteSpace(logonUser)) 
     { 
      if (logonUser.Split('\\').Length > 1) 
      { 
       var domain = logonUser.Split('\\')[0]; 
       var username = logonUser.Split('\\')[1]; 

       var timeout = 30; 

       var encTicket = CreateTicketWithSecurityGroups(false, username, domain, timeout); 

       var authCookie = new HttpCookie(".MVCAUTH", encTicket) { HttpOnly = true }; 
       Response.Cookies.Add(authCookie); 


      } 
      //else 
      //{ 
      // this is a redirect due to returnUrl being WinLogin page, in which logonUser will no longer have domain attached 
      // ignore as forms ticket should already exist 
      //} 

      string returnUrl = Request.QueryString["ReturnUrl"]; 

      if (returnUrl.IsEmpty()) 
      { 
       Response.Redirect("~/"); 
      } 
      else 
      { 
       Response.Redirect(returnUrl); 
      } 
     } 

     public static string CreateTicketWithSecurityGroups(bool rememberMe, string username, string domain, int timeout) 
    { 
     using (var context = new PrincipalContext(ContextType.Domain, domain)) 
     { 
      using (var principal = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username)) 
      { 
       var securityGroups = String.Join(";", principal.GetAuthorizationGroups()); 

       var ticket = 
        new FormsAuthenticationTicket(1, 
                username, 
                DateTime.UtcNow, 
                DateTime.UtcNow.AddMinutes(timeout), 
                rememberMe, 
                securityGroups, 
                "/"); 

       string encTicket = FormsAuthentication.Encrypt(ticket); 
       return encTicket; 
      } 
     } 
    } 

IIS 7.5에서 오류 페이지를 클릭,이 코드로, Redirect401.htm 파일의 경로를 파일로 401 페이지를 설정 :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org /TR/xhtml1/DTD/xhtml1-transitional.dtd"> 

    <html xmlns="http://www.w3.org/1999/xhtml"> 
    <head> 
     <title></title> 
     <script> 
      window.location.assign('../Account/Signin'); 
     </script> 
    </head> 
    <body> 

    </body> 
    </html> 

을 AccountController에서. ..

Global.asax에에서
public ActionResult SignIn() 
    { 
     return View(new SignInModel()); 
    } 

    // 
    // POST: /Account/SignIn 

    [HttpPost] 
    public ActionResult SignIn(SignInModel model, string returnUrl) 
    { 
     if (ModelState.IsValid) 
     { 
      if (Membership.ValidateUser(model.UserName, model.Password)) 
      { 
       string encTicket = CreateTicketWithSecurityGroups(model.RememberMe, model.UserName, model.Domain, FormsAuthentication.Timeout.Minutes); 

       Response.Cookies.Add(new HttpCookie(".MVCAUTH", encTicket)); 

       //var returnUrl = ""; 
       for (var i = 0; i < Request.Cookies.Count; i++) 
       { 
        HttpCookie cookie = Request.Cookies[i]; 
        if (cookie.Name == ".MVCRETURNURL") 
        { 
         returnUrl = cookie.Value; 
         break; 
        } 
       } 

       if (returnUrl.IsEmpty()) 
       { 
        return Redirect("~/"); 
       } 

       return Redirect(returnUrl); 
      } 

      ModelState.AddModelError("Log In Failure", "The username/password combination is invalid"); 
     } 

     return View(model); 
    } 

    // 
    // GET: /Account/SignOut 

    public ActionResult SignOut() 
    { 
     FormsAuthentication.SignOut(); 

     if (Request.Cookies[".MVCRETURNURL"] != null) 
     { 
      var returnUrlCookie = new HttpCookie(".MVCRETURNURL") { Expires = DateTime.Now.AddDays(-1d) }; 
      Response.Cookies.Add(returnUrlCookie); 
     } 

     // Redirect back to sign in page so user can 
     // sign in with different credentials 

     return RedirectToAction("SignIn", "Account"); 

:

protected void Application_BeginRequest(object sender, EventArgs e) 
    { 


     try 
     { 
      bool cookieFound = false; 

      HttpCookie authCookie = null; 

      for (int i = 0; i < Request.Cookies.Count; i++) 
      { 
       HttpCookie cookie = Request.Cookies[i]; 
       if (cookie.Name == ".MVCAUTH") 
       { 
        cookieFound = true; 
        authCookie = cookie; 
        break; 
       } 
      } 

      if (cookieFound) 
      { 
       // Extract the roles from the cookie, and assign to our current principal, which is attached to the HttpContext. 
       FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(authCookie.Value); 
       HttpContext.Current.User = new GenericPrincipal(new FormsIdentity(ticket), ticket.UserData.Split(';')); 
      } 



     } 
     catch (Exception ex) 
     { 
      throw; 
     } 

    } 


    protected void Application_AuthenticateRequest() 
    { 
     var returnUrl = Request.QueryString["ReturnUrl"]; 
     if (!Request.IsAuthenticated && 
      !String.IsNullOrWhiteSpace(returnUrl)) 
     { 
      var returnUrlCookie = new HttpCookie(".MVCRETURNURL", returnUrl) {HttpOnly = true}; 
      Response.Cookies.Add(returnUrlCookie); 
     } 
    } 

웹.설정

<!--<authorization> 
    <deny users="?"/> 
</authorization>--> 
<authentication mode="Forms"> 
    <forms name=".MVCAUTH" loginUrl="~/WinLogin/WinLogin2.aspx" timeout="30" enableCrossAppRedirects="true"/> 
</authentication> 
<membership defaultProvider="AspNetActiveDirectoryMembershipProvider"> 
    <providers> 
    <add name="AspNetActiveDirectoryMembershipProvider" type="System.Web.Security.ActiveDirectoryMembershipProvider,   System.Web, Version=4.0.0.0, Culture=neutral,   PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ADService" connectionProtection="Secure" enablePasswordReset="false" enableSearchMethods="true" requiresQuestionAndAnswer="true" applicationName="/" description="Default AD connection" requiresUniqueEmail="false" clientSearchTimeout="30" serverSearchTimeout="30" attributeMapPasswordQuestion="department" attributeMapPasswordAnswer="division" attributeMapEmail="mail" attributeMapUsername="sAMAccountName" maxInvalidPasswordAttempts="5" passwordAttemptWindow="10" passwordAnswerAttemptLockoutDuration="30" minRequiredPasswordLength="7" minRequiredNonalphanumericCharacters="1"/> 
    </providers> 
</membership><machineKey decryptionKey="..." validationKey="..." /> </system.web><connectionStrings> <add name="ADService" connectionString="LDAP://SERVER:389"/></connectionStrings> 

신용 찾을 수 http://msdn.microsoft.com/en-us/library/ms972958.aspx

+1

안녕하세요 - Redirect401.htm 파일이 추가 된 스크립트를 통해 리디렉션됩니다. 401.1에 대한 오류 처리를 리디렉션으로 설정하여이를 수행 할 수 있습니까? IIS에서 이것은 401 오류 페이지에 대해 '302 리디렉션으로 응답'옵션으로 변경된다는 것을 의미합니다. 말은 절대 URL을 사용하지만 루트 친척도 사용할 수 있습니다. –

4

이 아마이 질문의 하단에 살고 결코 뜻에 빚진하지만 난

http://mvolo.com/iis-70-twolevel-authentication-with-forms-authentication-and-windows-authentication/

그것에서 설명한 것을 구현할 수 있었다 아주 쉽고 사소했습니다. FormsAuthModule을 확장하고 web.config를 변경하기 만하면 여러 응용 프로그램이나 쿠키 해킹이 필요하지 않았습니다.

+1

내가 언급 한 시나리오가 문제의 시나리오와 다른 것 같습니다. 2 레벨 인증을 사용하려면 양식 인증뿐만 아니라 두 창 모두를 사용하여 사용자를 인증해야합니다. 그러나 우리는 사용자 (인트라넷/인터넷)의 종류에 따라 Windows 또는 폼 기반 인증에 의해 인증되어야한다고 요구합니다 – Samra

0

나는 이것이 오래된 게시물이라는 것을 알고있다. 그러나 모든 것은 인터넷에서 영원히 산다!

어쨌든 이전 웹 사이트를 IIS6에서 IIS8로 이동해야했습니다. 이것은 WebForms 웹 사이트이지만이 간단한 솔루션은 동일하다고 가정합니다.

'System.Security.Principal.WindowsIdentity'형식의 개체를 'System.Web.Security.FormsIdentity'형식으로 캐스팅 할 수 없습니다.

나는 웹 사이트를위한 새로운 응용 프로그램 풀을 만들었습니다. 이것을 만들 때 Managed 파이프 라인 모드를 '클래식'으로 설정합니다. (여기에서 더 많은 것을 읽으십시오 - http://www.hanselman.com/blog/MovingOldAppsFromIIS6ToIIS8AndWhyClassicModeExists.aspx) 방금 작성한 새로운 수영장에 웹 사이트의 응용 프로그램 풀을 설정하는 것을 잊지 마십시오.