jQuery Ajax调用后如何管理重定向请求


1368

我正在使用$.post()Ajax调用servlet,然后使用结果HTML片段替换div用户当前页面中的元素。但是,如果会话超时,服务器将发送重定向指令以将用户发送到登录页面。在这种情况下,jQuery将用div登录页面的内容替换元素,从而迫使用户的眼睛确实看到了罕见的场景。

如何使用jQuery 1.2.6从Ajax调用管理重定向指令?


1
(不是这样的答案)-过去,我通过编辑jquery库并在每个XHR上添加对登录页面的检查来做到这一点。这不是最佳解决方案,因为每次升级时都必须这样做,但确实可以解决问题。
Sugendran

1
请参阅相关的问题:stackoverflow.com/questions/5941933/...
Nutel

HttpContext.Response.AddHeader在ajaxsetup sucess和检查是要走的路
LCJ

4
为什么服务器不能返回401?在这种情况下,您可以具有全局$ .ajaxSetup并使用状态代码重定向页面。
维沙尔2013年

1
此链接doanduyhai.wordpress.com/2012/04/21/…给了我正确的解决方案
pappu_kutty 2014年

Answers:


697

我阅读了此问题,并实现了有关将响应HTTP状态代码设置为278的方法,以避免浏览器透明地处理重定向。即使这样做有效,我还是有点不满意,因为它有点破烂。

在深入研究之后,我放弃了这种方法并使用了JSON。在这种情况下,对AJAX请求的所有响应都具有状态码 200,并且响应的主体包含在服务器上构造的JSON对象。然后,客户端上的JavaScript可以使用JSON对象来决定它需要做什么。

我有一个与您类似的问题。我执行的AJAX请求有2种可能的响应:一种浏览器重定向到新页面,另一种新页面替换当前页面上的现有HTML表单。执行此操作的jQuery代码如下所示:

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

JSON对象“数据”在服务器上构造为具有2个成员:data.redirectdata.form。我发现这种方法要好得多。


58
stackoverflow.com/questions/503093/…中的解决方案所述,最好使用window.location.replace(data.redirect); 比window.location.href = data.redirect;
卡莱斯·巴罗贝斯(CarlesBarrobés)2010年

8
出于某种原因,最好在操作中使用HTTP代码。例如307代码是HTTP临时重定向?
Sergei Golos

15
@Sergei Golos的原因是,如果您执行HTTP重定向,则该重定向实际上永远不会到达ajax成功回调。浏览器处理重定向,并提供200代码以及重定向目标的内容。
Miguel Silva

2
服务器代码是什么样的?具有data.form和data.redirect的返回值。基本上我该如何确定是否将重定向添加到其中?
Vnge

6
如果它显示了如何在服务器上完成此答案,则将更有用。
Pedro Hoehl Carvalho,2015年

243

我通过以下方法解决了这个问题:

  1. 向响应中添加自定义标头:

    public ActionResult Index(){
        if (!HttpContext.User.Identity.IsAuthenticated)
        {
            HttpContext.Response.AddHeader("REQUIRES_AUTH","1");
        }
        return View();
    }
  2. 将JavaScript函数绑定到ajaxSuccess事件并检查标题是否存在:

    $(document).ajaxSuccess(function(event, request, settings) {
        if (request.getResponseHeader('REQUIRES_AUTH') === '1') {
           window.location = '/';
        }
    });

6
多么棒的解决方案。我喜欢一站式解决方案的想法。我需要检查403状态,但是我可以在主体上使用ajaxSuccess绑定(这是我真正想要的。)谢谢。
Bretticus,2010年

4
我只是这样做,发现在使用$ .get()函数的地方需要ajaxComplete,并且除200以外的任何状态都不会触发。实际上,我可能只绑定了ajaxError。请参阅下面的我的答案以获取更多详细信息。
Bretticus,2010年

2
在很多情况下都可以,但是如果您的框架处理授权该怎么办?
jwaliszko 2012年

13
我喜欢标题方法,但也认为-类似于@ mwoods79-重定向到何处的知识不应重复。我通过添加标头REDIRECT_LOCATION而不是布尔值解决了该问题。
rintcius 2012年

3
重定向后,请务必在响应上设置标头。如本页其他答案所述,重定向对ajaxSucces处理程序可能是透明的。因此,我在登录页面的GET响应中包含了标头(该标头最终是并且在我的场景中仅触发ajaxSuccess)。
sieppl

118

没有浏览器正确处理301和302响应。实际上,该标准甚至说他们应该“透明地”处理它们,这对于Ajax Library供应商来说是一个巨大的麻烦。在拉阿贾克斯我们被迫使用HTTP响应状态代码278(只是一些“未使用”的成功代码)来透明地处理来自服务器的重定向。

这真的让我很烦,如果这里有人在W3C中有一些“拉”,我将不胜感激,您可以让W3C 知道我们确实需要自己处理301和302代码...!;)


3
我的一招是278应该成为官方HTTP规范的一部分。
克里斯·马里西奇

2
它不是已经透明地处理了它们吗?如果资源已移动,则透明地处理意味着在提供的URL上重复请求。这就是使用XMLHttpRequest API的期望。
菲利普·拉特

@PhilippeRathé同意。透明地处理正是我想要的。而且我不知道为什么它被认为是不好的。
smwikipedia '16

@smwikipedia安排在主要部分中重定向标记而不重定向页面。
cmc

如果这里有人在W3C中有“拉”,我将不胜感激,您可以让W3C知道我们确实需要自己处理301和302代码...!;)
Tommy.Tang

100

最终实现的解决方案是对Ajax调用的回调函数使用包装器,并在此包装器中检查返回的HTML块上是否存在特定元素。如果找到该元素,则包装器将执行重定向。如果不是,则包装将调用转发给实际的回调函数。

例如,我们的包装器函数类似于:

function cbWrapper(data, funct){
    if($("#myForm", data).length > 0)
        top.location.href="login.htm";//redirection
    else
        funct(data);
}

然后,在进行Ajax调用时,我们使用了类似以下内容:

$.post("myAjaxHandler", 
       {
        param1: foo,
        param2: bar
       },
       function(data){
           cbWrapper(data, myActualCB);
       }, 
       "html"
);

这对我们有用,因为所有Ajax调用总是在DIV元素内返回HTML,该DIV元素用于替换页面的一部分。另外,我们只需要重定向到登录页面。


4
请注意,可以将其简化为函数cbWrapper(funct){返回函数(数据){if($(“#myForm”,data).size()> 0)top.location.href =“ login”; else funct(data); }}。然后,在调用.post时只需要cbWrapper(myActualCB)。是的,注释中的代码是一团糟,但应注意:)
Simen Echholt 2010年

尺寸已贬值,因此您可以在此处使用.length代替尺寸
sunil

66

我喜欢蒂默兹的方法,加上一点柠檬味。如果你有机会返回的contentType的text / html,当你期待JSON,你是最有可能被重定向。就我而言,我只是简单地重新加载页面,然后将其重定向到登录页面。哦,检查一下jqXHR的状态是否为200,这似乎很愚蠢,因为您使用的是错误函数,对吗?否则,合法的错误情况将迫使迭代重载(哎呀)

$.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();
    }

});

1
非常感谢Brian,您的回答是我的最佳选择,尽管我希望是否有更安全的检查,例如比较重定向到哪个url /页面,而不是简单的“内容类型”检查。我找不到从jqXHR对象重定向到的页面。
约翰尼

我检查了状态401,然后重定向。像冠军一样工作。
埃里克

55

使用低级$.ajax()调用:

$.ajax({
  url: "/yourservlet",
  data: { },
  complete: function(xmlHttp) {
    // xmlHttp is a XMLHttpRquest object
    alert(xmlHttp.status);
  }
});

尝试以下重定向:

if (xmlHttp.code != 200) {
  top.location.href = '/some/other/page';
}

我试图避免低级的东西。无论如何,假设我使用了您所描述的内容,一旦检测到HTTP代码为3xx,如何强制浏览器重定向?我的目标是重定向用户,而不仅仅是宣布他/她的会话已过期。
Elliot Vargas

4
顺便说一句,$ .ajax()不是很低级。就jQuery而言,它只是底层的,因为有$ .get,$。post等,它们比$ .ajax及其所有选项都简单得多。
直到

16
好家伙!抱歉,我不得不“不接受”您的回答,它仍然非常有帮助。事实是,重定向由XMLHttpRequest自动管理,因此重定向后我总是得到200状态代码(叹息!)。我想我将不得不做一些讨厌的事情,例如解析HTML并寻找标记。
Elliot Vargas

1
只是好奇,如果会话在服务器上结束,这是否意味着服务器发送了不同的SESSIONID?我们不能检测到吗?
Salamander2007

10
注意:这不适用于重定向。ajax将转到新页面并返回其状态代码。

33

我只想分享我的方法,因为这可能会帮助某人:

我基本上包括一个JavaScript模块,该模块处理身份验证之类的内容,例如显示用户名,在这种情况下,还处理重定向到登录页面的情况

我的情况:我们基本上有一个ISA服务器,在这两个服务器之间侦听所有请求,并以302和登录页面的位置标头作为响应

在我的JavaScript模块中,我最初的方法

$(document).ajaxComplete(function(e, xhr, settings){
    if(xhr.status === 302){
        //check for location header and redirect...
    }
});

问题(在这里已经提到了很多)是浏览器自己处理重定向,因此ajaxComplete从未调用过我的回调,而是得到了已经重定向的登录页面响应,这显然是一个status 200。问题:您如何检测成功的200响应是您的实际登录页面还是其他任意页面?

解决方案

由于无法捕获302重定向响应,因此我LoginPage在登录页面上添加了一个标头,其中包含登录页面本身的网址。现在,在模块中,我监听标题并进行重定向:

if(xhr.status === 200){
    var loginPageRedirectHeader = xhr.getResponseHeader("LoginPage");
    if(loginPageRedirectHeader && loginPageRedirectHeader !== ""){
        window.location.replace(loginPageRedirectHeader);
    }
}

...就像魅​​力:)。您可能想知道为什么我在LoginPage标头中包含url ...基本上是因为我找不到确定对象GET的自动位置重定向所产生的url的方法xhr...


+1-但自定义标头应该以开头X-,因此可以使用更好的标头X-LoginPage: http://example.com/login
uınbɐɥs

6
@ShaquinTrifonoff再也没有了。我之所以没有使用X-前缀,是因为20116月,一份ITEF文件提出了弃用它们的建议,实际上,自2012年6月起,不再需要对自定义标头添加前缀X-
朱里2012年

我们也有一个ISA服务器,而我遇到了同样的问题。与其在代码中变通解决,不如使用kb2596444中的说明将ISA配置为停止重定向。
斯科特·

29

我知道这个主题很旧,但是我将给出我已经找到并在此之前描述的另一种方法。基本上,我将ASP.MVC与WIF一起使用(但这对于本主题的上下文并不真正重要-无论使用哪种框架,答案都足够。线索保持不变-在执行Ajax请求时处理与身份验证失败相关的问题)

下面显示的方法可以应用于所有开箱即用的ajax请求(如果它们显然没有重新定义beforeSend事件)。

$.ajaxSetup({
    beforeSend: checkPulse,
    error: function (XMLHttpRequest, textStatus, errorThrown) {
        document.open();
        document.write(XMLHttpRequest.responseText);
        document.close();
    }
});

在执行任何ajax请求之前,CheckPulse将调用方法(控制器方法可以是最简单的方法):

[Authorize]
public virtual void CheckPulse() {}

如果用户未通过身份验证(令牌已过期),则无法访问此方法(受Authorize属性保护)。由于框架处理身份验证,而令牌到期,因此将http状态302放入响应中。如果您不希望浏览器透明地处理302响应,请在Global.asax中捕获它并将响应状态更改为例如200 OK。另外,添加标头,它指示您以特殊方式(稍后在客户端)处理此类响应:

protected void Application_EndRequest()
{
    if (Context.Response.StatusCode == 302
        && (new HttpContextWrapper(Context)).Request.IsAjaxRequest())
    {                
        Context.Response.StatusCode = 200;
        Context.Response.AddHeader("REQUIRES_AUTH", "1");
    }
}

最后,在客户端检查此类自定义标头。如果存在-应当完全重定向到登录页面(在我的情况下window.location,该请求将替换为来自我的框架自动处理的请求中的网址)。

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

我通过使用PostAuthenticateRequest事件而不是EndRequest事件解决了该问题。
Frinavale

@JaroslawWaliszko我在上次回复中粘贴了错误的事件!我的意思是PreSendRequestHeaders事件.....不是PostAuthenticateRequest!>>脸红<<感谢您指出我的错误。
弗里纳维尔

@JaroslawWaliszko使用WIF时,您还可以为AJAX请求返回401响应,并让您的JavaScript处理这些响应。另外,您假设所有302都需要身份验证,这在所有情况下可能都不正确。如果有人感兴趣,我已经添加了答案。
罗布2014年

26

我认为解决此问题的更好方法是利用现有的HTTP协议响应代码,尤其是401 Unauthorized

这是我解决的方法:

  1. 服务器端:如果会话过期,并且请求是ajax。发送401响应码标头
  2. 客户端:绑定到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';
    }
    });

IMO这是更通用的,您没有编写新的自定义规范/标题。您也不必修改任何现有的ajax调用。

编辑:根据下面的@Rob的注释,401(身份验证错误的HTTP状态代码)应作为指示符。有关更多详细信息,请参见403禁止与401未经授权的HTTP响应。话虽如此,某些Web框架同时使用403进行身份验证和授权错误-因此请进行相应调整。谢谢罗伯。


我使用相同的方法。jQuery是否真的在403错误代码上调用ajaxSuccess?我认为只有ajaxError部分实际需要
马吕斯Balčytis

24

我这样解决了这个问题:

添加一个中间件来处理响应,如果它是ajax请求的重定向,则使用重定向URL将响应更改为正常响应。

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

然后在ajaxComplete中,如果响应包含重定向,则它必须是重定向,因此请更改浏览器的位置。

$('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;
       }
   }
}

20

我有一个适用于我的简单解决方案,无需更改服务器代码...只需添加一小撮肉豆蔻...

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

我检查html标签的存在,但是您可以更改indexOf来搜索登录页面中存在的唯一字符串...


这似乎对我不起作用,它继续调用用ajax调用定义的函数,就像它没有覆盖成功方法一样。
adriaanp 2011年

似乎至少对我不起作用。可能的解释在这里:stackoverflow.com/a/12010724/260665
Raj Pawan Gumdal

20

我发现的另一种解决方案(如果要设置全局行为特别有用)是将该$.ajaxsetup()方法statusCodeproperty一起使用。就像其他人指出的那样,不要使用重定向状态码(3xx),而应使用状态码来4xx处理客户端重定向。

$.ajaxSetup({ 
  statusCode : {
    400 : function () {
      window.location = "/";
    }
  }
});

替换400为您要处理的状态码。像已经提到的那样,401 Unauthorized可能是个好主意。我使用,400因为它是非常不确定的,我可以将其401用于更具体的情况(例如错误的登录凭据)。因此,4xx当会话超时并且您要处理重定向客户端时,后端不应该直接重定向,而应返回错误代码。即使使用骨干.js之类的框架,对我来说也很完美


在页面上哪里提到功能?
Vikrant

@Vikrant如果我正确理解了您的问题,则可以在加载jQuery之后且在执行实际请求之前立即调用该函数。
morten.c

19

给定的大多数解决方案都使用一种变通方法,即使用额外的标头或不正确的HTTP代码。这些解决方案很可能会起作用,但会有点“棘手”。我想出了另一个解决方案。

我们正在使用WIF,该WIF被配置为在401响应上重定向(passiveRedirectEnabled =“ true”)。重定向在处理普通请求时很有用,但不适用于AJAX请求(因为浏览器不会执行302 /重定向)。

在global.asax中使用以下代码,您可以为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;
        }
    }

这样,您就可以为AJAX请求返回401响应,然后您的JavaScript可以通过重新加载页面来处理该响应。重新加载页面将抛出401,将由WIF处理(WIF会将用户重定向到登录页面)。

处理401错误的示例JavaScript:

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

好的解决方案。谢谢。
DanMan '18

19

然后使用ASP.NET MVC RedirectToAction方法可能会出现此问题。为了防止表单在div中显示响应,您可以简单地使用某种ajax响应过滤器来使用$ .ajaxSetup传入响应。如果响应包含MVC重定向,则可以在JS端评估此表达式。以下是JS的示例代码:

$.ajaxSetup({
    dataFilter: function (data, type) {
        if (data && typeof data == "string") {
            if (data.indexOf('window.location') > -1) {
                eval(data);
            }
        }
        return data;
    }
});

如果数据为:“ window.location ='/ Acount / Login'”,则过滤器将捕获该数据并进行评估以进行重定向,而不是显示数据。


data在响应正文或标题中?
GMsoF

16

弗拉基米尔·普鲁德尼科夫和托马斯·汉森说的话:

  • 更改服务器端代码以检测它是否为XHR。如果是,请将重定向的响应代码设置为278。在django中:
   if request.is_ajax():
      response.status_code = 278

这使浏览器将响应视为成功,并将其交给Javascript。

  • 在您的JS中,确保通过Ajax提交表单,检查响应代码并根据需要重定向:
$('#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); 
});

13
    <script>
    function showValues() {
        var str = $("form").serialize();
        $.post('loginUser.html', 
        str,
        function(responseText, responseStatus, responseXML){
            if(responseStatus=="success"){
                window.location= "adminIndex.html";
            }
        });     
    }
</script>

12

尝试

    $(document).ready(function () {
        if ($("#site").length > 0) {
            window.location = "<%= Url.Content("~") %>" + "Login/LogOn";
        }
    });

将其放在登录页面上。如果已将其加载到主页的div中,它将重定向到登录页面。“ #site”是位于除登录页面以外的所有页面上的div的ID。


12

如果您使用的是Spring Security,答案似乎对某些人有用,但我发现可以扩展LoginUrlAuthenticationEntryPoint并添加特定的代码来更健壮地处理AJAX。大多数示例拦截所有重定向,而不仅仅是身份验证失败。对于我从事的项目来说,这是不可取的。如果您不想缓存失败的AJAX请求,则可能还需要扩展ExceptionTranslationFilter并重写“ sendStartAuthentication”方法以删除缓存步骤。

示例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"));
    }
}

来源: 12


3
如果反对派选民解释他们为什么反对投票,这将对我有帮助。如果此解决方案有不好的地方,我想从错误中学习。谢谢。
John

如果使用Spring并且还使用JSF,则还要检查以下内容:(“ partial / ajax”)。equalsIgnoreCase(request.getHeader(“ faces-request”));
J Slick

用户可能会投票否决,因为您没有提到:(1)检测您的错误响应所必需的客户端mod;(2)Spring配置的必需mod,以将您自定义的LoginUrlAuthenticationEntryPoint添加到过滤器链。
J Slick 2014年

您的答案类似于@Arpad的答案。它为我工作;使用Spring Security 3.2.9。stackoverflow.com/a/8426947/4505142
达伦·帕克

11

我通过将以下内容放在我的login.php页面中解决了这个问题。

<script type="text/javascript">
    if (top.location.href.indexOf('login.php') == -1) {
        top.location.href = '/login.php';
    }
</script>

10

让我再次引用@Steg描述的问题

我有一个与您类似的问题。我执行的ajax请求有2种可能的响应:一种将浏览器重定向到新页面,另一种将新页面替换当前页面上的现有HTML表单。

恕我直言,这是一个真正的挑战,必须正式扩展到当前的HTTP标准。

我相信新的Http标准将使用新的状态代码。含义:当前301/302告诉浏览器继续将此请求的内容提取到new location

在扩展标准中,它将说如果响应status: 308(仅作为示例),则浏览器应将主页重定向到location提供的页面。

话虽如此; 我倾向于已经模仿了这种将来的行为,因此,当需要document.redirect时,服务器将响应如下:

status: 204 No Content
x-status: 308 Document Redirect
x-location: /login.html

当JS获得“ status: 204”时,它将检查x-status: 308标题是否存在,并执行document.redirect到location标题中提供的页面。

这对您有意义吗?


7

有些人可能发现以下有用:

我希望将客户端重定向到登录页面,以进行没有授权令牌发送的任何其他操作。因为我所有的休息动作都是基于Ajax的,所以我需要一种很好的通用方法来重定向到登录页面,而不是处理Ajax成功功能。

这是我所做的:

在任何Ajax请求上,我的服务器都将返回Json 200响应“需要身份验证”(如果客户端需要进行身份验证)。

Java中的简单示例(服务器端):

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

在我的Javascript中,我添加了以下代码:

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

就是这样。


5

在servlet中,您应该 response.setStatus(response.SC_MOVED_PERMANENTLY); 发送重定向所需的'301'xmlHttp状态...

并且在$ .ajax函数中,您不应使用该.toString()函数...,只是

if (xmlHttp.status == 301) { top.location.href = 'xxxx.jsp'; }

问题是它不是非常灵活,您无法确定要重定向到的位置。

通过servlet重定向应该是最好的方法。但是我仍然找不到正确的方法。


5

我只是想锁定整个页面的所有ajax请求。@SuperG让我开始。我最终得到的是:

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

我想专门检查某些HTTP状态代码以作为我的决定的基础。但是,您可以将其绑定到ajaxError以获得除成功以外的任何内容(也许只有200个?)。

$('body').bind('ajaxError', function(event,request,settings){
    window.location.replace("http://mysite.tld/login");
}

1
后者会隐藏任何其他错误,使故障排除成为问题
Tim Abell

1
403并不表示用户未通过身份验证,而是表示(可能已通过身份验证)用户无权查看请求的资源。因此,它不应重定向到登录页面
Rob

5

如果您还想传递值,那么还可以设置会话变量并访问Eg:在您的jsp中,您可以编写

<% HttpSession ses = request.getSession(true);
   String temp=request.getAttribute("what_you_defined"); %>

然后,您可以将此临时值存储在javascript变量中,然后进行操作


5

头文件解决方案没有任何成功-我的ajaxSuccess / ajaxComplete方法中从未将它们拾取。我在自定义响应中使用了Steg的答案,但在JS方面做了一些修改。我安装,我在每个函数调用,所以我可以使用标准的方法$.get$.post方法。

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

使用中的示例...

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

5

最后,我通过添加一个custom解决了这个问题HTTP Header。在响应服务器端的每个请求之前,我将当前请求的url添加到响应的标头中。

我在服务器上的应用程序类型是Asp.Net MVC,它是执行此操作的好地方。在Global.asax我实施的Application_EndRequest活动是这样的:

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

    }

它对我来说很完美!现在,在JQuery $.posti的每个响应中,都有被请求的url响应头和其他响应头,这些响应头是POST方法根据状态产生的302303 ......。

其他重要的事情是,无需在服务器端或客户端修改代码。

其次是以这种方式访问​​事后操作的其他信息(如错误,消息等)的能力。

我张贴了这个,也许可以帮助某人:)


4

我在修改的django应用上遇到了这个问题(免责声明:我在学习,但绝不是专家)。我想做的是使用jQuery ajax将DELETE请求发送到资源,在服务器端将其删除,然后将重定向发送回(基本上)到主页。当我HttpResponseRedirect('/the-redirect/')从python脚本发送信息时,jQuery的ajax方法接收的是200而不是302。因此,我要做的是使用以下命令发送响应300:

response = HttpResponse(status='300')
response['Location'] = '/the-redirect/' 
return  response

然后,我使用jQuery.ajax在客户端上发送/处理了请求,如下所示:

<button onclick="*the-jquery*">Delete</button>

where *the-jquery* =
$.ajax({ 
  type: 'DELETE', 
  url: '/resource-url/', 
  complete: function(jqxhr){ 
    window.location = jqxhr.getResponseHeader('Location'); 
  } 
});

也许使用300不是“正确的”,但至少它能按我想要的那样工作。

PS:在SO的移动版本上进行编辑非常麻烦。当我完成答案后,愚蠢的ISP就将我的服务取消请求转为正确!


4

您还可以挂钩XMLHttpRequest发送原型。这将适用于使用一个处理程序的所有发送(jQuery / dojo / etc)。

我编写了这段代码来处理500页的过期错误,但它应该也可以捕获200个重定向。准备好XMLHttpRequest上的Wikipedia条目onreadystatechange,以了解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);
}

2

另外,您可能希望将用户重定向到标题URL中给定的URL。所以最终它将看起来像这样:

$.ajax({
    //.... other definition
    complete:function(xmlHttp){
        if(xmlHttp.status.toString()[0]=='3'){
        top.location.href = xmlHttp.getResponseHeader('Location');
    }
});

UPD:对。具有相同的任务,但不起作用。做这个东西。当我找到它时,我将向您显示解决方案。


4
作者应删除此答案,因为他本人说这是行不通的。他可以稍后发布有效的解决方案。-1
TheCrazyProgrammer

这个主意很好,但是问题是“ Location”标头从不传递。
MartinZvarík'19

我的编辑被拒绝,所以...您必须使用“ var xmlHttp = $ .ajax({....”变量不能在内部...然后使用:console.log(xmlHttp.getAllResponseHeaders()) ;
MartinZvarík19年

2

我得到了使用@John和@Arpad 链接以及@RobWinch 链接的答案的有效解决 方案

我使用Spring Security 3.2.9和jQuery 1.10.2。

扩展Spring的类,使其仅从AJAX请求引起4XX响应:

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-security.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>

在我的JSP,添加一个全局AJAX错误处理程序如图所示这里

  $( document ).ajaxError(function( event, jqxhr, settings, thrownError ) {
      if ( jqxhr.status === 403 ) {
          window.location = "login.jsp";
      } else {
          if(thrownError != null) {
              alert(thrownError);
          } else {
              alert("error");
          }
      }
  });

另外,从JSP页面中的AJAX调用中删除现有的错误处理程序:

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

我希望它可以帮助其他人。

Update1 我发现我需要将选项(always-use-default-target =“ true”)添加到表单登录配置中。之所以需要这样做,是因为AJAX请求重定向到登录页面后(由于会话过期),Spring会记住先前的AJAX请求并在登录后自动重定向到该页面。这将导致返回的JSON显示在浏览器页面上。当然,不是我想要的。

Update2 可以always-use-default-target="true"使用@RobWinch示例来阻止来自requstCache的AJAX请求,而不是使用。这使普通链接在登录后可以重定向到其原始目标,但是AJAX在登录后转到主页。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.