Spring安全性+ Ajax会话超时问题

时间:2014-05-28 02:35:59

标签: ajax spring spring-mvc spring-security

我有一个使用Spring MVC的应用程序构建并使用Spring安全保护,一堆控制器是JSON休息服务,都受到保护。如果发生会话超时,我使用LoginUrlAuthenticationEntryPoint来检测AJAX请求并发送403 错误代码 - 所有其他请求只会被重定向回登录页面。

下面是Spring Security XML代码段和authenticationEntryPoint java类。

问题是会话超时后的第一个AJAX请求,Spring重定向到登录页面并返回登录页面HTML,如果我再次尝试执行AJAX请求(重定向发生后),则执行authenticationEntryPoint并返回HTTP错误代码403 。我使用这种机制http://distigme.wordpress.com/2012/11/01/ajax-and-spring-security-form-based-login/尝试了同样的事情并且发生了同样的事情(在第一次AJAX请求时发生重定向,所有后续的AJAX请求都返回HTTP 403)。我不想让Redirect到登录页面来查看会话超时的AJAX请求。

有什么想法吗?

<beans:bean id="authenticationEntryPoint"  class="mojo.ocs.web.AjaxAwareAuthenticationEntryPoint">
    <beans:constructor-arg name="loginUrl" value="/login"/>
</beans:bean>
<!-- ENTRY POINT REF IMPLEMENTATION -->
<http auto-config="true" use-expressions="true" access-denied-page="/accessdenied" entry-point-ref="authenticationEntryPoint">
    <intercept-url pattern="/login" access="isAnonymous()"/>
    <intercept-url pattern="/loginfailed" access="isAnonymous()"/>
    <intercept-url pattern="/welcome" access="isAuthenticated()" />
    <intercept-url pattern="/" access="isAuthenticated()" />
    <intercept-url pattern="/private_res/**" access="isAuthenticated()" />
    <intercept-url pattern="/tne/**" access="isAuthenticated()" />
    <intercept-url pattern="/team_reports/**" access="isAuthenticated()" />
    <form-login login-page="/login" default-target-url="/welcome" always-use-default-target="true" authentication-failure-url="/loginfailed" />
    <logout delete-cookies="JSESSIONID"  logout-success-url="/logout" invalidate-session="true"/>
    <session-management invalid-session-url="/login" />
</http>

这是LoginAuthenticationEntryPoint:

public class AjaxAwareAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint     
{
    public AjaxAwareAuthenticationEntryPoint(String loginUrl) {
        super(loginUrl);
    }

    @Override
    public void commence(
        HttpServletRequest request,
        HttpServletResponse response,
        AuthenticationException authException)
        throws IOException, ServletException {
        String ajaxHeader = ((HttpServletRequest) request).getHeader("X-Requested-With");
        boolean isAjax = "XMLHttpRequest".equals(ajaxHeader);
        if (isAjax) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "Ajax REquest Denied (Session Expired)");
        } else {
            super.commence(request, response, authException);
        }
    }
}

3 个答案:

答案 0 :(得分:5)

确认使用Spring Boot以及Spring安全性以编程方式设置安全性而无需任何必需的XML,这确实很好用,例如:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/admin**").hasRole("ADMIN")
            // everything else
            .anyRequest().fullyAuthenticated()
        .and()
            .exceptionHandling().authenticationEntryPoint(new AjaxAwareAuthenticationEntryPoint("/login"));
}

答案 1 :(得分:0)

我通过自己的自定义过滤器实现此问题,将其置于ANONYMOUS_FILTER之前,如果Spring主体不存在则返回403。

答案 2 :(得分:0)

我意识到这是很久以前的事了,但我想把它放在这里,看看它是否有助于其他人。

我在http://distigme.wordpress.com/2012/11/01/ajax-and-spring-security-form-based-login/中遵循了相同的想法,并且遇到了同样的问题,即第一个返回的内容是登录页面,下一个是HTTP 403。

我认为这是Spring的一部分,我们在Spring XML配置执行所有操作之间达成了分歧,或者我们编写了一堆代码来重载它可以为我们做些什么。我更喜欢在XML配置中尽我所能。

我的解决方案是让XML配置像博客一样抛出403错误。我没有编写Matching课程,因为我的工作流程要求返回第一页,因此我不使用org.springframework.security.web.savedrequest.HttpSessionRequestCache

<bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
    <constructor-arg name="loginFormUrl" value="/index.html" />
</bean>

<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint">
    <constructor-arg>
        <map>
            <entry key="!hasHeader('X-Requested-With','XMLHttpRequest')" value-ref="loginUrlAuthenticationEntryPoint" />
        </map>
    </constructor-arg>
    <property name="defaultEntryPoint">
        <bean class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint" />
    </property>
</bean>

如果我不想在其他地方使用它,我是嵌套豆子的忠实粉丝。在我的$.ajax电话中,我提出了

dataType: 'json'

确保如果返回的内容不是JSON(例如登录页面),则调用error函数。这也将捕获403错误。

error: function (xhr, textStatus, errorThrown) {
    if (xhr.status == 403 || textStatus == 'parsererror' && xhr.responseText.match('rememberMe').length > 0) {
        alert('Your session has timed out.');
        window.location = '<c:url value="/index.html" />';
    } else
        alert('Something went wrong. ' + xhr.status + ': ' + errorThrown);

}

我正在搜索rememberMe文字,以确保它是登录页面。我不希望在任何其他页面上。