如何使用spring-security和jQuery处理过期的会话?

我在我的应用程序中使用spring-security和jQuery。 主页通过Ajax动态加载内容到选项卡。 一切都很好,但有时我的选项卡中有登录页面,如果我输入凭据,我将被重定向到没有标签的内容页面。

所以我想处理这种情况。 我知道有些人使用ajax身份validation,但我不确定它是否适合我,因为它对我来说看起来很复杂,我的应用程序不允许任何访问而无需登录。 我想为所有ajax响应编写一个全局处理程序,如果我们需要进行身份validation,它将执行window.location.reload() 。 我认为在这种情况下,最好是获取401错误而不是标准登录表单,因为它更容易处理。

所以,

1)是否可以为所有jQuery ajax请求编写全局error handling程序?

2)我如何自定义spring-security的行为,以便为ajax请求发送401错误,但是常规请求会像往常一样显示标准登录页面?

3)你可能有更优雅的解决方案吗? 请分享。

谢谢。

这是一种我认为非常简单的方法。 这是我在这个网站上观察到的一系列方法。 我写了一篇关于它的博客文章:http: //yoyar.com/blog/2012/06/dealing-with-the-spring-security-ajax-session-timeout-problem/

基本思想是使用如上所述的api url前缀(即/ api / secured)以及身份validation入口点。 它很简单而且很有效。

这是validation入口点:

 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中的内容:

              

我使用了以下解决方案。

在spring security中定义了无效的session url

  

对于该页面添加了以下控制器

 @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"; } } }); 

看看http://forum.springsource.org/showthread.php?t=95881 ,我认为建议的解决方案比其他答案更清晰:

  1. 在jquery ajax调用中添加自定义标头(使用’beforeSend’挂钩)。 您还可以使用jQuery发送的“X-Requested-With”标头。
  2. 配置Spring Security以在服务器端查找该标头以返回HTTP 401错误代码,而不是将用户带到登录页面。

我刚刚想出了解决这个问题的方法,但还没有彻底测试过。 我也使用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错误。 在一个项目中,我通过在包含登录表单的iframe周围放置一个jQuery对话框来处理jQuery身份validation。

以下是我通常的做法。 在每次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; } 

所以这里有两个问题。 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); } }); 

发生超时时,在会话已清除时触发任何ajax操作后,用户将被重定向到登录页面

安全背景:

                         

登录监听器:

 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); } } } 

}