Подтвердить что ты не робот

Как обрабатывать истекший сеанс с помощью spring -security и jQuery?

Я использую spring -security и jQuery в своем приложении. На главной странице динамически загружается контент на вкладки через Ajax. И все в порядке, однако иногда у меня есть страница входа в мою вкладку, и если я набираю учетные данные, я буду перенаправлен на страницу контента без вкладок.

Поэтому я бы хотел справиться с этой ситуацией. Я знаю, что некоторые люди используют аутентификацию ajax, но я не уверен, что это подходит для меня, потому что это выглядит довольно сложно для меня, и мое приложение не разрешает доступ без входа в систему раньше. Я хотел бы просто написать глобальный обработчик для всех ответов ajax, которые будут выполнять window.location.reload(), если нам нужно пройти аутентификацию. Я думаю, что в этом случае лучше получить ошибку 401 вместо стандартной формы входа, потому что ее легче обрабатывать.

Итак,

1) Можно ли написать глобальный обработчик ошибок для всех jQuery ajax-запросов?

2) Как настроить поведение spring -security для отправки ошибки 401 для ajax-запросов, но для обычных запросов, чтобы показать стандартную страницу входа в систему как обычно?

3) Может быть, у вас есть более изящное решение? Пожалуйста, поделитесь им.

Спасибо.

4b9b3361

Ответ 1

Здесь подход, который я считаю довольно простым. Это сочетание подходов, которые я наблюдал на этом сайте. Я написал сообщение в блоге об этом: http://yoyar.com/blog/2012/06/dealing-with-the-spring-security-ajax-session-timeout-problem/

Основная идея заключается в использовании префикса api url (т.е./api/secure), как предложено выше, и точки входа аутентификации. Это просто и работает.

Здесь точка входа аутентификации:

package com.yoyar.yaya.config;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;

public class AjaxAwareAuthenticationEntryPoint 
             extends LoginUrlAuthenticationEntryPoint {

    public AjaxAwareAuthenticationEntryPoint(String loginUrl) {
        super(loginUrl);
    }

    @Override
    public void commence(
        HttpServletRequest request, 
        HttpServletResponse response, 
        AuthenticationException authException) 
            throws IOException, ServletException {

        boolean isAjax 
            = request.getRequestURI().startsWith("/api/secured");

        if (isAjax) {
            response.sendError(403, "Forbidden");
        } else {
            super.commence(request, response, authException);
        }
    }
}

И вот что входит в ваш контекст spring xml:

<bean id="authenticationEntryPoint"
  class="com.yoyar.yaya.config.AjaxAwareAuthenticationEntryPoint">
    <constructor-arg name="loginUrl" value="/login"/>
</bean>

<security:http auto-config="true"
  use-expressions="true"
  entry-point-ref="authenticationEntryPoint">
    <security:intercept-url pattern="/api/secured/**" access="hasRole('ROLE_USER')"/>
    <security:intercept-url pattern="/login" access="permitAll"/>
    <security:intercept-url pattern="/logout" access="permitAll"/>
    <security:intercept-url pattern="/denied" access="hasRole('ROLE_USER')"/>
    <security:intercept-url pattern="/" access="permitAll"/>
    <security:form-login login-page="/login"
                         authentication-failure-url="/loginfailed"
                         default-target-url="/login/success"/>
    <security:access-denied-handler error-page="/denied"/>
    <security:logout invalidate-session="true"
                     logout-success-url="/logout/success"
                     logout-url="/logout"/>
</security:http>

Ответ 2

Я использовал следующее решение.

В spring безопасности определен неверный URL-адрес сеанса

<security:session-management invalid-session-url="/invalidate.do"/>

Для этой страницы добавлен следующий контроллер

@Controller
public class InvalidateSession
{
    /**
     * This url gets invoked when spring security invalidates session (ie timeout).
     * Specific content indicates ui layer that session has been invalidated and page should be redirected to logout. 
     */
    @RequestMapping(value = "invalidate.do", method = RequestMethod.GET)
    @ResponseBody
    public String invalidateSession() {
        return "invalidSession";
    }
}

И для ajax используется ajaxSetup для обработки всех запросов ajax:

// Checks, if data indicates that session has been invalidated.
// If session is invalidated, page is redirected to logout
   $.ajaxSetup({
    complete: function(xhr, status) {
                if (xhr.responseText == 'invalidSession') {
                    if ($("#colorbox").count > 0) {
                        $("#colorbox").destroy();
                    }
                    window.location = "logout";
                }
            }
        });

Ответ 3

Взгляните на http://forum.springsource.org/showthread.php?t=95881, я думаю, что предлагаемое решение намного яснее, чем другие ответы здесь:

  • Добавить пользовательский заголовок в ваши jquery-вызовы ajax (используя крюк "beforeSend" ). Вы также можете использовать заголовок "X-Requested-With", который отправляет jQuery.
  • Настроить Spring Безопасность для поиска этого заголовка на стороне сервера, чтобы вернуть код ошибки HTTP 401 вместо того, чтобы перевести пользователя на страницу входа.

Ответ 4

Я только придумал решение этой проблемы, но не проверил ее полностью. Я также использую spring, spring безопасность и jQuery. Во-первых, из моего контроллера входа я установил код состояния 401:

LoginController {

public ModelAndView loginHandler(HttpServletRequest request, HttpServletResponse response) {

...
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
... 
return new ModelAndView("login", model);
}

В своих методах onload() все мои страницы вызывают функцию в моем глобальном файле javascript:

function initAjaxErrors() {

jQuery(window).ajaxError(function(event, xmlHttpRequest, ajaxOptions, thrownError) {
    if (403 == xmlHttpRequest.status)
        showMessage("Permission Denied");
    else
        showMessage("An error occurred: "+xmlHttpRequest.status+" "+xmlHttpRequest.statusText);
});

}

В этот момент вы можете обрабатывать ошибку 401 любым способом. В одном проекте я обработал аутентификацию jQuery, установив диалог jQuery вокруг iframe, содержащего форму входа.

Ответ 5

Вот как я обычно это делаю. На каждом вызове AJAX проверьте результат перед его использованием.

$.ajax({ type: 'GET',
    url: GetRootUrl() + '/services/dosomething.ashx',
    success: function (data) {
      if (HasErrors(data)) return;

      // process data returned...

    },
    error: function (xmlHttpRequest, textStatus) {
      ShowStatusFailed(xmlHttpRequest);
    }
  });

И тогда функция HasErrors() выглядит так и может использоваться на всех страницах.

function HasErrors(data) {
  // check for redirect to login page
  if (data.search(/login\.aspx/i) != -1) {
    top.location.href = GetRootUrl() + '/login.aspx?lo=TimedOut';
    return true;
  }
  // check for IIS error page
  if (data.search(/Internal Server Error/) != -1) {
    ShowStatusFailed('Server Error.');
    return true;
  }
  // check for our custom error handling page
  if (data.search(/Error.aspx/) != -1) {
    ShowStatusFailed('An error occurred on the server. The Technical Support Team has been provided with the error details.');
    return true;
  }
  return false;
}

Ответ 6

Итак, здесь есть две проблемы. 1) Безопасность Spring работает, но ответ возвращается в браузер при вызове ajax. 2) Spring безопасность отслеживает первоначально запрошенную страницу, чтобы она могла перенаправить вас к ней ПОСЛЕ того, как вы входите в систему (если вы не укажете, что вы всегда хотите использовать определенную страницу после входа в систему). В этом случае запрос был строкой Ajax, поэтому вы будете перенаправлены на эту строку, и это то, что вы увидите в браузере.

Простым решением является обнаружение ошибки Ajax, и если запрос, отправленный назад, специфичен для вашей страницы входа (Spring отправит обратно страницу входа в систему html, это будет свойство responseText запроса) Это. Затем просто перезагрузите свою текущую страницу, которая удалит пользователя из контекста вызова Ajax. Spring затем автоматически отправит их на страницу входа. (Я использую имя j_username по умолчанию, которое является строковым значением, уникальным для моей страницы входа).

$(document).ajaxError( function(event, request, settings, exception) {
    if(String.prototype.indexOf.call(request.responseText, "j_username") != -1) {
        window.location.reload(document.URL);
    }
});

Ответ 7

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

контекст безопасности:

<http use-expressions="true" entry-point-ref="authenticationEntryPoint">
    <logout invalidate-session="true" success-handler-ref="logoutSuccessBean" delete-cookies="JSESSIONID" />
    <custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
    <custom-filter position="FORM_LOGIN_FILTER" ref="authFilter" />
    <session-management invalid-session-url="/logout.xhtml" session-authentication-strategy-ref="sas"/>
</http>

<beans:bean id="concurrencyFilter"
  class="org.springframework.security.web.session.ConcurrentSessionFilter">
    <beans:property name="sessionRegistry" ref="sessionRegistry" />
    <beans:property name="expiredUrl" value="/logout.xhtml" />
</beans:bean>

<beans:bean id="authenticationEntryPoint"  class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
    <beans:property name="loginFormUrl" value="/login.xhtml" />
</beans:bean>

<beans:bean id="authFilter"
  class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
    <beans:property name="sessionAuthenticationStrategy" ref="sas" />
    <beans:property name="authenticationManager" ref="authenticationManager" />
    <beans:property name="authenticationSuccessHandler" ref="authenticationSuccessBean" />
    <beans:property name="authenticationFailureHandler" ref="authenticationFailureBean" />
</beans:bean>

<beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
    <beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
    <beans:property name="maximumSessions" value="1" />
    <beans:property name="exceptionIfMaximumExceeded" value="1" />
</beans:bean>

Вход слушателя:

public class LoginListener implements PhaseListener {

@Override
public PhaseId getPhaseId() {
    return PhaseId.RESTORE_VIEW;
}

@Override
public void beforePhase(PhaseEvent event) {
    // do nothing
}

@Override
public void afterPhase(PhaseEvent event) {
    FacesContext context = event.getFacesContext();
    HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
    String logoutURL = request.getContextPath() + "/logout.xhtml";
    String loginURL = request.getContextPath() + "/login.xhtml";

    if (logoutURL.equals(request.getRequestURI())) {
        try {
            context.getExternalContext().redirect(loginURL);
        } catch (IOException e) {
            throw new FacesException(e);
        }
    }
}

}