How can I use Spring Security without sessions?

前端 未结 7 1421
执念已碎
执念已碎 2020-11-29 15:53

I am building a web application with Spring Security that will live on Amazon EC2 and use Amazon\'s Elastic Load Balancers. Unfortunately, ELB does not support sticky sessio

相关标签:
7条回答
  • 2020-11-29 16:38

    After struggling with the numerous solutions posted in this answer, to try to get something working when using the <http> namespace config, I finally found an approach that actually works for my use case. I don't actually require that Spring Security doesn't start a session (because I use session in other parts of the application), just that it doesn't "remember" authentication in the session at all (it should be re-checked every request).

    To begin with, I wasn't able to figure out how to do the "null implementation" technique described above. It wasn't clear whether you are supposed to set the securityContextRepository to null or to a no-op implementation. The former does not work because a NullPointerException gets thrown within SecurityContextPersistenceFilter.doFilter(). As for the no-op implementation, I tried implementing in the simplest way I could imagine:

    public class NullSpringSecurityContextRepository implements SecurityContextRepository {
    
        @Override
        public SecurityContext loadContext(final HttpRequestResponseHolder requestResponseHolder_) {
            return SecurityContextHolder.createEmptyContext();
        }
    
        @Override
        public void saveContext(final SecurityContext context_, final HttpServletRequest request_,
                final HttpServletResponse response_) {
        }
    
        @Override
        public boolean containsContext(final HttpServletRequest request_) {
            return false;
        }
    
    }
    

    This doesn't work in my application, because of some strange ClassCastException having to do with the response_ type.

    Even assuming I did manage to find an implementation that works (by simply not storing the context in session), there is still the problem of how to inject that into the filters built by the <http> configuration. You cannot simply replace the filter at the SECURITY_CONTEXT_FILTER position, as per the docs. The only way I found to hook into the SecurityContextPersistenceFilter that is created under the covers was to write an ugly ApplicationContextAware bean:

    public class SpringSecuritySessionDisabler implements ApplicationContextAware {
    
        private final Logger logger = LoggerFactory.getLogger(SpringSecuritySessionDisabler.class);
    
        private ApplicationContext applicationContext;
    
        @Override
        public void setApplicationContext(final ApplicationContext applicationContext_) throws BeansException {
            applicationContext = applicationContext_;
        }
    
        public void disableSpringSecuritySessions() {
            final Map<String, FilterChainProxy> filterChainProxies = applicationContext
                    .getBeansOfType(FilterChainProxy.class);
            for (final Entry<String, FilterChainProxy> filterChainProxyBeanEntry : filterChainProxies.entrySet()) {
                for (final Entry<String, List<Filter>> filterChainMapEntry : filterChainProxyBeanEntry.getValue()
                        .getFilterChainMap().entrySet()) {
                    final List<Filter> filterList = filterChainMapEntry.getValue();
                    if (filterList.size() > 0) {
                        for (final Filter filter : filterList) {
                            if (filter instanceof SecurityContextPersistenceFilter) {
                                logger.info(
                                        "Found SecurityContextPersistenceFilter, mapped to URL '{}' in the FilterChainProxy bean named '{}', setting its securityContextRepository to the null implementation to disable caching of authentication",
                                        filterChainMapEntry.getKey(), filterChainProxyBeanEntry.getKey());
                                ((SecurityContextPersistenceFilter) filter).setSecurityContextRepository(
                                 new NullSpringSecurityContextRepository());
                            }
                        }
                    }
    
                }
            }
        }
    }
    

    Anyway, to the solution that actually does work, albeit very hackish. Simply use a Filter that deletes the session entry that the HttpSessionSecurityContextRepository looks for when it does its thing:

    public class SpringSecuritySessionDeletingFilter extends GenericFilterBean implements Filter {
    
        @Override
        public void doFilter(final ServletRequest request_, final ServletResponse response_, final FilterChain chain_)
                throws IOException, ServletException {
            final HttpServletRequest servletRequest = (HttpServletRequest) request_;
            final HttpSession session = servletRequest.getSession();
            if (session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY) != null) {
                session.removeAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
            }
    
            chain_.doFilter(request_, response_);
        }
    }
    

    Then in the configuration:

    <bean id="springSecuritySessionDeletingFilter"
        class="SpringSecuritySessionDeletingFilter" />
    
    <sec:http auto-config="false" create-session="never"
        entry-point-ref="authEntryPoint">
        <sec:intercept-url pattern="/**"
            access="IS_AUTHENTICATED_REMEMBERED" />
        <sec:intercept-url pattern="/static/**" filters="none" />
        <sec:custom-filter ref="myLoginFilterChain"
            position="FORM_LOGIN_FILTER" />
    
        <sec:custom-filter ref="springSecuritySessionDeletingFilter"
            before="SECURITY_CONTEXT_FILTER" />
    </sec:http>
    
    0 讨论(0)
提交回复
热议问题