Grails isn't responding with a 401 during ajax request and timed out session

后端 未结 2 2103
被撕碎了的回忆
被撕碎了的回忆 2020-12-22 12:46

I\'m using grails along with spring security and angularjs. When a user session has expired and the user clicks an ajax action on the page, rather than respond with a 401, t

相关标签:
2条回答
  • 2020-12-22 13:01

    Did you consider "locking a screen" when the user is idle on a client-side? Of course you should handle end of a session on server-side but in fact it seems even cleaner and more secure solution than waiting for an action from client side (especially if user has left and left on a screen some sensitive data).

    Check out this ng-idle directive.

    0 讨论(0)
  • 2020-12-22 13:17

    I've run into a similar issue and have implemented a filter in the filter chain to detect AJAX requests and respond with a customized HTTP status (you can change it to 401 if you like).

    Basically there are three parts to this. The first, is the filter. It's a servlet filter and examines the request as well as the state of the authentication in the session. Second, defining the filter as a bean within the application context in Resources.groovy. Finally, inserting it into the Spring Security filter chain, which I've done in Bootstrap.groovy.

    I'll walk you through this now.

    First the servlet filter (under 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;
        }
    }
    

    Second, defining the filter as a bean in the application context in Resources.groovy

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

    And finally, getting the filter into the Spring Security filter chain (I used BootStrap.groovy for this)

    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 讨论(0)
提交回复
热议问题