Answers:
在带有Java Config的 Spring Security 3中,可以使用HttpSecurity.sessionManagement():
@Override
protected void configure(final HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
在Spring Securitiy 3.0中,这似乎更加容易。如果使用名称空间配置,则只需执行以下操作:
<http create-session="never">
<!-- config -->
</http>
或者你可以配置SecurityContextRepository为空,并没有什么会永远得救这样也。
今天,我们处理了同一问题(将自定义SecurityContextRepository注入到SecurityContextPersistenceFilter中)为4-5个小时。最后,我们弄清楚了。首先,在Spring Security参考的8.3节中。doc,有一个SecurityContextPersistenceFilter bean定义
<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中。因此,我们只是使用自定义的impl更改了上面的bean定义,并将其放入安全上下文中。
当我们运行应用程序时,我们跟踪日志并看到SecurityContextPersistenceFilter没有使用我们的自定义隐含功能,而是使用HttpSessionSecurityContextRepository。
在尝试了一些其他操作之后,我们发现必须为自定义的SecurityContextRepository隐式代码赋予“ http”名称空间的“ security-context-repository-ref”属性。如果您使用“ http”命名空间,并且想注入自己的SecurityContextRepository隐式命令,请尝试使用“ security-context-repository-ref”属性。
当使用“ http”名称空间时,将忽略单独的SecurityContextPersistenceFilter定义。正如我在上面复制的那样,参考文档。没有说明。
如果我误会了这些东西,请纠正我。
看一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>
在努力解决此答案中发布的众多解决方案之后,尝试在使用<http>
名称空间配置时使某些功能正常工作,我终于找到了一种实际适用于我的用例的方法。我实际上并不需要Spring Security不启动会话(因为我在应用程序的其他部分中使用了会话),只是根本不“记住”会话中的身份验证(应该重新检查)每个请求)。
首先,我无法弄清楚如何执行上述“空实现”技术。目前尚不清楚您应该将securityContextRepository设置null
为no-op实施还是将其设置为no-op实现。前者不起作用,因为将a 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
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());
}
}
}
}
}
}
}
无论如何,对于确实有效的解决方案,尽管非常糟糕。只需使用a即可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>
WebSecurityConfigurerAdapter
与“ http.addFilterBefore(new SpringSecuritySessionDeletingFilter(), SecurityContextPersistenceFilter.class)
”
请注意:它是“创建会话”而不是“创建会话”
控制创建HTTP会话的热情。
如果未设置,则默认为“ ifRequired”。其他选项是“始终”和“从不”。
此属性的设置会影响HttpSessionContextIntegrationFilter的allowSessionCreation和forceEagerSessionCreation属性。除非将此属性设置为“从不”,否则allowSessionCreation始终为true。除非将其设置为“ always”,否则forceEagerSessionCreation为“ false”。
因此,默认配置允许会话创建,但不强制执行。例外是如果启用了并发会话控制,那么无论forceEagerSessionCreation设置为true时,无论此处的设置如何。然后,在初始化HttpSessionContextIntegrationFilter时使用“从不”将导致异常。
有关会话用法的特定详细信息,HttpSessionSecurityContextRepository javadoc中有一些不错的文档。
auto-config=false
,您显然也无法用自己的SECURITY_CONTEXT_FILTER
位置替换当前位置。我一直在尝试使用某些ApplicationContextAware
bean 禁用它(使用反射将强制为securityContextRepository
中的null实现SessionManagementFilter
),但没有骰子。遗憾的是,我无法切换到提供的春季安全3 create-session=stateless
. 1年。