在 Spring MVC 中检测 Ajax 请求中的会话超时
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4964145/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Detect Session Timeout in Ajax Request in Spring MVC
提问by Mike Flynn
I can't see seem to find a good example/answer on how to send back some data from an ajax request when a session has timed out. It sends back the login page HTML and I want to either send json or a status code I can intercept.
我似乎找不到一个很好的示例/答案,说明如何在会话超时时从 ajax 请求发回一些数据。它发回登录页面 HTML,我想发送 json 或我可以拦截的状态代码。
采纳答案by Boris Kirzner
The simplest way for doing this is using a filter on URLs of your AJAX requests.
执行此操作的最简单方法是对 AJAX 请求的 URL 使用过滤器。
In the example below I'm just sending HTTP 500 response code with a response body indicating the session timeout, but you can easily set the response code and body to what is more suitable for your case..
在下面的示例中,我只是发送带有指示会话超时的响应正文的 HTTP 500 响应代码,但您可以轻松地将响应代码和正文设置为更适合您的情况。
package com.myapp.security.authentication;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ExpiredSessionFilter extends GenericFilterBean {
static final String FILTER_APPLIED = "__spring_security_expired_session_filter_applied";
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (request.getAttribute(FILTER_APPLIED) != null) {
chain.doFilter(request, response);
return;
}
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "SESSION_TIMED_OUT");
return;
}
chain.doFilter(request, response);
}
}
回答by Matt Friedman
Here's an approach that I think is quite simple. It's a combination of approaches that I've observed on this site. I wrote a blog post about it: http://yoyar.com/blog/2012/06/dealing-with-the-spring-security-ajax-session-timeout-problem/
这是一种我认为非常简单的方法。这是我在本网站上观察到的方法的组合。我写了一篇关于它的博客文章:http: //yoyar.com/blog/2012/06/dealing-with-the-spring-security-ajax-session-timeout-problem/
The basic idea is to use an api url prefix (i.e. /api/secured) as suggested above along with an authentication entry point. It's simple and works.
基本思想是使用上面建议的 api url 前缀(即 /api/secured)以及身份验证入口点。这很简单而且有效。
Here's the authentication entry point:
这是身份验证入口点:
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);
}
}
}
And here's what goes in your spring context xml:
这是您的 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>
回答by bnguyen82
I use the same solution by @Matt in backend. If you're using angularJs on front end, add below interceptor in angular $http to let browser actually do a redirect to login page.
我在后端使用@Matt 的相同解决方案。如果您在前端使用 angularJs,请在 angular $http 中添加下面的拦截器,让浏览器实际重定向到登录页面。
var HttpInterceptorModule = angular.module('httpInterceptor', [])
.config(function ($httpProvider) {
$httpProvider.interceptors.push('myInterceptor');
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
})
.factory('myInterceptor', function ($q) {
return {
'responseError': function(rejection) {
// do something on error
if(rejection.status == 403 || rejection.status == 401) window.location = "login";
return $q.reject(rejection);
}
};
});
});
Note that below line is needed only if you're using AngularJs after version 1.1.1 (angularJS removed header "X-Requested-With" from that version onward)
请注意,仅当您在 1.1.1 版之后使用 AngularJs 时才需要以下行(angularJS 从该版本开始删除标头“X-Requested-With”)
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
回答by pedorro
Seeing as all of the present answers are a few years old now, I'll share my solution which I currently have working in a Spring Boot REST application:
鉴于目前所有的答案都已经有几年了,我将分享我目前在 Spring Boot REST 应用程序中使用的解决方案:
@Configuration
@EnableWebSecurity
public class UISecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
...
http.exceptionHandling.authenticationEntryPoint(authenticationEntryPoint());
...
}
private AuthenticationEntryPoint authenticationEntryPoint() {
// As a REST service there is no 'authentication entry point' like MVC which can redirect to a login page
// Instead just reply with 401 - Unauthorized
return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
}
}
The basic premise here is that I override the authentication entry point which by default was issuing a redirect to my non-existent login page. It now responds by sending a 401. Spring also implicitly creates an standard error response JSON object that it returns as well.
这里的基本前提是我覆盖了身份验证入口点,默认情况下它会重定向到我不存在的登录页面。它现在通过发送 401 进行响应。Spring 还隐式创建了一个标准错误响应 JSON 对象,它也返回该对象。

