it-swarm.com.ru

Как обработать истекший сеанс с использованием Spring-Security и JQuery?

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

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

Так,

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

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

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

Благодарю.

22
viator

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

А вот что происходит в вашем весеннем контексте 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>
10
Matt Friedman

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

В весенней безопасности определен неверный 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";
                }
            }
        });
9
andro83

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

  1. Добавьте пользовательский заголовок в ваши вызовы jquery ajax (используя хук beforeSend). Вы также можете использовать заголовок «X-Requested-With», который отправляет jQuery.
  2. Сконфигурируйте Spring Security для поиска этого заголовка на стороне сервера, чтобы он возвращал код ошибки HTTP 401 вместо того, чтобы перевести пользователя на страницу входа.
4
Guido

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

3
Hank

Вот как я обычно это делаю. При каждом вызове 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;
}
2
Glen Little

Итак, здесь есть 2 проблемы. 1) Безопасность Spring работает, но ответ возвращается в браузер в виде ajax-вызова. 2) Spring Security отслеживает изначально запрошенную страницу, чтобы она могла перенаправить вас на нее ПОСЛЕ входа в систему (если вы не укажете, что всегда хотите использовать определенную страницу после входа в систему). В этом случае запрос был строкой 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);
    }
});
0
MattC

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

}

0
Thomas Crook