Cómo administrar una solicitud de redireccionamiento después de una llamada jQuery Ajax
Frecuentes
Visto 957,921 veces
1425
Estoy usando $.post()
para llamar a un servlet usando Ajax y luego usando el fragmento HTML resultante para reemplazar un div
elemento en la página actual del usuario. Sin embargo, si la sesión se agota, el servidor envía una directiva de redireccionamiento para enviar al usuario a la página de inicio de sesión. En este caso, jQuery está reemplazando el div
elemento con el contenido de la página de inicio de sesión, lo que obliga a los ojos del usuario a presenciar una escena rara.
¿Cómo puedo administrar una directiva de redireccionamiento desde una llamada Ajax con jQuery 1.2.6?
30 Respuestas
715
Leí esta pregunta e implementé el enfoque que se ha establecido con respecto a la configuración de la respuesta Código de estado HTTP a 278 para evitar que el navegador maneje los redireccionamientos de forma transparente. Aunque esto funcionó, estaba un poco insatisfecho, ya que es un truco.
Después de investigar más, abandoné este enfoque y utilicé JSON. En este caso, todas las respuestas a las solicitudes AJAX tienen la código de estado 200 y el cuerpo de la respuesta contiene un objeto JSON que se construye en el servidor. El JavaScript del cliente puede usar el objeto JSON para decidir qué debe hacer.
Tuve un problema similar al tuyo. Realizo una solicitud AJAX que tiene 2 posibles respuestas: una que redirecciones el navegador a una nueva página y una que reemplaza un formulario HTML existente en la página actual con uno nuevo. El código jQuery para hacer esto se parece a lo siguiente:
$.ajax({
type: "POST",
url: reqUrl,
data: reqBody,
dataType: "json",
success: function(data, textStatus) {
if (data.redirect) {
// data.redirect contains the string URL to redirect to
window.location.href = data.redirect;
} else {
// data.form contains the HTML for the replacement form
$("#myform").replaceWith(data.form);
}
}
});
Los "datos" del objeto JSON se construyen en el servidor para tener 2 miembros: data.redirect
y data.form
. Encontré que este enfoque es mucho mejor.
Respondido 29 Oct 19, 12:10
Como se indica en la solución en stackoverflow.com/questions/503093/… es mejor usar window.location.replace (data.redirect); que window.location.href = data.redirect; - Carles Barrobes
Cualquier motivo por el que no sería mejor utilizar códigos HTTP en la acción. Por ejemplo, ¿un código 307 que es redireccionamiento temporal HTTP? - Serguéi Golos
@Sergei Golos la razón es que si realiza una redirección HTTP, la redirección en realidad nunca llega a la devolución de llamada exitosa de ajax. El navegador procesa la redirección entregando un código 200 con el contenido del destino de la redirección. - miguel silva
¿Cómo se vería el código del servidor? para tener un valor de retorno de data.form y data.redirect. Básicamente, ¿cómo puedo determinar si le pongo una redirección? - Vnge
Esta respuesta sería más útil si hubiera mostrado cómo se hace esto en el servidor. - Pedro Hoehl Carvalho
252
Resolví este problema por:
Agregar un encabezado personalizado a la respuesta:
public ActionResult Index(){ if (!HttpContext.User.Identity.IsAuthenticated) { HttpContext.Response.AddHeader("REQUIRES_AUTH","1"); } return View(); }
Vincular una función de JavaScript al
ajaxSuccess
evento y verificando si el encabezado existe:$(document).ajaxSuccess(function(event, request, settings) { if (request.getResponseHeader('REQUIRES_AUTH') === '1') { window.location = '/'; } });
respondido 08 nov., 17:08
Qué solución tan asombrosa. Me gusta la idea de una solución integral. Necesito verificar el estado 403, pero puedo usar el enlace ajaxSuccess en el cuerpo para esto (que es lo que realmente estaba buscando). Gracias. - Bretticus
Acabo de hacer esto y descubrí que necesitaba ajaxComplete donde estaba usando la función $ .get () y cualquier estado que no fuera 200 no se activaba. De hecho, probablemente podría haberme enlazado a ajaxError en su lugar. Vea mi respuesta a continuación para obtener más detalles. - Bretticus
Está bien en muchos casos, pero ¿qué pasa si su marco maneja la autorización? - jwaliszko
Me gusta el enfoque del encabezado, pero también creo, como @ mwoods79, que el conocimiento de a dónde redireccionar no debe duplicarse. Lo resolví agregando un encabezado REDIRECT_LOCATION en lugar de un booleano. - rincio
Tenga cuidado de establecer el encabezado en la respuesta DESPUÉS de la redirección. Como se describe en otras respuestas en esta página, la redirección puede ser transparente para el controlador ajaxSucces. Por lo tanto, incluí el encabezado en la respuesta GET de la página de inicio de sesión (que finalmente y solo desencadenó ajaxSuccess en mi escenario). - Sieppl
120
Ningún navegador maneja correctamente las respuestas 301 y 302. Y, de hecho, el estándar incluso dice que deberían manejarlos de manera "transparente", lo que es un dolor de cabeza MASIVO para los proveedores de bibliotecas Ajax. En Ra-Ajax nos vimos obligados a usar el código de estado de respuesta HTTP 278 (solo un código de éxito "no utilizado") para manejar las redirecciones de forma transparente desde el servidor ...
Esto realmente me molesta, y si alguien aquí tiene algún "tirón" en el W3C, agradecería que pudieras dejar que el W3C sabes qué que realmente necesitamos manejar los códigos 301 y 302 nosotros mismos ...! ;)
Respondido 19 ago 15, 09:08
Por un lado, creo que 278 debería convertirse en parte de la especificación HTTP oficial. - Chris Marisic
¿No los maneja ya de forma transparente? Si un recurso se ha movido, manejarlo de manera transparente significa repetir la solicitud en la URL proporcionada. Eso es lo que esperaría usar la API XMLHttpRequest. - Philippe Rathé
@ PhilippeRathé De acuerdo. El manejo transparente es justo lo que quiero. Y no sé por qué se considera malo. - smwikipedia
@smwikipedia Para organizar una redirección del marcado en la sección principal sin redirigir la página. - cmc
Si alguien aquí tiene algún "tirón" en el W3C, le agradecería que pudiera informarle al W3C que realmente necesitamos manejar los códigos 301 y 302 nosotros mismos ...! ;) - Tommy Tang
101
La solución que finalmente se implementó fue utilizar un contenedor para la función de devolución de llamada de la llamada Ajax y, en este contenedor, verificar la existencia de un elemento específico en el fragmento HTML devuelto. Si se encontró el elemento, el contenedor ejecutó una redirección. De lo contrario, el contenedor reenvió la llamada a la función de devolución de llamada real.
Por ejemplo, nuestra función contenedora era algo como:
function cbWrapper(data, funct){
if($("#myForm", data).length > 0)
top.location.href="login.htm";//redirection
else
funct(data);
}
Luego, al hacer la llamada Ajax usamos algo como:
$.post("myAjaxHandler",
{
param1: foo,
param2: bar
},
function(data){
cbWrapper(data, myActualCB);
},
"html"
);
Esto funcionó para nosotros porque todas las llamadas Ajax siempre devolvían HTML dentro de un elemento DIV que usamos para reemplazar una parte de la página. Además, solo necesitábamos redirigir a la página de inicio de sesión.
Respondido 31 ago 16, 07:08
Tenga en cuenta que esto se puede acortar a function cbWrapper (funct) {return function (data) {if ($ ("# myForm", data) .size ()> 0) top.location.href = "login"; else funct (datos); }}. Entonces solo necesita cbWrapper (myActualCB) al llamar a .post. Sí, el código en los comentarios es un desastre, pero debe tenerse en cuenta :) - simen echholt
el tamaño se deprecia, por lo que puede usar .length aquí en lugar del tamaño - Sunil
69
Me gusta el método de Timmerz con un toque de limón. Si alguna vez te devuelven tipo de contenido of text / html cuando estas esperando JSON, lo más probable es que se le redirija. En mi caso, simplemente recargo la página y se redirige a la página de inicio de sesión. Ah, y verifique que el estado de jqXHR sea 200, lo que parece una tontería, porque está en la función de error, ¿verdad? De lo contrario, los casos de error legítimos forzarán una recarga iterativa (oops)
$.ajax(
error: function (jqXHR, timeout, message) {
var contentType = jqXHR.getResponseHeader("Content-Type");
if (jqXHR.status === 200 && contentType.toLowerCase().indexOf("text/html") >= 0) {
// assume that our login has expired - reload our current page
window.location.reload();
}
});
Respondido el 14 de Septiembre de 11 a las 18:09
muchas gracias Brian, tu respuesta fue la mejor para mi escenario, aunque me gustaría que hubiera una verificación más segura, como comparar a qué URL / página se está redireccionando en lugar de la simple verificación de "tipo de contenido". No pude encontrar a qué página se está redirigiendo desde el objeto jqXHR. - Johnny
Verifiqué el estado 401 y luego lo redireccioné. Funciona como un campeón. - Eric
57
Utilice el nivel bajo $.ajax()
llamada:
$.ajax({
url: "/yourservlet",
data: { },
complete: function(xmlHttp) {
// xmlHttp is a XMLHttpRquest object
alert(xmlHttp.status);
}
});
Pruebe esto para una redirección:
if (xmlHttp.code != 200) {
top.location.href = '/some/other/page';
}
Respondido 17 Oct 08, 01:10
Estaba tratando de evitar las cosas de bajo nivel. De todos modos, suponga que uso algo como lo que describe, ¿cómo fuerzo una redirección del navegador una vez que detecto que el código HTTP es 3xx? Mi objetivo es redirigir al usuario, no solo anunciar que su sesión ha expirado. - eliot vargas
Por cierto, $ .ajax () no es de muy, muy bajo nivel. Es de bajo nivel en términos de jQuery porque hay $ .get, $ .post, etc. que son mucho más simples que $ .ajax y todas sus opciones. - a
¡Oh chico! Lo siento, tuve que "anular la aceptación" de tu respuesta, sigue siendo muy útil. La cuestión es que las redirecciones son administradas automáticamente por XMLHttpRequest, por lo tanto, SIEMPRE obtengo un código de estado 200 después de la redirección (¡suspiro!). Creo que tendré que hacer algo desagradable como analizar el HTML y buscar un marcador. - eliot vargas
Solo por curiosidad, si la sesión termina en el servidor, ¿no significa eso que el servidor envía un SESSIONID diferente? ¿No podríamos simplemente detectar eso? - Salamander2007
NOTA: Esto no funciona para redireccionamientos. ajax irá a la nueva página y devolverá su código de estado. - usuario34537
33
Solo quería compartir mi enfoque, ya que esto podría ayudar a alguien:
Básicamente incluí un módulo de JavaScript que maneja las cosas de autenticación como mostrar el nombre de usuario y también este caso maneja el redirigir a la página de inicio de sesión.
Mi escenario: Básicamente tenemos un servidor ISA entre el cual escucha todas las solicitudes y responde con un 302 y un encabezado de ubicación a nuestra página de inicio de sesión.
En mi módulo de JavaScript mi acercamiento inicial fue algo como
$(document).ajaxComplete(function(e, xhr, settings){
if(xhr.status === 302){
//check for location header and redirect...
}
});
El problema (como muchos ya se han mencionado aquí) es que el navegador maneja la redirección por sí mismo, por lo que mi ajaxComplete
la devolución de llamada nunca fue llamada, sino que obtuve el respuesta de la página de inicio de sesión ya redirigida que obviamente fue un status 200
. El problema: ¿cómo detecta si la respuesta 200 exitosa es su página de inicio de sesión real o simplemente alguna otra página arbitraria?
la solución
Como no pude capturar las respuestas de redireccionamiento 302, agregué un LoginPage
encabezado en mi página de inicio de sesión que contenía la URL de la página de inicio de sesión en sí. En el módulo, ahora escucho el encabezado y hago una redirección:
if(xhr.status === 200){
var loginPageRedirectHeader = xhr.getResponseHeader("LoginPage");
if(loginPageRedirectHeader && loginPageRedirectHeader !== ""){
window.location.replace(loginPageRedirectHeader);
}
}
... y eso funciona a las mil maravillas :). Quizás se pregunte por qué incluyo la URL en el LoginPage
encabezado ... bueno, básicamente porque no encontré forma de determinar la URL de GET
como resultado de la redirección automática de ubicación desde el xhr
objeto...
contestado el 23 de mayo de 12 a las 11:05
+1, pero se supone que los encabezados personalizados comienzan con X-
, por lo que sería mejor utilizar un encabezado X-LoginPage: http://example.com/login
. - uınbɐɥs
@ShaquinTrifonoff Ya no. No usé el prefijo X- porque en Junio 2011 un documento de la ITEF propuso su desaprobación y, de hecho, con Junio 2012 No es oficial que los encabezados personalizados ya no deben tener el prefijo X-
. - Juri
También tenemos un servidor ISA y me encontré con el mismo problema. En lugar de solucionarlo en el código, usamos las instrucciones en kb2596444 para configurar ISA para que deje de redireccionar. - piedra de scott
29
Sé que este tema es antiguo, pero daré otro enfoque que encontré y describí anteriormente. aquí. Básicamente estoy usando ASP.MVC con WIF (pero esto no es realmente importante para el contexto de este tema; la respuesta es adecuada sin importar qué marcos se utilicen. La pista permanece sin cambios: se trata de problemas relacionados con fallas de autenticación mientras se realizan solicitudes ajax).
El enfoque que se muestra a continuación se puede aplicar a todas las solicitudes ajax listas para usar (si no redefinen el evento beforeSend, obviamente).
$.ajaxSetup({
beforeSend: checkPulse,
error: function (XMLHttpRequest, textStatus, errorThrown) {
document.open();
document.write(XMLHttpRequest.responseText);
document.close();
}
});
Antes de que se realice cualquier solicitud ajax CheckPulse
se invoca el método (el método del controlador que puede ser cualquier cosa más simple):
[Authorize]
public virtual void CheckPulse() {}
Si el usuario no está autenticado (el token ha caducado) no se puede acceder a dicho método (protegido por Authorize
atributo). Debido a que el marco maneja la autenticación, mientras que el token expira, coloca el estado http 302 en la respuesta. Si no desea que su navegador maneje la respuesta 302 de forma transparente, descárguela en Global.asax y cambie el estado de la respuesta, por ejemplo a 200 OK. Además, agregue el encabezado, que le indica que procese dicha respuesta de manera especial (más adelante en el lado del cliente):
protected void Application_EndRequest()
{
if (Context.Response.StatusCode == 302
&& (new HttpContextWrapper(Context)).Request.IsAjaxRequest())
{
Context.Response.StatusCode = 200;
Context.Response.AddHeader("REQUIRES_AUTH", "1");
}
}
Finalmente, en el lado del cliente, verifique dicho encabezado personalizado. Si está presente, se debe realizar una redirección completa a la página de inicio de sesión (en mi caso window.location
se reemplaza por la URL de la solicitud que es manejada automáticamente por mi marco).
function checkPulse(XMLHttpRequest) {
var location = window.location.href;
$.ajax({
url: "/Controller/CheckPulse",
type: 'GET',
async: false,
beforeSend: null,
success:
function (result, textStatus, xhr) {
if (xhr.getResponseHeader('REQUIRES_AUTH') === '1') {
XMLHttpRequest.abort(); // terminate further ajax execution
window.location = location;
}
}
});
}
contestado el 23 de mayo de 17 a las 12:05
Solucioné el problema utilizando el evento PostAuthenticateRequest en lugar del evento EndRequest. - Frinavale
@JaroslawWaliszko ¡Pegué el evento equivocado en mi última respuesta! Me refiero al evento PreSendRequestHeaders ... ¡no PostAuthenticateRequest! >> rubor << gracias por señalar mi error. - Frinavale
@JaroslawWaliszko cuando usa WIF también puede devolver respuestas 401 para solicitudes AJAX y dejar que su javascript las maneje. También está asumiendo que todos los 302 requieren autenticación, lo que podría no ser cierto en todos los casos. Agregué una respuesta si alguien está interesado. - Robar
27
Creo que una mejor manera de manejar esto es aprovechar los códigos de respuesta del protocolo HTTP existentes, específicamente 401 Unauthorized
.
Así es como lo resolví:
- Lado del servidor: si la sesión expira y la solicitud es ajax. enviar un encabezado de código de respuesta 401
Lado del cliente: enlace a los eventos ajax
$('body').bind('ajaxSuccess',function(event,request,settings){ if (401 == request.status){ window.location = '/users/login'; } }).bind('ajaxError',function(event,request,settings){ if (401 == request.status){ window.location = '/users/login'; } });
En mi opinión, esto es más genérico y no está escribiendo una nueva especificación / encabezado personalizado. Tampoco debería tener que modificar ninguna de sus llamadas ajax existentes.
Edit: Según el comentario de @ Rob a continuación, 401 (el código de estado HTTP para errores de autenticación) debería ser el indicador. Ver Respuestas HTTP 403 prohibidas vs 401 no autorizadas para más detalles. Dicho esto, algunos frameworks web usan 403 tanto para autenticación como para errores de autorización, así que adáptese en consecuencia. Gracias Rob.
contestado el 23 de mayo de 17 a las 12:05
Utilizo el mismo enfoque. ¿JQuery realmente llama a ajaxSuccess en el código de error 403? Creo que solo se necesita la parte ajaxError - Marius Balčytis
trabajando para mi .. Gracias - Puntero nulo
25
Resolví este problema así:
Agregue un middleware para procesar la respuesta, si es un redireccionamiento para una solicitud ajax, cambie la respuesta a una respuesta normal con la URL de redireccionamiento.
class AjaxRedirect(object):
def process_response(self, request, response):
if request.is_ajax():
if type(response) == HttpResponseRedirect:
r = HttpResponse(json.dumps({'redirect': response['Location']}))
return r
return response
Luego, en ajaxComplete, si la respuesta contiene redirección, debe ser una redirección, así que cambie la ubicación del navegador.
$('body').ajaxComplete(function (e, xhr, settings) {
if (xhr.status == 200) {
var redirect = null;
try {
redirect = $.parseJSON(xhr.responseText).redirect;
if (redirect) {
window.location.href = redirect.replace(/\?.*$/, "?next=" + window.location.pathname);
}
} catch (e) {
return;
}
}
}
contestado el 13 de mayo de 18 a las 13:05
21
Otra solución que encontré (especialmente útil si desea establecer un comportamiento global) es usar el $.ajaxsetup()
Método junto con la statusCode
perfecta. Como han señalado otros, no use un código de estado de redireccionamiento (3xx
), en su lugar utilice un 4xx
statuscode y manejar el redireccionamiento del lado del cliente.
$.ajaxSetup({
statusCode : {
400 : function () {
window.location = "/";
}
}
});
Reemplaza 400
con el código de estado que desea manejar. Como ya se mencionó 401 Unauthorized
podría ser una buena idea. Yo uso la 400
ya que es muy inespecífico y puedo usar el 401
para casos más específicos (como credenciales de inicio de sesión incorrectas). Entonces, en lugar de redirigir directamente, su backend debería devolver un 4xx
error-code cuando se agotó el tiempo de espera de la sesión y usted maneja la redirección del lado del cliente. Funciona perfecto para mí incluso con marcos como backbone.js
contestado el 31 de mayo de 16 a las 13:05
20
Tengo una solución simple que funciona para mí, no es necesario cambiar el código del servidor ... solo agregue una cucharadita de nuez moscada ...
$(document).ready(function ()
{
$(document).ajaxSend(
function(event,request,settings)
{
var intercepted_success = settings.success;
settings.success = function( a, b, c )
{
if( request.responseText.indexOf( "<html>" ) > -1 )
window.location = window.location;
else
intercepted_success( a, b, c );
};
});
});
Verifico la presencia de la etiqueta html, pero puede cambiar indexOf para buscar cualquier cadena única que exista en su página de inicio de sesión ...
Respondido 17 ago 10, 19:08
Esto no parece funcionar para mí, sigue llamando a la función definida con la llamada ajax, es como si no anulara el método de éxito. - adriaanp
No parece funcionar para mí, al menos ya. Posible explicación aquí: stackoverflow.com/a/12010724/260665 - Raj Pawan Gumdal
19
La mayoría de las soluciones proporcionadas utilizan una solución alternativa, utilizando un encabezado adicional o un código HTTP inadecuado. Esas soluciones probablemente funcionarán, pero se sentirán un poco "hack". Se me ocurrió otra solución.
Estamos usando WIF que está configurado para redirigir (passiveRedirectEnabled = "true") en una respuesta 401. La redirección es útil cuando se manejan solicitudes normales, pero no funcionará para solicitudes AJAX (ya que los navegadores no ejecutarán la redirección 302).
Usando el siguiente código en su global.asax, puede deshabilitar la redirección para solicitudes AJAX:
void WSFederationAuthenticationModule_AuthorizationFailed(object sender, AuthorizationFailedEventArgs e)
{
string requestedWithHeader = HttpContext.Current.Request.Headers["X-Requested-With"];
if (!string.IsNullOrEmpty(requestedWithHeader) && requestedWithHeader.Equals("XMLHttpRequest", StringComparison.OrdinalIgnoreCase))
{
e.RedirectToIdentityProvider = false;
}
}
Esto le permite devolver respuestas 401 para solicitudes AJAX, que su javascript puede manejar al volver a cargar la página. Recargar la página arrojará un 401 que será manejado por WIF (y WIF redirigirá al usuario a la página de inicio de sesión).
Un javascript de ejemplo para manejar errores 401:
$(document).ajaxError(function (event, jqxhr, settings, exception) {
if (jqxhr.status == 401) { //Forbidden, go to login
//Use a reload, WIF will redirect to Login
location.reload(true);
}
});
Respondido 01 Abr '14, 13:04
Buena solución. Gracias. - Dan Man
19
Este problema puede aparecer luego usando el método ASP.NET MVC RedirectToAction. Para evitar que el formulario muestre la respuesta en div, simplemente puede hacer algún tipo de filtro de respuesta ajax para las respuestas entrantes con $ .ajaxSetup. Si la respuesta contiene la redirección MVC, puede evaluar esta expresión en el lado de JS. Código de ejemplo para JS a continuación:
$.ajaxSetup({
dataFilter: function (data, type) {
if (data && typeof data == "string") {
if (data.indexOf('window.location') > -1) {
eval(data);
}
}
return data;
}
});
Si los datos son: "window.location = '/ Cuenta / Iniciar sesión'" El filtro anterior lo detectará y evaluará para realizar la redirección en lugar de permitir que se muestren los datos.
Respondido 27 Oct 16, 14:10
data
está en el cuerpo de la respuesta o en el encabezado? - sam yc
17
Juntando lo que dijeron Vladimir Prudnikov y Thomas Hansen:
- Cambie su código del lado del servidor para detectar si es un XHR. Si es así, establezca el código de respuesta de la redirección en 278. En django:
if request.is_ajax(): response.status_code = 278
Esto hace que el navegador trate la respuesta como un éxito y se la entregue a su Javascript.
- En su JS, asegúrese de que el envío del formulario sea a través de Ajax, verifique el código de respuesta y redirija si es necesario:
$('#my-form').submit(function(event){ event.preventDefault(); var options = { url: $(this).attr('action'), type: 'POST', complete: function(response, textStatus) { if (response.status == 278) { window.location = response.getResponseHeader('Location') } else { ... your code here ... } }, data: $(this).serialize(), }; $.ajax(options); });
Respondido 06 ago 09, 21:08
13
<script>
function showValues() {
var str = $("form").serialize();
$.post('loginUser.html',
str,
function(responseText, responseStatus, responseXML){
if(responseStatus=="success"){
window.location= "adminIndex.html";
}
});
}
</script>
Respondido el 10 de Septiembre de 11 a las 17:09
12
Intente
$(document).ready(function () {
if ($("#site").length > 0) {
window.location = "<%= Url.Content("~") %>" + "Login/LogOn";
}
});
Ponlo en la página de inicio de sesión. Si se cargó en un div en la página principal, se redirigirá a la página de inicio de sesión. "#site" es una identificación de un div que se encuentra en todas las páginas excepto en la página de inicio de sesión.
Respondido 29 Oct 10, 09:10
12
Si bien las respuestas parecen funcionar para las personas si está utilizando Spring Security, he descubierto que extendía LoginUrlAuthenticationEntryPoint y agregaba un código específico para manejar AJAX más robusto. La mayoría de los ejemplos interceptan todos redirige no solo las fallas de autenticación. Esto era indeseable para el proyecto en el que trabajo. Es posible que también necesite extender ExceptionTranslationFilter y anular el método "sendStartAuthentication" para eliminar el paso de almacenamiento en caché si no desea que la solicitud AJAX fallida se almacene en caché.
Ejemplo AjaxAwareAuthenticationEntryPoint:
public class AjaxAwareAuthenticationEntryPoint extends
LoginUrlAuthenticationEntryPoint {
public AjaxAwareAuthenticationEntryPoint(String loginUrl) {
super(loginUrl);
}
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
if (isAjax(request)) {
response.sendError(HttpStatus.UNAUTHORIZED.value(), "Please re-authenticate yourself");
} else {
super.commence(request, response, authException);
}
}
public static boolean isAjax(HttpServletRequest request) {
return request != null && "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
}
}
contestado el 13 de mayo de 18 a las 14:05
Sería útil (para mí) si los votantes en contra me explicaran por qué están votando en contra. Si hay algo malo con esta solución, me gustaría aprender de mis errores. Gracias. - Juan
Si usa Spring y también JSF, entonces también verifique esto: ("parcial / ajax"). EqualsIgnoreCase (request.getHeader ("faces-request")); - J pulido
Los usuarios pueden haber votado en contra porque no mencionaste: (1) las modificaciones del lado del cliente necesarias para detectar tu respuesta de error; (2) las modificaciones necesarias para la configuración de Spring para agregar su LoginUrlAuthenticationEntryPoint personalizado a la cadena de filtros. - J pulido
Tu respuesta es similar a esta de @Arpad. Funcionó para mí; utilizando Spring Security 3.2.9. stackoverflow.com/a/8426947/4505142 - darren parker
11
Resolví esto poniendo lo siguiente en mi página login.php.
<script type="text/javascript">
if (top.location.href.indexOf('login.php') == -1) {
top.location.href = '/login.php';
}
</script>
Respondido el 05 de diciembre de 11 a las 19:12
10
Permítanme citar nuevamente el problema como lo describe @Steg
Tuve un problema similar al tuyo. Realizo una solicitud ajax que tiene 2 posibles respuestas: una que redirige el navegador a una nueva página y otra que reemplaza un formulario HTML existente en la página actual por uno nuevo.
En mi humilde opinión, este es un verdadero desafío y tendrá que extenderse oficialmente a los estándares HTTP actuales.
Creo que el nuevo estándar Http será utilizar un nuevo código de estado. significado: actualmente 301/302
le dice al navegador que vaya a buscar el contenido de este solicitud a un nuevo location
.
En el estándar extendido, dirá que si la respuesta status: 308
(solo un ejemplo), el navegador debe redirigir la página principal a la location
previsto.
Habiendo dicho eso; Me inclino a imitar esto Futuro comportamiento y, por lo tanto, cuando se necesita un document.redirect, hago que el servidor responda como:
status: 204 No Content
x-status: 308 Document Redirect
x-location: /login.html
Cuando JS obtiene el "status: 204
", comprueba la existencia del x-status: 308
encabezado, y hace un document.redirect a la página proporcionada en el location
cabecera.
¿Esto tiene algún sentido para tí?
Respondido 12 Jul 18, 22:07
7
Algunos pueden encontrar útil lo siguiente:
Quería que los clientes fueran redirigidos a la página de inicio de sesión para cualquier acción de descanso que se envíe sin un token de autorización. Dado que todas mis acciones de descanso están basadas en Ajax, necesitaba una buena forma genérica de redirigir a la página de inicio de sesión en lugar de manejar la función de éxito de Ajax.
Esto es lo que hice:
En cualquier solicitud de Ajax, mi servidor devolverá una respuesta Json 200 "NECESITA AUTENTIFICARSE" (si el cliente necesita autenticarse).
Ejemplo simple en Java (lado del servidor):
@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
private final Logger m_logger = LoggerFactory.getLogger(AuthenticationFilter.class);
public static final String COOKIE_NAME = "token_cookie";
@Override
public void filter(ContainerRequestContext context) throws IOException {
// Check if it has a cookie.
try {
Map<String, Cookie> cookies = context.getCookies();
if (!cookies.containsKey(COOKIE_NAME)) {
m_logger.debug("No cookie set - redirect to login page");
throw new AuthenticationException();
}
}
catch (AuthenticationException e) {
context.abortWith(Response.ok("\"NEED TO AUTHENTICATE\"").type("json/application").build());
}
}
}
En mi Javascript agregué el siguiente código:
$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
var originalSuccess = options.success;
options.success = function(data) {
if (data == "NEED TO AUTHENTICATE") {
window.location.replace("/login.html");
}
else {
originalSuccess(data);
}
};
});
Y eso es todo.
Respondido 09 Abr '16, 00:04
5
en el servlet debes poner
response.setStatus(response.SC_MOVED_PERMANENTLY);
para enviar el estado xmlHttp '301' que necesita para una redirección ...
y en la función $ .ajax no debes usar la .toString()
función ..., solo
if (xmlHttp.status == 301) {
top.location.href = 'xxxx.jsp';
}
el problema es que no es muy flexible, no puedes decidir a dónde quieres redireccionar.
Redirigir a través de los servlets debería ser la mejor manera. pero todavía no puedo encontrar la manera correcta de hacerlo.
respondido 14 nov., 08:20
5
Solo quería aferrarme a cualquier solicitud ajax para toda la página. @SuperG me ayudó a empezar. Esto es lo que terminé con:
// redirect ajax requests that are redirected, not found (404), or forbidden (403.)
$('body').bind('ajaxComplete', function(event,request,settings){
switch(request.status) {
case 301: case 404: case 403:
window.location.replace("http://mysite.tld/login");
break;
}
});
Quería verificar específicamente ciertos códigos de estado http para basar mi decisión. Sin embargo, puede enlazar a ajaxError para obtener cualquier otra cosa que no sea el éxito (¿solo 200 quizás?). Podría haber escrito:
$('body').bind('ajaxError', function(event,request,settings){
window.location.replace("http://mysite.tld/login");
}
Respondido 04 ago 10, 19:08
este último ocultaría cualquier otro error que dificulte la resolución de problemas - tim abel
Un 403 no significa que el usuario no esté autenticado, significa que el usuario (probablemente autenticado) no tiene permiso para ver el recurso solicitado. Por lo tanto, no debería redirigir a la página de inicio de sesión. Robar
5
Si también desea pasar los valores, también puede configurar las variables de sesión y acceder, por ejemplo: En su jsp puede escribir
<% HttpSession ses = request.getSession(true);
String temp=request.getAttribute("what_you_defined"); %>
Y luego puede almacenar este valor de temperatura en su variable de JavaScript y jugar
Respondido el 07 de enero de 13 a las 06:01
5
No tuve ningún éxito con la solución de encabezado; nunca se recogieron en mi método ajaxSuccess / ajaxComplete. Usé la respuesta de Steg con la respuesta personalizada, pero modifiqué un poco el lado JS. Configuré un método que llamo en cada función para poder usar estándar $.get
y $.post
métodos.
function handleAjaxResponse(data, callback) {
//Try to convert and parse object
try {
if (jQuery.type(data) === "string") {
data = jQuery.parseJSON(data);
}
if (data.error) {
if (data.error == 'login') {
window.location.reload();
return;
}
else if (data.error.length > 0) {
alert(data.error);
return;
}
}
}
catch(ex) { }
if (callback) {
callback(data);
}
}
Ejemplo de ello en uso ...
function submitAjaxForm(form, url, action) {
//Lock form
form.find('.ajax-submit').hide();
form.find('.loader').show();
$.post(url, form.serialize(), function (d) {
//Unlock form
form.find('.ajax-submit').show();
form.find('.loader').hide();
handleAjaxResponse(d, function (data) {
// ... more code for if auth passes ...
});
});
return false;
}
Respondido 06 Feb 13, 13:02
5
Finalmente, resuelvo el problema agregando un personalizado HTTP Header
. Justo antes de la respuesta para cada solicitud en el lado del servidor, agrego la URL solicitada actual al encabezado de la respuesta.
Mi tipo de aplicación en el servidor es Asp.Net MVC
y tiene un buen lugar para hacerlo. en Global.asax
Implementé el Application_EndRequest
evento así:
public class MvcApplication : System.Web.HttpApplication
{
// ...
// ...
protected void Application_EndRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
app.Context.Response.Headers.Add("CurrentUrl",app.Context. Request.CurrentExecutionFilePath);
}
}
¡Funciona perfecto para mí! Ahora, en cada respuesta del JQuery
$.post
tengo lo solicitado url
y también otros encabezados de respuesta que vienen como resultado de POST
método por estado 302
, 303
, ...
y otra cosa importante es que no es necesario modificar el código del lado del servidor ni del lado del cliente.
y el siguiente es la posibilidad de acceder a la otra información de la acción posterior, tales como errores, mensajes y ..., de esta manera.
Publiqué esto, tal vez ayude a alguien :)
Respondido 30 Jul 15, 07:07
4
Estaba teniendo este problema en una aplicación de django con la que estoy jugando (descargo de responsabilidad: estoy jugando para aprender y de ninguna manera soy un experto). Lo que quería hacer era usar jQuery ajax para enviar una solicitud DELETE a un recurso, eliminarlo en el lado del servidor y luego enviar una redirección a (básicamente) la página de inicio. Cuando envié HttpResponseRedirect('/the-redirect/')
desde el script de python, el método ajax de jQuery estaba recibiendo 200 en lugar de 302. Entonces, lo que hice fue enviar una respuesta de 300 con:
response = HttpResponse(status='300')
response['Location'] = '/the-redirect/'
return response
Luego envié / manejé la solicitud en el cliente con jQuery.ajax así:
<button onclick="*the-jquery*">Delete</button>
where *the-jquery* =
$.ajax({
type: 'DELETE',
url: '/resource-url/',
complete: function(jqxhr){
window.location = jqxhr.getResponseHeader('Location');
}
});
Quizás usar 300 no sea "correcto", pero al menos funcionó como yo quería.
PD: fue un gran dolor editarlo en la versión móvil de SO. ¡El estúpido ISP pasó mi solicitud de cancelación de servicio justo cuando terminé con mi respuesta!
Respondido 28 ago 11, 11:08
4
También puede enlazar XMLHttpRequest send prototype. Esto funcionará para todos los envíos (jQuery / dojo / etc) con un controlador.
Escribí este código para manejar un error de vencimiento de 500 páginas, pero debería funcionar igual de bien para atrapar un redireccionamiento 200. Prepara la entrada de wikipedia en XMLHttpRequest onreadystatechange sobre el significado de readyState.
// Hook XMLHttpRequest
var oldXMLHttpRequestSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
//console.dir( this );
this.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 500 && this.responseText.indexOf("Expired") != -1) {
try {
document.documentElement.innerHTML = this.responseText;
} catch(error) {
// IE makes document.documentElement read only
document.body.innerHTML = this.responseText;
}
}
};
oldXMLHttpRequestSend.apply(this, arguments);
}
Respondido 10 Abr '12, 20:04
2
Obtuve una solución de trabajo usando las respuestas de @John y @Arpad aquí y @RobWinch aquí
Yo uso Spring Security 3.2.9 y jQuery 1.10.2.
Extienda la clase de Spring para causar una respuesta 4XX solo de las solicitudes AJAX:
public class CustomLoginUrlAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
public CustomLoginUrlAuthenticationEntryPoint(final String loginFormUrl) {
super(loginFormUrl);
}
// For AJAX requests for user that isn't logged in, need to return 403 status.
// For normal requests, Spring does a (302) redirect to login.jsp which the browser handles normally.
@Override
public void commence(final HttpServletRequest request,
final HttpServletResponse response,
final AuthenticationException authException)
throws IOException, ServletException {
if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
} else {
super.commence(request, response, authException);
}
}
}
applicationContext-seguridad.xml
<security:http auto-config="false" use-expressions="true" entry-point-ref="customAuthEntryPoint" >
<security:form-login login-page='/login.jsp' default-target-url='/index.jsp'
authentication-failure-url="/login.jsp?error=true"
/>
<security:access-denied-handler error-page="/errorPage.jsp"/>
<security:logout logout-success-url="/login.jsp?logout" />
...
<bean id="customAuthEntryPoint" class="com.myapp.utils.CustomLoginUrlAuthenticationEntryPoint" scope="singleton">
<constructor-arg value="/login.jsp" />
</bean>
...
<bean id="requestCache" class="org.springframework.security.web.savedrequest.HttpSessionRequestCache">
<property name="requestMatcher">
<bean class="org.springframework.security.web.util.matcher.NegatedRequestMatcher">
<constructor-arg>
<bean class="org.springframework.security.web.util.matcher.MediaTypeRequestMatcher">
<constructor-arg>
<bean class="org.springframework.web.accept.HeaderContentNegotiationStrategy"/>
</constructor-arg>
<constructor-arg value="#{T(org.springframework.http.MediaType).APPLICATION_JSON}"/>
<property name="useEquals" value="true"/>
</bean>
</constructor-arg>
</bean>
</property>
</bean>
En mis JSP, agregue un controlador de errores AJAX global como se muestra aquí
$( document ).ajaxError(function( event, jqxhr, settings, thrownError ) {
if ( jqxhr.status === 403 ) {
window.location = "login.jsp";
} else {
if(thrownError != null) {
alert(thrownError);
} else {
alert("error");
}
}
});
Además, elimine los controladores de errores existentes de las llamadas AJAX en las páginas JSP:
var str = $("#viewForm").serialize();
$.ajax({
url: "get_mongoDB_doc_versions.do",
type: "post",
data: str,
cache: false,
async: false,
dataType: "json",
success: function(data) { ... },
// error: function (jqXHR, textStatus, errorStr) {
// if(textStatus != null)
// alert(textStatus);
// else if(errorStr != null)
// alert(errorStr);
// else
// alert("error");
// }
});
Espero que ayude a otros.
Actualización1 Descubrí que necesitaba agregar la opción (always-use-default-target = "true") a la configuración de formulario de inicio de sesión. Esto era necesario ya que después de que una solicitud AJAX se redirige a la página de inicio de sesión (debido a una sesión caducada), Spring recuerda la solicitud AJAX anterior y la redirecciona automáticamente después de iniciar sesión. Esto hace que el JSON devuelto se muestre en la página del navegador. Por supuesto, no es lo que quiero.
Actualización2
En lugar de usar always-use-default-target="true"
, use el ejemplo de @RobWinch de bloqueo de solicitudes AJAX desde requstCache. Esto permite que los enlaces normales sean redirigidos a su destino original después de iniciar sesión, pero AJAX va a la página de inicio después de iniciar sesión.
respondido 26 mar '18, 19:03
1
Además, probablemente desee redirigir al usuario a la URL de los encabezados. Así que finalmente se verá así:
$.ajax({
//.... other definition
complete:function(xmlHttp){
if(xmlHttp.status.toString()[0]=='3'){
top.location.href = xmlHttp.getResponseHeader('Location');
}
});
UPD: Opps. Tiene la misma tarea, pero no funciona. Haciendo estas cosas. Te mostraré la solución cuando la encuentre.
Respondido 19 Oct 08, 12:10
El autor debe eliminar esta respuesta, ya que él mismo dice que esto no funciona. Puede publicar una solución de trabajo más tarde. -1 - El Programador Loco
La idea es buena, pero el problema es que el encabezado "Ubicación" nunca se pasa. - Martín Zvarík
Mi edición fue rechazada, así que ... tienes que usar "var xmlHttp = $ .ajax ({...." la variable no puede estar dentro ... luego usa: console.log (xmlHttp.getAllResponseHeaders ()) ; - Martín Zvarík
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas javascript jquery ajax redirect or haz tu propia pregunta.
(no es una respuesta como tal): he hecho esto en el pasado editando la biblioteca jquery y agregando una marca de verificación para la página de inicio de sesión en cada XHR completo. No es la mejor solución porque tendría que hacerse cada vez que actualiza, pero resuelve el problema. - Sugendran
Ver pregunta relacionada: stackoverflow.com/questions/5941933/… - Nutel
EL
HttpContext.Response.AddHeader
y verifique en ajaxsetup el éxito es el camino a seguir - LCJ¿Por qué el servidor no puede devolver el 401? En ese caso, puede tener un $ .ajaxSetup global y usar el código de estado para redirigir la página. - Vishal
este enlace doanduyhai.wordpress.com/2012/04/21/… me da la solución correcta - pappu_kutty