Как управлять запросом перенаправления после вызова jQuery Ajax

Я использую $.post() вызвать сервлет с помощью Ajax, а затем использовать полученный фрагмент HTML для замены div элемент на текущей странице пользователя. Однако если время сеанса истекло, сервер отправляет директиву перенаправления для отправки пользователя на страницу входа в систему. В этом случае jQuery заменяет div элемент с содержимым страницы входа в систему, заставляя глаза пользователя наблюдать редкую сцену.

как я могу управлять директивой перенаправления из вызова Ajax с помощью в jQuery 1.2.6?

30 ответов


Я прочитал этот вопрос и реализовал подход, который был заявлен относительно установки кода состояния ответа на 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 "data" построен на сервере, чтобы иметь 2 участники: data.перенаправление и данные.форма. Я нашел этот подход намного лучше.


я решил эту проблему следующим образом:

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

браузеры не обрабатывают 301 и 302 ответа правильно. И на самом деле стандарт даже говорит, что они должны обрабатывать их "прозрачно", что является огромной головной болью для поставщиков библиотек Ajax. В Ра-Аякс мы были вынуждены использовать код состояния ответа HTTP 278 (просто некоторый "неиспользуемый" код успеха) для обработки прозрачных перенаправлений с сервера...

Это действительно раздражает меня, и если у кого-то здесь есть "тянуть" в W3C, я был бы признателен, что вы могли бы позволить W3C знаю что нам действительно нужно обрабатывать 301 и 302 коды сами...! ;)


решение, которое было в конечном итоге реализовано, состояло в том, чтобы использовать оболочку для функции обратного вызова вызова 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 всегда возвращали HTML внутри элемента DIV, который мы используем для замены части страницы. Кроме того, нам нужно было только перенаправить на страницу входа.


Мне нравится метод Тиммерца с небольшим завихрением лимона. Если вы когда-нибудь вернетесь contentType of 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();
    }

});

использование низкого уровня $.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';
}

Я просто хотел поделиться своим подходом, так как это может это может помочь кому-то:

Я в основном включил модуль JavaScript, который обрабатывает аутентификацию, такую как отображение имени пользователя, а также обработку этого случая редирект на страницу входа.

мой сценарий: у нас в основном есть ISA-сервер, между которым прослушивает все запросы и отвечает с 302 и заголовком местоположения на странице входа.

в моем Модуль JavaScript my первоначальный подход было что-то вроде

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

проблема (как многие здесь уже упоминалось) заключается в том, что браузер обрабатывает перенаправление сам по себе, поэтому мой ajaxComplete обратный вызов никогда не вызывался, но вместо этого я получил ответ уже перенаправленной страницы входа что, очевидно, было status 200. Проблема: как определить, является ли успешный ответ 200 вашей фактической страницей входа или просто какой-то другой произвольной Пейдж??

решение

поскольку я не смог захватить 302 ответа перенаправления, я добавил LoginPage заголовок на моей странице входа, который содержал url-адрес самой страницы входа. В модуле я теперь слушаю заголовок и делаю перенаправление:

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

...и это работает как шарм :). Вы можете задаться вопросом, почему я включаю url в LoginPage заголовок...ну в основном потому, что я не нашел способа определить url GET в результате автоматической перенаправление местоположения из


Я знаю, что эта тема старая, но я дам еще один подход, который я нашел и ранее описал здесь. В основном я использую 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() {}

если пользователь не аутентифицирован (токен истек) , такой метод не может быть доступен (защищен


Я думаю, что лучший способ справиться с этим-использовать существующие коды ответов протокола 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.

Edit: в комментарии @Rob ниже, 401 (код состояния HTTP для ошибок аутентификации) должен быть индикатором. См.403 запрещено против 401 несанкционированных HTTP-ответов подробнее. При этом некоторые веб-фреймворки используют 403 как для ошибок аутентификации, так и для ошибок авторизации - поэтому адаптируйтесь соответствующим образом. Спасибо, Роб.


Я решил эту проблему следующим образом:

добавьте промежуточное ПО для обработки ответа, если это перенаправление для запроса 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;
       }
   }
}

большинство данных решений используют обходной путь, используя дополнительный заголовок или неподходящий HTTP-код. Эти решения, скорее всего, будут работать, но чувствовать себя немного "hacky". Я придумал другое решение.

мы используем WIF, который настроен на перенаправление (passiveRedirectEnabled= "true") в ответе 401. Перенаправление полезно при обработке обычных запросов, но не будет работать для запросов AJAX (поскольку браузеры не будут выполнять 302/redirect).

используя следующие код в глобальный.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;
        }
    }

Это позволяет вам возвращать 401 ответ для AJAX-запросов, которые ваш javascript может обрабатывать путем перезагрузки страницы. Перезагрузка страницы вызовет 401, который будет обрабатываться WIF (и WIF перенаправит пользователя на страницу входа в систему).

пример javascript для обработки 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);
    }
});

другое решение, которое я нашел (особенно полезно, если вы хотите установить глобальное поведение), - использовать $.ajaxsetup() метод вместе с statusCode свойства. Как указывали другие, не используйте statuscode перенаправления (3xx), вместо того, чтобы использовать 4xx statuscode и обрабатывать перенаправление на стороне клиента.

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

заменить 400 С statuscode, который вы хотите обработать. Как уже упоминалось 401 Unauthorized может быть хорошей идеей. Я использую 400 поскольку это очень неспецифично, и я могу использовать 401 для более конкретных случаев (например, неправильные учетные данные для входа). Поэтому вместо перенаправления напрямую ваш бэкэнд должен вернуть 4xx error-code когда истекло время сеанса, и вы обрабатываете перенаправление на стороне клиента. Работает идеально для меня даже с такими фреймворками, как backbone.js


эта проблема может появиться, используя 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;
    }
});

Если данные: окно".location = '/ Acount / Login'" над фильтром поймает это и оценит, чтобы сделать перенаправление вместо отображения данных.


складывая воедино то, что сказали Владимир Прудников и Томас Хансен:

  • измените код на стороне сервера, чтобы определить, является ли это XHR. Если это так, установите код ответа перенаправления на 278. В Джанго:
   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); 
});

У меня есть простое решение, которое работает для меня, не требуется изменение кода сервера...просто добавьте ложку мускатного ореха...

$(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 для поиска любой уникальной строки, существующей на Вашей странице входа в систему...


попробовать

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

поместите его на страницу входа в систему. Если он был загружен в div на главной странице, он перенаправит до страницы входа в систему. "#site" - это идентификатор div, который находится на всех страницах, кроме страницы входа.


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

хотя ответы, похоже, работают для людей, если вы используете Spring Security, я нашел расширение LoginUrlAuthenticationEntryPoint и добавление определенного кода для обработки AJAX более надежным. Большинство примеров перехвата все перенаправляет не только сбои аутентификации. Это было нежелательно для проекта, над которым я работаю. Возможно, потребуется также расширить ExceptionTranslationFilter и переопределить метод "sendStartAuthentication", чтобы удалить шаг кэширования, если вы не хотите неудачный запрос AJAX кэшируется.

Пример 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"));
    }
}

источники: 1, 2


Я решил это, поместив следующее в свой логин.PHP-страница.

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

некоторые могут найти полезную ниже:

Я хотел, чтобы клиенты перенаправлялись на страницу входа для любого действия rest, которое отправляется без токена авторизации. Поскольку все мои действия rest основаны на 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);
        }
    };      
});

и это все.


позвольте мне еще раз процитировать проблему, описанную @Steg

у меня была аналогичная проблема с вашей. Я выполняю запрос ajax, который имеет 2 возможные ответы: тот, который перенаправляет браузер на новую страницу и тот, который заменяет существующую HTML-форму на текущей странице новой один.

IMHO это реальная проблема и должна быть официально распространена на текущие стандарты HTTP.

Я считаю, что новый стандарт Http будет использоваться новый статус-код. значение: в настоящее время 301/302 говорит браузеру пойти и получить содержимое этой запрос на новый location.

в расширенном стандарте он скажет, что если ответ status: 308 (просто пример), затем браузер должен перенаправить главную страницу на location при условии.

это сказано; я склонен уже подражать этому будущее поведение, и, следовательно, когда документ.редирект нужен, я сервер отвечает так:

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

когда JS получает "status: 204", он проверяет наличие x-status: 308 заголовок и делает документ.перенаправление на страницу, указанную в location заголовок.

имеет ли это какой-либо смысл для вас?


в сервлет вы должны положить response.setStatus(response.SC_MOVED_PERMANENTLY); для отправки статуса " 301 " xmlHttp вам необходимо перенаправление...

и в $.функция ajax вы не должны использовать


Я просто хотел зацепиться за любые запросы 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");
}

Если вы также хотите передать значения, вы также можете установить переменные сеанса и получить доступ Например: В вашем jsp вы можете написать

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

и затем вы можете сохранить это значение temp в своей переменной javascript и играть вокруг


у меня не было успеха с решением заголовка - они никогда не были подобраны в моем методе ajaxSuccess / ajaxComplete. Я использовал ответ Стега с пользовательским ответом, но я немного изменил сторону JS. Я настраиваю метод, который я вызываю в каждой функции, чтобы я мог использовать standard $.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;
}

наконец, я решаю проблему, добавив пользовательский 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 способ по статусу 302, 303 ,... .

и другая важная вещь что никакая потребность доработать код на стороне сервера или стороне клиента.

и следующая возможность получить доступ к другой информации о post action таких ошибок, сообщений и ... Вот так.

Я выложил это, может быть, помочь кому-то :)


у меня была эта проблема в приложении django, с которым я возился (отказ от ответственности: я возился, чтобы учиться, и никоим образом не эксперт). Я хотел использовать jQuery ajax для отправки запроса на удаление на ресурс, удалить его на стороне сервера, а затем отправить перенаправление обратно на (в основном) домашнюю страницу. Когда я послал HttpResponseRedirect('/the-redirect/') из скрипта python метод ajax jQuery получал 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 :Это была огромная боль для редактирования на мобильной версии так. Глупый интернет-провайдер поставил мой запрос на отмену услуги через право, когда я закончил с моим ответом!


вы также можете подключить прототип отправки XMLHttpRequest. Это будет работать для всех отправлений (jQuery/dojo / etc) с одним обработчиком.

Я написал этот код для обработки ошибки с истекшим сроком действия 500 страниц, но он должен работать так же хорошо, чтобы перехватить перенаправление 200. Готова запись "Википедии" о 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);
}

кроме того, вы, вероятно, захотите перенаправить пользователя на указанный в заголовках URL. Так что, наконец, это будет выглядеть так:

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

UPD: Opps. Есть такая же задача, но она не работает. Делать все это. Я покажу тебе решение, когда найду его.


я получил рабочий solulion, используя ответы от @John и @Arpad ссылке и @RobWinch ссылке

я использую Spring Security 3.2.9 и jQuery 1.10.2.

расширить класс Spring, чтобы вызвать ответ 4XX только из 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-безопасности.в 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>

в моем JSPs добавьте глобальный обработчик ошибок 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");
          }
      }
  });

кроме того, удалить существующие обработчики ошибок из вызовов AJAX на страницах 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");
//            }
        });

Я надеюсь, что это помогает другим.

обновление 1 Я обнаружил, что мне нужно добавить опцию (always-use-default-target="true") в конфигурацию form-login. Это было необходимо, так как после перенаправления запроса AJAX на страницу входа (из-за истекшего сеанса) Spring запоминает предыдущий запрос AJAX и автоматически перенаправляет его после входа в систему. Это приводит к отображению возвращенного JSON на странице браузера. Из конечно, не то, что я хочу.

обновление 2 Вместо использования always-use-default-target="true", используйте @RobWinch пример блокировки AJAX-запросов из requstCache. Это позволяет обычным ссылкам перенаправляться на исходную цель после входа в систему, но AJAX переходит на домашнюю страницу после входа в систему.