2016-03-07 41 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指令。