Как я могу использовать Spring Security без сессий?
Я создаю веб-приложение с Spring Security, которое будет жить на Amazon EC2 и использовать эластичные балансировщики нагрузки Amazon. К сожалению, ELB не поддерживает липкие сеансы, поэтому мне нужно убедиться, что мое приложение работает правильно без сеансов.
до сих пор у меня есть настройка RememberMeServices для назначения токена через cookie, и это отлично работает, но я хочу, чтобы cookie истекал с сеансом браузера (например, когда браузер закрывается).
Я должен представить, что я не первый, кто хочет использовать Spring Security без сеансов... есть предложения?
7 ответов:
весной безопасность 3 с Java Config, вы можете использовать HttpSecurity.sessionManagement():
@Override protected void configure(final HttpSecurity http) throws Exception { http .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); }
мы работали над одной и той же проблемой (вводя пользовательский SecurityContextRepository в SecurityContextPersistenceFilter) в течение 4-5 часов сегодня. Наконец-то мы это поняли. Прежде всего, в разделе 8.3 Spring Security ref. док, существует определение компонента SecurityContextPersistenceFilter
<bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter"> <property name='securityContextRepository'> <bean class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'> <property name='allowSessionCreation' value='false' /> </bean> </property> </bean>и после этого определения, есть этому объяснение: "В качестве альтернативы вы можете предоставить нулевую реализацию интерфейса SecurityContextRepository, которая предотвратит контекст безопасности не сохраняется, даже если сеанс уже был создан во время запроса."
нам нужно было ввести наш пользовательский SecurityContextRepository в SecurityContextPersistenceFilter. Поэтому мы просто изменили определение bean выше с помощью нашего пользовательского impl и поместили его в контекст безопасности.
когда мы запускали приложение, мы отслеживали журналы и видели, что SecurityContextPersistenceFilter не использовал наш пользовательский impl, он использовал HttpSessionSecurityContextRepository.
после нескольких других вещей, которые мы пробовали, мы выяснили, что нам нужно было дать наш пользовательский securitycontextrepository impl с атрибутом "security-context-repository-ref" пространства имен "http". Если вы используете пространство имен "http "и хотите ввести свой собственный securitycontextrepository impl, попробуйте атрибут" security-context-repository-ref".
при использовании пространства имен "http" отдельное определение SecurityContextPersistenceFilter игнорируется. как я скопировано выше, ссылка doc. не утверждает этого.
пожалуйста, поправьте меня, если я неправильно понял все.
Кажется, еще проще весной секьюрити 3.0. Если вы используете конфигурацию пространства имен, вы можете просто сделать следующее:
<http create-session="never"> <!-- config --> </http>или вы можете настроить SecurityContextRepository как null, и ничто никогда не будет сохранено таким образом а также.
посмотри
SecurityContextPersistenceFilterкласса. Он определяет какSecurityContextHolderзаполняется. По умолчанию он используетHttpSessionSecurityContextRepositoryдля хранения контекста безопасности в сеансе http.я реализовал этот механизм довольно легко, с custom
SecurityContextRepository.посмотреть
securityContext.xmlниже:<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:sec="http://www.springframework.org/schema/security" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd"> <context:annotation-config/> <sec:global-method-security secured-annotations="enabled" pre-post-annotations="enabled"/> <bean id="securityContextRepository" class="com.project.server.security.TokenSecurityContextRepository"/> <bean id="securityContextFilter" class="com.project.server.security.TokenSecurityContextPersistenceFilter"> <property name="repository" ref="securityContextRepository"/> </bean> <bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter"> <constructor-arg value="/login.jsp"/> <constructor-arg> <list> <bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/> </list> </constructor-arg> </bean> <bean id="formLoginFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"> <property name="authenticationManager" ref="authenticationManager"/> <property name="authenticationSuccessHandler"> <bean class="com.project.server.security.TokenAuthenticationSuccessHandler"> <property name="defaultTargetUrl" value="/index.html"/> <property name="passwordExpiredUrl" value="/changePassword.jsp"/> <property name="alwaysUseDefaultTargetUrl" value="true"/> </bean> </property> <property name="authenticationFailureHandler"> <bean class="com.project.server.modules.security.CustomUrlAuthenticationFailureHandler"> <property name="defaultFailureUrl" value="/login.jsp?failure=1"/> </bean> </property> <property name="filterProcessesUrl" value="/j_spring_security_check"/> <property name="allowSessionCreation" value="false"/> </bean> <bean id="servletApiFilter" class="org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter"/> <bean id="anonFilter" class="org.springframework.security.web.authentication.AnonymousAuthenticationFilter"> <property name="key" value="ClientApplication"/> <property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/> </bean> <bean id="exceptionTranslator" class="org.springframework.security.web.access.ExceptionTranslationFilter"> <property name="authenticationEntryPoint"> <bean class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"> <property name="loginFormUrl" value="/login.jsp"/> </bean> </property> <property name="accessDeniedHandler"> <bean class="org.springframework.security.web.access.AccessDeniedHandlerImpl"> <property name="errorPage" value="/login.jsp?failure=2"/> </bean> </property> <property name="requestCache"> <bean id="nullRequestCache" class="org.springframework.security.web.savedrequest.NullRequestCache"/> </property> </bean> <alias name="filterChainProxy" alias="springSecurityFilterChain"/> <bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy"> <sec:filter-chain-map path-type="ant"> <sec:filter-chain pattern="/**" filters="securityContextFilter, logoutFilter, formLoginFilter, servletApiFilter, anonFilter, exceptionTranslator, filterSecurityInterceptor"/> </sec:filter-chain-map> </bean> <bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor"> <property name="securityMetadataSource"> <sec:filter-security-metadata-source use-expressions="true"> <sec:intercept-url pattern="/staticresources/**" access="permitAll"/> <sec:intercept-url pattern="/index.html*" access="hasRole('USER_ROLE')"/> <sec:intercept-url pattern="/rpc/*" access="hasRole('USER_ROLE')"/> <sec:intercept-url pattern="/**" access="permitAll"/> </sec:filter-security-metadata-source> </property> <property name="authenticationManager" ref="authenticationManager"/> <property name="accessDecisionManager" ref="accessDecisionManager"/> </bean> <bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased"> <property name="decisionVoters"> <list> <bean class="org.springframework.security.access.vote.RoleVoter"/> <bean class="org.springframework.security.web.access.expression.WebExpressionVoter"/> </list> </property> </bean> <bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager"> <property name="providers"> <list> <bean name="authenticationProvider" class="com.project.server.modules.security.oracle.StoredProcedureBasedAuthenticationProviderImpl"> <property name="dataSource" ref="serverDataSource"/> <property name="userDetailsService" ref="userDetailsService"/> <property name="auditLogin" value="true"/> <property name="postAuthenticationChecks" ref="customPostAuthenticationChecks"/> </bean> </list> </property> </bean> <bean id="customPostAuthenticationChecks" class="com.project.server.modules.security.CustomPostAuthenticationChecks"/> <bean name="userDetailsService" class="com.project.server.modules.security.oracle.UserDetailsServiceImpl"> <property name="dataSource" ref="serverDataSource"/> </bean> </beans>
на самом деле
create-session="never"не означает быть полностью апатридом. Есть проблема для этого весной управления вопросами безопасности.
просто краткое Примечание: это "create-session", а не"create-sessions"
управляет рвением, с которым создается сеанс HTTP.
Если не установлено, по умолчанию используется "ifRequired". Другие варианты "всегда" и "никогда".
установка этого атрибута влияет на свойства allowSessionCreation и forceEagerSessionCreation HttpSessionContextIntegrationFilter. allowSessionCreation всегда будет истинным, если этот атрибут не установлен в "никогда". forceEagerSessionCreation является "ложным", если не установлено значение"всегда".
таким образом, конфигурация по умолчанию позволяет создавать сеанс, но не заставляет его. Исключение составляет, если включен параллельный контроль сеанса, когда forceEagerSessionCreation будет установлен в true, независимо от того, что здесь задано. Использование "никогда" затем вызовет исключение во время инициализации HttpSessionContextIntegrationFilter.
для получения подробной информации об использовании сессии, есть несколько хороших документации в Javadoc HttpSessionSecurityContextRepository.
после борьбы с многочисленными решениями, опубликованными в этом ответе, чтобы попытаться получить что-то работающее при использовании
<http>namespace config, я наконец нашел подход, который действительно работает для моего варианта использования. Я на самом деле не требую, чтобы Spring Security не запускал сеанс (потому что я использую сеанс в других частях приложения), просто он вообще не "запоминает" аутентификацию в сеансе (он должен повторно проверять каждый запрос).для начала, я не удалось выяснить, как сделать метод "нулевой реализации", описанный выше. Не было ясно, должны ли вы установить securityContextRepository в
nullили к реализации no-op. Бывший не работает, потому чтоNullPointerExceptionбросается внутрьSecurityContextPersistenceFilter.doFilter(). Что касается реализации no-op, я попытался реализовать самым простым способом, который я мог себе представить: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()); } } } } } } }в любом случае, к решению, которое на самом деле работает, хотя и очень hackish. Просто используйте
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>
Comments