编辑:从 Spring Security 3.1 开始,有一个 STATELESS 选项可以用来代替所有这些。查看其他答案。原始答案保留在下面以供后代使用。
在与此答案中发布的众多解决方案作斗争之后,为了尝试在使用 <http> 命名空间配置时使某些东西正常工作,我终于找到了一种真正适用于我的用例的方法。我实际上并不要求 Spring Security 不启动会话(因为我在应用程序的其他部分使用会话),只是它根本不“记住”会话中的身份验证(应该重新检查每个请求)。
首先,我无法弄清楚如何执行上述“空实现”技术。目前尚不清楚您是否应该将 securityContextRepository 设置为 null 或无操作实现。前者不起作用,因为NullPointerException 在SecurityContextPersistenceFilter.doFilter() 中被抛出。至于无操作实现,我尝试以我能想象到的最简单的方式实现:
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;
}
}
这在我的应用程序中不起作用,因为 ClassCastException 与 response_ 类型有关。
即使假设我确实设法找到了一个可行的实现(通过简单地不在会话中存储上下文),仍然存在如何将其注入由<http> 配置构建的过滤器的问题。根据docs,您不能简单地更换SECURITY_CONTEXT_FILTER 位置的过滤器。我发现挂钩在幕后创建的SecurityContextPersistenceFilter 的唯一方法是编写一个丑陋的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());
}
}
}
}
}
}
}
无论如何,对于实际有效的解决方案,尽管非常老套。只需使用Filter 删除HttpSessionSecurityContextRepository 在执行操作时查找的会话条目:
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_);
}
}
然后在配置中:
<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>