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

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

1419
Elliot Vargas 14 Окт 2008 в 01:29

28 ответов

Лучший ответ

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

97
radbyx 31 Авг 2016 в 06:15

Я решил это, разместив следующее на моей странице login.php.

<script type="text/javascript">
    if (top.location.href.indexOf('login.php') == -1) {
        top.location.href = '/login.php';
    }
</script>
11
Paul Richards 5 Дек 2011 в 19:59

Я знаю, что эта тема старая, но я приведу еще один подход, который я нашел и ранее описал -сетью - 3 MVC- , в то время , используя - WIF - и - JQuery " > . В основном я использую 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;
                }
            }
    });
}
29
Community 23 Май 2017 в 11:47

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

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

И тогда вы можете сохранить это временное значение в вашей переменной JavaScript и поиграть

5
karthik339 7 Янв 2013 в 06:35

В сервлете вы должны положить response.setStatus(response.SC_MOVED_PERMANENTLY); чтобы отправить «301» xmlHttp статус, необходимый для перенаправления ...

И в функции $ .ajax вы не должны использовать функцию .toString() ... просто

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

Проблема в том, что он не очень гибкий, вы не можете решить, куда хотите перенаправить ..

Перенаправление через сервлеты должно быть лучшим способом. но я до сих пор не могу найти правильный способ сделать это.

5
Juan ToroJuan Toro 14 Ноя 2008 в 20:56

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

});
66
BrianY 14 Сен 2011 в 17:44

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

Добавьте промежуточное программное обеспечение для обработки ответа, если это перенаправление для запроса 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;
       }
   }
}
24
Mohammad Reza Farahani 13 Май 2018 в 12:54

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

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

ИМХО, это реальная проблема, и ее придется официально распространить на текущие стандарты HTTP.

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

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

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

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

Когда JS получает «status: 204», он проверяет наличие заголовка x-status: 308 и выполняет document.redirect для страницы, предоставленной в заголовке location.

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

10
Chaim Klar 12 Июл 2018 в 21:45

У меня была эта проблема в приложении 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 было огромной болью. Глупый провайдер пропустил мой запрос на отмену услуги, когда я закончил с моим ответом!

4
Benny Jobigan 28 Авг 2011 в 10:18

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

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

116
Gone Coding 19 Авг 2015 в 08:03

Хотя ответы, кажется, работают для людей, если вы используете Spring Security, я обнаружил, что расширение LoginUrlAuthenticationEntryPoint и добавление специального кода для обработки AJAX более надежны. Большинство примеров перехватывает all , перенаправляет не только ошибки аутентификации. Это было нежелательно для проекта, над которым я работаю. Может также возникнуть необходимость расширить 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

12
Mohammad Reza Farahani 13 Май 2018 в 13:08

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

  • Измените код на стороне сервера, чтобы определить, является ли он 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); 
});
16
Graham King 6 Авг 2009 в 20:17

Вы также можете перехватить отправку прототипа 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);
}
4
Curtis Yallop 10 Апр 2012 в 19:38

Некоторые могут найти следующее полезное:

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

Вот что я сделал:

На любой Ajax-запрос мой сервер будет возвращать ответ Json 200 «NEUT TO AUTHENTICATE» (если клиент должен пройти аутентификацию).

Простой пример на 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);
        }
    };      
});

И это все.

7
Tomer 8 Апр 2016 в 23:26

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

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

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

19
morten.c 31 Май 2016 в 12:38

Используйте низкоуровневый вызов $.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';
}
52
Till 17 Окт 2008 в 00:50

Я прочитал этот вопрос и реализовал подход, который был заявлен в отношении установки ответа HTTP status code на 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.redirect и data.form. Я нашел этот подход намного лучше.

688
informatik01 29 Окт 2019 в 12:06

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

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

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

2
Vladimir Prudnikov 19 Окт 2008 в 11:13

Пытаться

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

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

12
podeig 29 Окт 2010 в 08:31

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

И еще одна важная вещь заключается в том, что нет необходимости изменять код на стороне сервера или на стороне клиента.

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

Я написал это, может быть, кто-то поможет :)

5
Ali Adlavaran 30 Июл 2015 в 06:02

Эта проблема может возникнуть при использовании метода RedirectToAction ASP.NET MVC. Чтобы форма не отображала ответ в 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 = '/ Account / Login'" , указанный выше фильтр поймает это и оценит перенаправление вместо того, чтобы позволить отображению данных.

19
Przemek Marcinkiewicz 27 Окт 2016 в 13:31

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

Мы используем WIF, который настроен на перенаправление (passiveRedirectEnabled = "true") для ответа 401. Перенаправление полезно при обработке обычных запросов, но не будет работать для запросов 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;
        }
    }

Это позволяет вам возвращать 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);
    }
});
19
Rob 1 Апр 2014 в 12:30

У меня не было никакого успеха с решением заголовка - они никогда не были подобраны в моем методе ajaxSuccess / ajaxComplete. Я использовал ответ Стег с пользовательским ответом, но немного изменил сторону 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
jocull 6 Фев 2013 в 13:45
    <script>
    function showValues() {
        var str = $("form").serialize();
        $.post('loginUser.html', 
        str,
        function(responseText, responseStatus, responseXML){
            if(responseStatus=="success"){
                window.location= "adminIndex.html";
            }
        });     
    }
</script>
13
Priyanka 10 Сен 2011 в 16:44

Я просто хотел фиксировать любые запросы 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");
}
5
Bretticus 4 Авг 2010 в 18:47

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

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

18
Timmerz 17 Авг 2010 в 18:07

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

Я в основном включил модуль 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 на мою страницу входа, который содержал URL самой страницы входа. В модуле я сейчас слушаю заголовок и делаю редирект:

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

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

33
Juri 23 Май 2012 в 10:02

Я получил рабочее решение, используя ответы @John и @Arpad ссылка и @RobWinch link

Я использую 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 - 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");
          }
      }
  });

Также удалите существующие обработчики ошибок из вызовов 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");
//            }
        });

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

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

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

2
Darren Parker 26 Мар 2018 в 18:51