2010-03-12 41 views
22

Tengo una situación en la que solicito a los usuarios que puedan autenticarse en una aplicación web ASP.NET MVC utilizando la autenticación de Windows o la autenticación de formularios. Si el usuario está en la red interna, usará la autenticación de Windows y, si se conectan externamente, usarán la autenticación Forms. He visto bastantes personas preguntando cómo puedo configurar una aplicación web ASP.NET MVC para esto, pero no he encontrado una explicación completa.ASP.NET MVC y autenticación de modo mixto

Por favor alguien puede dar una explicación detallada, con ejemplos de código, sobre cómo se haría esto?

Gracias.

Alan T

Respuesta

14

Esto se llama mixed authentication mode. Básicamente, no puede lograr esto en una aplicación única porque en IIS, una vez que configura la autenticación de Windows para un directorio virtual, ya no aceptará usuarios de diferentes dominios. Así que, básicamente, debe tener dos aplicaciones, la primera con autenticación de Windows y la segunda (la aplicación principal) con la autenticación de formularios. La primera aplicación consistirá en una sola dirección que simplemente redireccionará a la aplicación principal al emitir un ticket de autenticación para el usuario del dominio.

+0

Gracias por la información y el enlace. Lo probaré. –

+0

Esto no funcionará en IIS7 utilizando el modo integrado: http://stackoverflow.com/questions/289317/iis7-and-authentication-problems –

+0

Lo descubrí también Garry. Todavía estoy buscando una solución para esto ya que ahora tengo dos aplicaciones MVC que requerirán esta funcionalidad. –

13

Esto se puede hacer. Invierta la configuración, configure la aplicación/raíz para usar Autenticación anónima y de formularios ... De esta manera, puede configurar la autenticación mixta dentro de la misma aplicación web, pero es complicado. Primero, configure su aplicación para Autenticación de formularios con loginUrl = "~/WinLogin/WinLogin2.aspx". En MVC, el enrutamiento anula las reglas de autenticación establecidas por IIS, por lo que necesita usar una página aspx, ya que IIS puede establecer la autenticación en el archivo. Habilite la autenticación anónima y de formularios en la aplicación web raíz. Habilite la Autenticación de Windows y deshabilite la autenticación anónima en el directorio raíz/WinLogin. Agregue páginas de error 401 y 401.2 personalizadas para redirigir a la URL de cuenta/inicio de sesión.

Esto permitirá que cualquier navegador con capacidad de transferencia pueda usar la autenticación integrada de Windows para iniciar sesión automáticamente. Mientras que algunos dispositivos recibirán una solicitud de credenciales (como iPhone) y otros dispositivos como blackberry redirigidos a la página de inicio de sesión.

Esto también crea una cookie que agrega explícitamente los roles de los usuarios y crea un principio genérico para que se pueda usar la autorización basada en roles.

en WinLogin2.aspx (en el directorio de WinLogin en la aplicación web "raíz" en IIS, y configurado para usar Autenticación de Windows, Anónimo deshabilitado y Formularios habilitados (como no se puede apagar ... nota IIS se quejará cuando habilitar la autenticación de windows, simplemente lo ignoran):

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; 
      } 
     } 
    } 

En IIS 7.5, haga clic en las páginas de error, establecer la página 401 a la ruta del archivo archivo Redirect401.htm, con este código:

<!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> 

En AccountController. ..

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"); 

En global.asax:

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); 
     } 
    } 

web.config

<!--<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> 

crédito debe a http://msdn.microsoft.com/en-us/library/ms972958.aspx

+1

Hola: muestra el redireccionamiento del archivo Redirect401.htm mediante script agregado. ¿No puede lograr esto configurando el manejo de errores para 401.1 como un redireccionamiento? En IIS esto significaría cambiar a la opción 'responder con 302 redirección' para la página de error 401. Se dice que se usa una url absoluta, pero la raíz relativa también funciona. –

4

Esto probablemente va a vivir en el fondo de esta cuestión y nunca ser encontrado pero yo era capaz de poner en práctica lo que se describió en

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

Se fue bastante fácil y trivial. No requirió múltiples aplicaciones o hacks de cookies, simplemente extendió el FormsAuthModule y realizó algunos cambios en la configuración web.

+1

Creo que el escenario que mencionaste es diferente al escenario en cuestión. la autenticación de dos niveles requiere que un usuario sea autenticado tanto con Windows como con autenticación de formularios. Sin embargo, requerimos que, según el tipo de usuario (intranet/internet), se autentiquen mediante autenticación basada en Windows o en formularios. – Samra

0

Sé que esta es una publicación anterior, ¡pero todo vive para siempre en Internet!

De todos modos, tuve que mover un sitio web antiguo de IIS6 a IIS8. Este es un sitio web de WebForms, pero asumo que esta solución muy simple es la misma.

Recibí el error: No se puede convertir el objeto del tipo 'System.Security.Principal.WindowsIdentity' para escribir 'System.Web.Security.FormsIdentity'.

Todo lo que hice fue crear un nuevo grupo de aplicaciones para el sitio web. Al crear esto, configuré el modo de canal administrado como 'Clásico'. (Obtenga más información aquí: http://www.hanselman.com/blog/MovingOldAppsFromIIS6ToIIS8AndWhyClassicModeExists.aspx) No olvide configurar el grupo de aplicaciones del sitio web en el nuevo grupo que acaba de crear.

Cuestiones relacionadas