Dopo aver lottato con le numerose soluzioni pubblicate in questa risposta, per provare a far funzionare qualcosa quando si utilizza la <http>
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é utilizzo la sessione in altre parti dell'applicazione), solo che non "ricorda" affatto l'autenticazione nella sessione (dovrebbe essere ricontrollata ogni richiesta).
Per cominciare, non sono riuscito a capire come eseguire la tecnica di "implementazione nulla" descritta sopra. Non era chiaro se si supponeva di impostare securityContextRepository su null
o su un'implementazione no-op. Il primo non funziona perché NullPointerException
viene buttato dentro SecurityContextPersistenceFilter.doFilter()
. Per quanto riguarda l'implementazione no-op, ho provato a implementarla 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 alcuni strani ClassCastException
problemi con il response_
tipo.
Anche supponendo di essere riuscito a trovare un'implementazione che funziona (semplicemente non archiviando il contesto nella sessione), c'è ancora il problema di come iniettarlo nei filtri costruiti dalla <http>
configurazione. Non è possibile sostituire semplicemente il filtro nella SECURITY_CONTEXT_FILTER
posizione, come da documenti . L'unico modo che ho trovato per agganciarmi a SecurityContextPersistenceFilter
ciò che viene creato sotto le coperte è stato scrivere un brutto ApplicationContextAware
fagiolo:
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());
}
}
}
}
}
}
}
Ad ogni modo, alla soluzione che effettivamente funziona, anche se molto hacker. Usa semplicemente 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:
<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>