Depois de lutar com as inúmeras soluções postadas nesta resposta, para tentar fazer algo funcionar ao usar a <http>
configuração do namespace, finalmente encontrei uma abordagem que realmente funciona para meu caso de uso. Na verdade, não exijo que o Spring Security não inicie uma sessão (porque eu uso a sessão em outras partes do aplicativo), apenas que ele não "lembre" de autenticação na sessão (deve ser verificado novamente cada pedido).
Para começar, não consegui descobrir como fazer a técnica de "implementação nula" descrita acima. Não estava claro se você deveria definir securityContextRepository como null
ou como uma implementação autônoma. O primeiro não funciona porque um NullPointerException
é jogado dentro SecurityContextPersistenceFilter.doFilter()
. Quanto à implementação no-op, tentei implementar da maneira mais simples que poderia 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;
}
}
Isso não funciona no meu aplicativo, por causa de alguma coisa estranha ClassCastException
relacionada ao response_
tipo.
Mesmo supondo que consegui encontrar uma implementação que funcione (simplesmente não armazenando o contexto na sessão), ainda há o problema de como injetar isso nos filtros construídos pela <http>
configuração. Você não pode simplesmente substituir o filtro na SECURITY_CONTEXT_FILTER
posição, conforme os documentos . A única maneira que encontrei de me conectar com o SecurityContextPersistenceFilter
que é criado sob as cobertas foi escrever um ApplicationContextAware
feijão feio :
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 qualquer forma, para a solução que realmente funciona, embora muito hackeada. Basta usar um Filter
que exclua a entrada de sessão que HttpSessionSecurityContextRepository
procura quando faz o que deve:
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_);
}
}
Então, na configuração:
<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>