asp.net - formsauthentication - authentication mode forms web config
Autenticación de formularios: deshabilite la redirección a la página de inicio de sesión (10)
Tengo una aplicación que usa Autenticación de formularios ASP.NET. En su mayor parte, está funcionando muy bien, pero estoy tratando de agregar soporte para una API simple a través de un archivo .ashx. Quiero que el archivo ashx tenga autenticación opcional (es decir, si no proporciona un encabezado de Autenticación, entonces simplemente funciona de forma anónima). Pero, dependiendo de lo que haga, quiero requerir autenticación bajo ciertas condiciones.
Pensé que sería una simple cuestión de responder con el código de estado 401 si no se proporcionó la autenticación requerida, pero parece que el módulo de autenticación de formularios está interceptando eso y respondiendo con un redireccionamiento a la página de inicio de sesión. Lo que quiero decir es, si mi método ProcessRequest
tiene este aspecto:
public void ProcessRequest(HttpContext context)
{
Response.StatusCode = 401;
Response.StatusDescription = "Authentication required";
}
Entonces, en lugar de obtener un código de error 401 en el cliente, como espero, en realidad recibo un redireccionamiento 302 a la página de inicio de sesión.
Para el tráfico HTTP normal, puedo ver cómo sería útil, pero para mi página de API, quiero que el 401 pase sin modificaciones para que la persona que llama al lado del cliente pueda responder programáticamente en su lugar.
¿Hay alguna forma de hacer eso?
ASP.NET 4.5 agregó la propiedad booleana HttpResponse.SuppressFormsAuthenticationRedirect
.
public void ProcessRequest(HttpContext context)
{
Response.StatusCode = 401;
Response.StatusDescription = "Authentication required";
Response.SuppressFormsAuthenticationRedirect = true;
}
Busque dentro de su archivo Web.config en configuration/authentication
. Si hay un subelemento de forms
allí con un atributo loginUrl
, quítelo e intente de nuevo.
Después de un poco de investigación, parece que el FormsAuthenticationModule agrega un controlador para el evento HttpApplicationContext.EndRequest
. En su controlador, busca un código de estado 401 y básicamente hace un Response.Redirect(loginUrl)
lugar. Por lo que puedo decir, no hay forma de anular este comportamiento si quiere usar FormsAuthenticationModule
.
La forma en que terminé FormsAuthenticationModule
fue deshabilitando el FormsAuthenticationModule
en el web.config así:
<authentication mode="None" />
Y luego implementando Application_AuthenticateEvent
mi cuenta:
void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (Context.User == null)
{
var oldTicket = ExtractTicketFromCookie(Context, FormsAuthentication.FormsCookieName);
if (oldTicket != null && !oldTicket.Expired)
{
var ticket = oldTicket;
if (FormsAuthentication.SlidingExpiration)
{
ticket = FormsAuthentication.RenewTicketIfOld(oldTicket);
if (ticket == null)
return;
}
Context.User = new GenericPrincipal(new FormsIdentity(ticket), new string[0]);
if (ticket != oldTicket)
{
// update the cookie since we''ve refreshed the ticket
string cookieValue = FormsAuthentication.Encrypt(ticket);
var cookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName] ??
new HttpCookie(FormsAuthentication.FormsCookieName, cookieValue) { Path = ticket.CookiePath };
if (ticket.IsPersistent)
cookie.Expires = ticket.Expiration;
cookie.Value = cookieValue;
cookie.Secure = FormsAuthentication.RequireSSL;
cookie.HttpOnly = true;
if (FormsAuthentication.CookieDomain != null)
cookie.Domain = FormsAuthentication.CookieDomain;
Context.Response.Cookies.Remove(cookie.Name);
Context.Response.Cookies.Add(cookie);
}
}
}
}
private static FormsAuthenticationTicket ExtractTicketFromCookie(HttpContext context, string name)
{
FormsAuthenticationTicket ticket = null;
string encryptedTicket = null;
var cookie = context.Request.Cookies[name];
if (cookie != null)
{
encryptedTicket = cookie.Value;
}
if (!string.IsNullOrEmpty(encryptedTicket))
{
try
{
ticket = FormsAuthentication.Decrypt(encryptedTicket);
}
catch
{
context.Request.Cookies.Remove(name);
}
if (ticket != null && !ticket.Expired)
{
return ticket;
}
// if the ticket is expired then remove it
context.Request.Cookies.Remove(name);
return null;
}
}
En realidad es un poco más complicado que eso, pero básicamente obtuve el código al observar la implementación de FormsAuthenticationModule
en el reflector. Mi implementación es diferente al FormsAuthenticationModule
en que no hace nada si respondes con un 401, sin redirigir a la página de inicio de sesión. Supongo que si alguna vez se convierte en un requisito, puedo poner un elemento en el contexto para deshabilitar el redireccionamiento automático o algo así.
Ese es un problema conocido, y hay un Paquete NuGet para eso y / o el código fuente disponible.
No establece el encabezado WWW-Authenticate
en el código que muestra, por lo que el cliente no puede realizar la autenticación HTTP en lugar de la autenticación de formularios. Si este es el caso, debe usar 403 en lugar de 401, que no será interceptado por FormsAuthenticaitonModule
.
No estoy seguro de si esto funcionará para todos, pero en IIS7 puede llamar a Response.End () después de haber configurado el código de estado y la descripción. ¡De esta manera, ese # y $ ^ # @ *! FormsAuthenticationModule no hará una redirección.
public void ProcessRequest(HttpContext context) {
Response.StatusCode = 401;
Response.StatusDescription = "Authentication required";
Response.End();
}
No sé cómo funciona Response.End () para ti. Lo probé sin alegría, luego miré a MSDN para Response.End (): ''detiene la ejecución de la página y levanta el evento EndRequest''.
Por lo que vale mi truco fue:
_response.StatusCode = 401;
_context.Items["401Override"] = true;
_response.End();
A continuación, en Global.cs, agregue un controlador EndRequest (que se llamará después de Authentication HTTPModule):
protected void Application_EndRequest(object sender, EventArgs e)
{
if (HttpContext.Current.Items["401Override"] != null)
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.StatusCode = 401;
}
}
Para construir sobre la respuesta de Zacharydl un poco, usé esto para resolver mis problemas. En cada solicitud, al principio, si es AJAX, suprima inmediatamente el comportamiento.
protected void Application_BeginRequest()
{
HttpRequestBase request = new HttpRequestWrapper(Context.Request);
if (request.IsAjaxRequest())
{
Context.Response.SuppressFormsAuthenticationRedirect = true;
}
}
Sé que ya hay una respuesta con tic pero al tratar de resolver un problema similar encontré esto ( http://blog.inedo.com/2010/10/12/http-418-im-a-teapot-finally-a- % e2% 80% 9clegitimate% e2% 80% 9d-use / ) como alternativa.
Básicamente, devuelve su propio código de estado HTTP (por ejemplo, 418) en su código. En mi caso, un servicio de datos WCF.
throw new DataServiceException(418, "401 Unauthorized");
Luego use un módulo HTTP para manejarlo en el evento EndRequest
para volver a escribir el código en 401.
HttpApplication app = (HttpApplication)sender;
if (app.Context.Response.StatusCode == 418)
{
app.Context.Response.StatusCode = 401;
}
El navegador / cliente recibirá el contenido correcto y el código de estado, funciona muy bien para mí :)
Si está interesado en obtener más información sobre el código de estado HTTP 418, consulte esta pregunta y respuesta .
lo que has descubierto es correcto acerca de la autenticación de formularios interceptando el 401 y haciendo un redireccionamiento, pero también podemos hacer eso para revertir eso.
Básicamente, lo que necesita es un módulo http para interceptar el redireccionamiento 302 a la página de inicio de sesión y revertirlo a un 401.
Pasos para hacer eso se explican here
El enlace dado se trata de un servicio WCF pero es el mismo en todos los escenarios auth de formularios.
Como se explica en el enlace anterior, también debe borrar los encabezados http, pero recuerde volver a poner el encabezado de la cookie en la respuesta si la respuesta original (es decir, antes de la interceptación) contiene cookies.