2016-03-07 42 views
0

我正在使用grails以及spring security和angularjs。当用户会话过期并且用户单击页面上的ajax操作时,应用程序会尝试重定向到登录页面,而原始ajax操作没有响应。在ajax请求和超时会话期间,Grails未响应401

我仍然使用传统的登录页面和我的一些应用程序仍然有一些传统的网页链接,所以当一个会话已经过期,用户点击一个链接页面,我想重定向到登录页面。

如果用户点击一个Ajax请求,我想获得一个401响应,而不是重定向的HTML响应,这样我可以在我的JavaScript进行重定向。

我有以下配置设置。

grails.plugin.springsecurity.providerNames = ['hriLoginClientAuthenticationProvider'] 
grails.plugin.springsecurity.useSecurityEventListener = true 
grails.plugin.springsecurity.failureHandler.defaultFailureUrl = '/login?error=1' 
grails.plugin.springsecurity.auth.loginFormUrl = '/login' 
grails.plugin.springsecurity.logout.postOnly = false 

什么我需要做的就是Ajax请求不定向到登录页面?

+0

您所查询的简单和更明确的答案[https://stackoverflow.com/a/47757842/4282369](https://stackoverflow.com/a/47757842/4282369) –

回答

1

我碰到了类似的问题,并已实施过滤器链来检测AJAX请求和响应与定制的HTTP状态的过滤器(你可以,如果你喜欢它更改为401)。

基本上有三个环节进行。第一个是过滤器。它是一个servlet过滤器,用于检查请求以及会话中的身份验证状态。其次,在Resources.groovy的应用程序上下文中将过滤器定义为bean。最后,将它插入到Spring安全筛选器链中,我已在Bootstrap.groovy中完成该链。

我现在就带你走过这段路。

首先Servlet过滤器(在src/java的)

package com.xyz.security; 

import java.io.IOException; 

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 org.springframework.security.authentication.AuthenticationTrustResolver; 
import org.springframework.security.authentication.AuthenticationTrustResolverImpl; 
import org.springframework.security.core.AuthenticationException; 
import org.springframework.security.core.context.SecurityContextHolder; 
import org.springframework.security.access.AccessDeniedException; 
import org.springframework.security.web.util.ThrowableAnalyzer; 
import org.springframework.security.web.util.ThrowableCauseExtractor; 
import org.springframework.web.filter.GenericFilterBean; 

public class AjaxTimeoutRedirectFilter extends GenericFilterBean { 

    // private static final Logger logger = 
    // LoggerFactory.getLogger(AjaxTimeoutRedirectFilter.class); 

    private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer(); 
    private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl(); 

    private int customSessionExpiredErrorCode = 901; 

    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, 
      FilterChain chain) throws IOException, ServletException { 
     try { 
      chain.doFilter(request, response); 

      // logger.debug("Chain processed normally"); 
     } catch (IOException ex) { 
      throw ex; 
     } catch (Exception ex) { 
      Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex); 
      RuntimeException ase = (AuthenticationException) throwableAnalyzer 
        .getFirstThrowableOfType(AuthenticationException.class, 
          causeChain); 

      if (ase == null) { 
       ase = (AccessDeniedException) throwableAnalyzer 
         .getFirstThrowableOfType(AccessDeniedException.class, 
           causeChain); 
      } 

      if (ase != null) { 
       if (ase instanceof AuthenticationException) { 
        throw ase; 
       } else if (ase instanceof AccessDeniedException) { 

        if (authenticationTrustResolver 
          .isAnonymous(SecurityContextHolder.getContext() 
            .getAuthentication())) { 
         // logger.info("User session expired or not logged in yet"); 
         String ajaxHeader = ((HttpServletRequest) request) 
           .getHeader("X-Requested-With"); 

         if ("XMLHttpRequest".equals(ajaxHeader)) { 
          // logger.info("Ajax call detected, send {} error code", 
          // this.customSessionExpiredErrorCode); 
          HttpServletResponse resp = (HttpServletResponse) response; 
          resp.sendError(this.customSessionExpiredErrorCode); 
         } else { 
          // logger.info("Redirect to login page"); 
          throw ase; 
         } 
        } else { 
         throw ase; 
        } 
       } 
      } 

     } 
    } 

    private static final class DefaultThrowableAnalyzer extends 
      ThrowableAnalyzer { 
     /** 
     * @see org.springframework.security.web.util.ThrowableAnalyzer#initExtractorMap() 
     */ 
     protected void initExtractorMap() { 
      super.initExtractorMap(); 

      registerExtractor(ServletException.class, 
        new ThrowableCauseExtractor() { 
         public Throwable extractCause(Throwable throwable) { 
          ThrowableAnalyzer.verifyThrowableHierarchy(
            throwable, ServletException.class); 
          return ((ServletException) throwable) 
            .getRootCause(); 
         } 
        }); 
     } 

    } 

    public void setCustomSessionExpiredErrorCode(
      int customSessionExpiredErrorCode) { 
     this.customSessionExpiredErrorCode = customSessionExpiredErrorCode; 
    } 
} 

其次,限定过滤器如在应用程序上下文的Bean中Resources.groovy

beans = { 
    ajaxTimeoutRedirectFilter(com.xyz.security.AjaxTimeoutRedirectFilter) 
} 

最后,得到了过滤器进入Spring Security filter chain(我为此使用BootStrap.groovy

import grails.plugin.springsecurity.SecurityFilterPosition 
import grails.plugin.springsecurity.SpringSecurityUtils 
class BootStrap { 

    def init = { servletContext -> 

     SpringSecurityUtils.clientRegisterFilter('ajaxTimeoutRedirectFilter', SecurityFilterPosition.EXCEPTION_TRANSLATION_FILTER.order + 10) 

    } 
    def destroy = { 
    } 
} 
0

当用户在客户端闲置时,您是否考虑过“锁定屏幕”?当然,你应该在服务器端处理一个会话的结束,但事实上,它似乎比等待客户端的行动更清洁和更安全的解决方案(特别是如果用户在屏幕上留下和留下一些敏感数据)。

看看这个ng-idle指令。