Come posso usare Spring Security senza sessioni?

Sto realizzando un’applicazione web con Spring Security che vivrà su Amazon EC2 e utilizzerò gli Elastic Load Balancer di Amazon. Sfortunatamente, ELB non supporta sessioni persistenti, quindi devo assicurarmi che la mia applicazione funzioni correttamente senza sessioni.

Finora, ho configurato RememberMeServices per assegnare un token tramite un cookie, e questo funziona bene, ma voglio che il cookie scada con la sessione del browser (ad esempio quando il browser si chiude).

Devo immaginare che non sono il primo a voler usare Spring Security senza sessioni … qualche suggerimento?

In Spring Security 3 con Java Config , puoi usare HttpSecurity.sessionManagement () :

@Override protected void configure(final HttpSecurity http) throws Exception { http .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); } 

Abbiamo lavorato sullo stesso problema (iniettando un SecurityContextRepository personalizzato a SecurityContextPersistenceFilter) per 4-5 ore oggi. Alla fine, l’abbiamo capito. Prima di tutto, nella sezione 8.3 di Spring Security rif. doc, c’è una definizione del bean SecurityContextPersistenceFilter

        

E dopo questa definizione, c’è questa spiegazione: “In alternativa potresti fornire un’implementazione nulla dell’interfaccia SecurityContextRepository, che impedirà l’archiviazione del contesto di sicurezza, anche se una sessione è già stata creata durante la richiesta.”

Dovevamo iniettare il nostro SecurityContextRepository personalizzato in SecurityContextPersistenceFilter. Quindi abbiamo semplicemente modificato la definizione del bean in alto con la nostra impl personalizzata e l’abbiamo inserita nel contesto di sicurezza.

Quando eseguiamo l’applicazione, abbiamo tracciato i log e visto che SecurityContextPersistenceFilter non stava usando il nostro impl personalizzato, stava usando HttpSessionSecurityContextRepository.

Dopo alcune altre cose che abbiamo provato, abbiamo capito che dovevamo fornire il nostro implettivo SecurityContextRepository personalizzato con l’attributo “security-context-repository-ref” dello spazio dei nomi “http”. Se si utilizza lo spazio dei nomi “http” e si desidera iniettare il proprio impl di SecurityContextRepository, provare l’attributo “security-context-repository-ref”.

Quando viene utilizzato lo spazio dei nomi “http”, viene ignorata una definizione SecurityContextPersistenceFilter separata. Come ho copiato sopra, il documento di riferimento. non lo dice

Per favore correggimi se ho frainteso le cose.

Sembra essere ancora più semplice in Spring Securitiy 3.0. Se stai usando la configurazione del namespace, puoi semplicemente fare come segue:

    

Oppure puoi configurare SecurityContextRepository come null, e nulla verrebbe mai salvato in questo modo.

Dai un’occhiata alla class SecurityContextPersistenceFilter . Definisce come viene popolato SecurityContextHolder . Per impostazione predefinita utilizza HttpSessionSecurityContextRepository per archiviare il contesto di sicurezza nella sessione http.

Ho implementato questo meccanismo abbastanza facilmente, con SecurityContextRepository personalizzato.

Vedi securityContext.xml seguito:

                                                                                                 

In realtà create-session="never" non significa essere completamente apolidi. C’è un problema nella gestione dei problemi di Spring Security.

Solo una breve nota: è “creare-sessione” piuttosto che “creare-sessioni”

creare-session

Controlla l’entusiasmo con cui viene creata una sessione HTTP.

Se non impostato, il valore predefinito è “ifRequired”. Altre opzioni sono “sempre” e “mai”.

L’impostazione di questo attributo influisce sulle proprietà allowSessionCreation e forceEagerSessionCreation di HttpSessionContextIntegrationFilter. allowSessionCreation sarà sempre true a meno che questo attributo sia impostato su “never”. forceEagerSessionCreation è “false” a meno che non sia impostato su “always”.

Quindi la configurazione predefinita consente la creazione della sessione ma non la forza. L’eccezione è se il controllo simultaneo della sessione è abilitato, quando forceEagerSessionCreation sarà impostato su true, indipendentemente da quale sia l’impostazione qui. L’utilizzo di “mai” causerebbe quindi un’eccezione durante l’inizializzazione di HttpSessionContextIntegrationFilter.

Per dettagli specifici sull’uso della sessione, vi è una buona documentazione in HttpSessionSecurityContextRepository javadoc.

Dopo aver lottato con le numerose soluzioni pubblicate in questa risposta, per cercare di ottenere qualcosa lavorando quando si utilizza la configurazione dello spazio dei nomi , ho finalmente trovato un approccio che funziona effettivamente per il mio caso d’uso. In realtà non richiedo che Spring Security non avvii una sessione (perché uso la sessione in altre parti dell’applicazione), solo che non “ricorda” l’autenticazione nella sessione (dovrebbe essere ricontrollata ogni richiesta).

Per cominciare, non ero in grado di capire come eseguire la tecnica di “implementazione nulla” descritta sopra. Non è chiaro se si debba impostare securityContextRepository su null o su un’implementazione no-op. Il primo non funziona perché viene generata una NullPointerException all’interno di SecurityContextPersistenceFilter.doFilter() . Per quanto riguarda l’implementazione no-op, ho provato a implementare nel modo più semplice che potessi immaginare:

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

Questo non funziona nella mia applicazione, a causa di alcune strane ClassCastException hanno a che fare con il tipo response_ .

Anche supponendo di essere riuscito a trovare un’implementazione che funziona (semplicemente non memorizzando il contesto in sessione), c’è ancora il problema di come inserirlo nei filtri creati dalla configurazione . Non puoi semplicemente sostituire il filtro nella posizione SECURITY_CONTEXT_FILTER , come da documentazione . L’unico modo in cui ho trovato aghook nel SecurityContextPersistenceFilter creato sotto le copertine era scrivere un brutto bean ApplicationContextAware :

 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 filterChainProxies = applicationContext .getBeansOfType(FilterChainProxy.class); for (final Entry filterChainProxyBeanEntry : filterChainProxies.entrySet()) { for (final Entry> filterChainMapEntry : filterChainProxyBeanEntry.getValue() .getFilterChainMap().entrySet()) { final List 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()); } } } } } } } 

Ad ogni modo, per la soluzione che effettivamente funziona, anche se molto hackerata. Basta usare un Filter che cancella la voce di sessione che HttpSessionSecurityContextRepository cerca quando fa la sua cosa:

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

Quindi nella configurazione: