ASP.NET MVC y autenticación de modo mixto
asp.net-mvc mixed-mode (4)
Esto probablemente vivirá al final de esta pregunta y nunca se encontrará, pero pude implementar lo que se describió en
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.
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.
¿Puede alguien proporcionar una explicación detallada, con ejemplos de código, sobre cómo se haría esto?
Gracias.
Alan T
Esto puede hacerse. 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 debe 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 ... tenga en cuenta que IIS se quejará cuando habilite la autenticación de Windows, solo ignoralo) :
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 Páginas de error, establezca la página 401 en la ruta de archivo del 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 debido a http://msdn.microsoft.com/en-us/library/ms972958.aspx
Esto se llama modo de autenticación mixta . Básicamente no puede lograr esto en una sola aplicación 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.
Sé que esta es una publicación antigua, ¡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''. (Lea más aquí - http://www.hanselman.com/blog/MovingOldAppsFromIIS6ToIIS8AndWhyClassicModeExists.aspx ) No olvide configurar el grupo de aplicaciones del sitio web para el nuevo grupo que acaba de crear.