После того, как вы столкнулись с многочисленными решениями, опубликованными в этом ответе, чтобы попытаться получить что-то, работающее при использовании конфигурации пространства имен <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>
. Вы не можете просто заменить фильтр на позицию SECURITY_CONTEXT_FILTER
, согласно docs. Единственный способ я нашел, чтобы вклиниться в SecurityContextPersistenceFilter
, который создается под одеялом было писать уродливый 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<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>
Это не сработало, как я думал. Вместо этого есть комментарий ниже, который различает «никогда» и «без гражданства». Используя «никогда», мое приложение все еще создавало сеансы. Используя «stateless», мое приложение фактически оставалось без гражданства, и мне не нужно было реализовывать какие-либо переопределения, упомянутые в других ответах. См. Вопрос JIRA здесь: https://jira.springsource.org/browse/SEC-1424 – sappenin