Después de luchar con las numerosas soluciones publicadas en esta respuesta, para intentar que algo funcione al usar el <http>
configuración espacio de nombres, finalmente encontré un enfoque que realmente funciona para mi caso de uso. En realidad, no requiero que Spring Security no inicie una sesión (porque uso la sesión en otras partes de la aplicación), solo que no "recuerda" la autenticación en la sesión en absoluto (debe volver a verificarse cada solicitud).
Para empezar, no pude averiguar cómo hacer la técnica de "implementación nula" descrita anteriormente. No estaba claro si se suponía que debía configurar securityContextRepository en null
o en una implementación sin operación. El primero no funciona porque NullPointerException
se tira a dentro SecurityContextPersistenceFilter.doFilter()
. En cuanto a la implementación sin operación, intenté implementar de la manera más simple que pude imaginar:
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;
}
}
Esto no funciona en mi aplicación, debido a algo extraño que ClassCastException
tiene que ver con el response_
tipo.
Incluso asumiendo que logré encontrar una implementación que funcione (simplemente no almacenando el contexto en la sesión), todavía existe el problema de cómo inyectar eso en los filtros construidos por la <http>
configuración. No puede simplemente reemplazar el filtro en la SECURITY_CONTEXT_FILTER
posición, según los documentos . La única forma que encontré de conectarme al SecurityContextPersistenceFilter
que se crea debajo de las cubiertas fue escribir un ApplicationContextAware
frijol feo :
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());
}
}
}
}
}
}
}
De todos modos, a la solución que realmente funciona, aunque muy pirateada. Simplemente use un Filter
que borre la entrada de sesión que HttpSessionSecurityContextRepository
busca cuando hace lo suyo:
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_);
}
}
Luego en la configuración:
<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>