ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

(不是这样的答案) - 我过去通过编辑 jquery 库并在每个 XHR 完成时添加对登录页面的检查来完成此操作。不是最好的解决方案,因为每次升级时都必须这样做,但它确实解决了问题。
请参阅相关问题:stackoverflow.com/questions/5941933/…
HttpContext.Response.AddHeader 并检查 ajaxsetup 成功是要走的路
为什么服务器不能返回 401?在这种情况下,您可以拥有一个全局 $.ajaxSetup 并使用状态代码来重定向页面。
此链接 doanduyhai.wordpress.com/2012/04/21/… 为我提供了正确的解决方案

i
informatik01

我阅读了这个问题并实施了关于将响应 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。我发现这种方法要好得多。


stackoverflow.com/questions/503093/…中的解决方案所述,最好使用 window.location.replace(data.redirect);比 window.location.href = data.redirect;
5
5 revs, 4 users 77% SuperG

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

向响应中添加自定义标头: public ActionResult Index(){ if (!HttpContext.User.Identity.IsAuthenticated) { HttpContext.Response.AddHeader("REQUIRES_AUTH","1"); } 返回视图();将 JavaScript 函数绑定到 ajaxSuccess 事件并检查标头是否存在: $(document).ajaxSuccess(function(event, request, settings) { if (request.getResponseHeader('REQUIRES_AUTH') === '1' ) { window.location = '/'; } });


多么棒的解决方案。我喜欢一站式解决方案的想法。我需要检查 403 状态,但我可以为此使用 body 上的 ajaxSuccess 绑定(这是我真正想要的。)谢谢。
我刚做了这个,发现我需要 ajaxComplete 我正在使用 $.get() 函数并且除了 200 之外的任何状态都没有触发。事实上,我可能只是绑定到 ajaxError 。有关更多详细信息,请参阅下面的答案。
在很多情况下都可以,但是如果您的框架处理授权怎么办?
我喜欢标题方法,但也认为 - 就像@mwoods79 - 不应该重复重定向到哪里的知识。我通过添加标题 REDIRECT_LOCATION 而不是布尔值解决了这个问题。
注意在重定向之后设置响应的标头。如本页其他答案中所述,重定向可能对 ajaxSucces 处理程序是透明的。因此,我在登录页面的 GET 响应中包含了标题(在我的场景中最终并且仅触发了 ajaxSuccess)。
G
Gone Coding

没有浏览器正确处理 301 和 302 响应。事实上,该标准甚至说他们应该“透明地”处理它们,这对于 Ajax 库供应商来说是一个非常头疼的问题。在 Ra-Ajax 中,我们被迫使用 HTTP 响应状态代码 278(只是一些“未使用”的成功代码)来透明地处理来自服务器的重定向...

这真的让我很恼火,如果这里有人对 W3C 有一些“吸引力”,我会很感激你可以让 W3C 知道我们确实需要自己处理 301 和 302 代码......! ;)


我认为 278 应该成为官方 HTTP 规范的一部分。
它不是已经透明地处理它们了吗?如果资源已移动,透明地处理它意味着在提供的 URL 上重复请求。这就是我使用 XMLHttpRequest API 所期望的。
@PhilippeRathé 同意。透明处理正是我想要的。而且我不知道为什么它被认为是坏的。
@smwikipedia 在不重定向页面的情况下安排主要部分中的标记重定向。
从第一天开始,浏览器就透明地处理它。什么是ajax请求?当您希望浏览器发出请求并返回结果时。您想要的是一种特殊情况:“...返回结果,但在重定向的情况下不返回结果。”作为库开发人员,这可能会惹恼您,因为您正在尝试处理一般情况,改变浏览器的工作方式。但既然你这样做了,你应该期待烦恼。
r
radbyx

最终实现的解决方案是对 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,我们用它来替换页面的一部分。此外,我们只需要重定向到登录页面。


请注意,这可以缩短为 function cbWrapper(funct) { return function(data) { if($("#myForm", data).size() > 0) top.location.href="login";否则函数(数据); } } 。然后在调用 .post 时只需要 cbWrapper(myActualCB)。是的,注释中的代码很乱,但应该注意:)
size 已折旧,因此您可以在此处使用 .length 代替 size
B
BrianY

我喜欢蒂默兹的方法,稍微加一点柠檬。如果您在期待 JSON 时返回 text/html 的 contentType,那么您很可能会被重定向。就我而言,我只是简单地重新加载页面,然后将其重定向到登录页面。哦,检查一下 jqXHR 的状态是 200,这看起来很傻,因为你在错误函数中,对吧?否则,合法的错误情况将强制迭代重新加载(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();
    }

});

非常感谢布赖恩,您的回答对我的方案来说是最好的,尽管我希望有一个更安全的检查,例如比较哪个 url/页面被重定向到而不是简单的“内容类型”检查。我无法找到从 jqXHR 对象重定向到的页面。
我检查了状态 401,然后重定向。像冠军一样工作。
T
Till

使用低级 $.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,我该如何强制浏览器重定向?我的目标是重定向用户,而不仅仅是宣布他/她的会话已过期。
顺便说一句, $.ajax() 不是非常非常低级。就 jQuery 而言,它只是低级的,因为有 $.get、$.post 等,它们比 $.ajax 及其所有选项简单得多。
好家伙!抱歉,我不得不“不接受”您的回答,它仍然很有帮助。事情是,重定向由 XMLHttpRequest 自动管理,因此我总是在重定向后得到 200 状态码(叹气!)。我想我将不得不做一些讨厌的事情,比如解析 HTML 并寻找标记。
只是好奇,如果会话在服务器端结束,这是否意味着服务器发送不同的 SESSIONID?我们不能检测到吗?
注意:这不适用于重定向。 ajax 将转到新页面并返回其状态代码。
J
Juri

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

我基本上包含了一个 JavaScript 模块,它处理身份验证内容,例如显示用户名,并且在这种情况下处理重定向到登录页面。

我的场景:我们基本上有一个 ISA 服务器,在它之间侦听所有请求,并以 302 和位置标头响应我们的登录页面。

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

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

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

解决方案

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

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

...这就像魅力:)。您可能想知道为什么我将 url 包含在 LoginPage 标头中...基本上是因为我发现无法确定 GET 的 url,这是由于来自 xhr 对象的自动位置重定向...


+1 - 但自定义标头应该以 X- 开头,因此更好的标头是 X-LoginPage: http://example.com/login
@ShaquinTrifonoff 不再。我没有使用 X- 前缀,因为在 June 2011 中,一个 ITEF 文档建议弃用它们,而且实际上,对于 June 2012,自定义标头不应再以 X- 为前缀并不是官方的。
我们也有一个 ISA 服务器,我也遇到了同样的问题。我们没有在代码中解决它,而是使用 kb2596444 中的说明来配置 ISA 以停止重定向。
C
Community

我知道这个话题已经过时了,但我将提供另一种我发现并在之前描述过的方法 here。基本上我将 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 被替换为请求中的 url,由我的框架自动处理)。

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 事件解决了这个问题。
@JaroslawWaliszko 我在上次回复中粘贴了错误的事件!我的意思是 PreSendRequestHeaders 事件.....不是 PostAuthenticateRequest! >>脸红<<谢谢你指出我的错误。
@JaroslawWaliszko 使用 WIF 时,您还可以为 AJAX 请求返回 401 响应,并让您的 javascript 处理这些。此外,您假设所有 302 都需要身份验证,这可能并非在所有情况下都是正确的。如果有人感兴趣,我已经添加了答案。
C
Community

我认为更好的处理方法是利用现有的 HTTP 协议响应代码,特别是 401 Unauthorized

这是我解决它的方法:

服务器端:如果会话过期,并且请求是 ajax。发送 401 响应代码头 客户端:绑定到 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 Forbidden vs 401 Unauthorized HTTP responses。话虽如此,一些 Web 框架使用 403 来进行身份验证和授权错误 - 所以相应地进行调整。谢谢罗布。


我使用相同的方法。 jQuery 真的在 403 错误代码上调用 ajaxSuccess 吗?我认为实际上只需要 ajaxError 部分
M
M Reza

我这样解决了这个问题:

添加中间件来处理响应,如果是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中,如果响应中包含redirect,那肯定是redirect,所以改变浏览器的位置。

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

m
morten.c

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

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

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


页面哪里提到功能?
@Vikrant 如果我正确理解您的问题,您可以在加载 jQuery 之后和执行实际请求之前立即调用该函数。
T
Timmerz

我有一个适合我的简单解决方案,无需更改服务器代码……只需添加一茶匙肉豆蔻……

$(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 调用定义的函数,就像它没有覆盖成功方法一样。
似乎对我不起作用,至少现在。此处可能的解释:stackoverflow.com/a/12010724/260665
R
Rob

大多数给定的解决方案都使用了一种变通方法,即使用额外的标头或不合适的 HTTP 代码。这些解决方案很可能会奏效,但感觉有点“笨拙”。我想出了另一个解决方案。

我们正在使用配置为在 401 响应上重定向 (passiveRedirectEnabled="true") 的 WIF。重定向在处理普通请求时很有用,但不适用于 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);
    }
});

很好的解决方案。谢谢。
P
Przemek Marcinkiewicz

然后使用 ASP.NET MVC RedirectToAction 方法可能会出现此问题。为了防止表单在 div 中显示响应,您可以简单地使用 $.ajaxSetup 对传入响应执行某种 ajax 响应过滤器。如果响应包含 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 在响应正文或标头中?
G
Graham King

将 Vladimir Prudnikov 和 Thomas Hansen 所说的话放在一起:

更改您的服务器端代码以检测它是否是 XHR。如果是,则将重定向的响应代码设置为 278。在 django 中:

如果 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 { ... 你的代码在这里 ... } },数据:$(this)。序列化(),};$.ajax(选项);});


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

p
podeig

尝试

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

把它放在登录页面上。如果它是在主页上的 div 中加载的,它将重定向到登录页面。 “#site”是一个 div 的 id,它位于除登录页面之外的所有页面上。


M
M Reza

虽然如果您使用 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


如果投反对票的人能解释他们投反对票的原因,那(对我来说)会很有帮助。如果这个解决方案有什么不好的地方,我想从我的错误中吸取教训。谢谢。
如果使用 Spring 并且还使用 JSF,那么还要检查这个: ("partial/ajax").equalsIgnoreCase(request.getHeader("faces-request"));
用户可能会因为您没有提及:(1)检测您的错误响应所必需的客户端模块而投反对票; (2) Spring 配置所需的 mods 以将您自定义的 LoginUrlAuthenticationEntryPoint 添加到过滤器链中。
您的回答与@Arpad 的回答类似。它对我有用;使用 Spring Security 3.2.9。 stackoverflow.com/a/8426947/4505142
C
Chaim Klar

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

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

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

我相信新的 Http 标准将使用新的状态码。含义:当前 301/302 告诉浏览器去获取 this 请求的内容到一个新的 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 标头中提供的页面。

这对你有意义吗?


P
Paul Richards

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

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

T
Tomer

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

我希望客户端被重定向到登录页面,以便在没有授权令牌的情况下发送任何休息操作。因为我所有的休息动作都是基于 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);
        }
    };      
});

就是这样。


佚名

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

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

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

问题是它不是很灵活,您无法决定要重定向的位置..

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


B
Bretticus

我只是想锁定整个页面的任何 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");
}

后者将隐藏任何其他错误,使故障排除有问题
403 并不意味着用户未通过身份验证,这意味着(可能已通过身份验证的)用户无权查看请求的资源。所以它不应该重定向到登录页面
k
karthik339

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

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

然后你可以将这个临时值存储在你的 javascript 变量中并玩转


j
jocull

我的标头解决方案没有任何成功——它们从未在我的 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;
}

A
Ali Adlavaran

最后,我通过添加自定义 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 $.post 的每个响应中,我都有请求的 url 以及其他响应标头,这些标头是 POST 方法的结果,状态为 302303,...。

另一个重要的事情是无需在服务器端或客户端修改代码。

接下来是能够以这种方式访问发布操作的其他信息,例如错误、消息和……。

我发布了这个,也许可以帮助某人:)


B
Benny Jobigan

我在我正在修补的 django 应用程序上遇到了这个问题(免责声明:我正在修补学习,绝不是专家)。我想做的是使用 jQuery ajax 向资源发送 DELETE 请求,在服务器端将其删除,然后将重定向发送回(基本上)主页。当我从 python 脚本发送 HttpResponseRedirect('/the-redirect/') 时,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 将我的服务取消请求通过了!


C
Curtis Yallop

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

我编写了这段代码来处理 500 页过期错误,但它应该同样可以捕获 200 重定向。准备好 wikipedia entry on XMLHttpRequest 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);
}

D
Darren Parker

使用@John 和@Arpad link 和@RobWinch link 的答案,我得到了一个可行的解决方案

我使用 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 错误处理程序,如图所示 here

  $( 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 会在登录后转到主页。


E
Eugen Konkov

作为 ajax 的替代方案,正在开发一个新的 Fetch API,它允许手动重定向处理。 You need to check 如果当前的浏览器支持足以满足您的需求。