SEC-1424: Added support for "stateless" option for create-session attribute, designed for applications which do not use sessions at all.

This commit is contained in:
Luke Taylor 2010-02-27 00:22:21 +00:00
parent 6a34807a07
commit f0466b6488
10 changed files with 197 additions and 129 deletions

View File

@ -40,8 +40,8 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
public BeanDefinition parse(Element element, ParserContext pc) {
if (!namespaceMatchesVersion(element)) {
pc.getReaderContext().fatal("You cannot use a spring-security-2.0.xsd schema with Spring Security 3.0." +
" Please update your schema declarations to the 3.0 schema.", element);
pc.getReaderContext().fatal("You cannot use a spring-security-2.0.xsd schema with Spring Security 3." +
" Please update your schema declarations to the 3.1 schema.", element);
}
String name = pc.getDelegate().getLocalName(element);
BeanDefinitionParser parser = parsers.get(name);
@ -131,7 +131,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
private boolean matchesVersionInternal(Element element) {
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
return schemaLocation.matches("(?m).*spring-security-3.0.xsd.*")
return schemaLocation.matches("(?m).*spring-security-3.*.xsd.*")
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|| !schemaLocation.matches("(?m).*spring-security.*");
}

View File

@ -24,7 +24,6 @@ import org.springframework.security.authentication.AnonymousAuthenticationProvid
import org.springframework.security.authentication.RememberMeAuthenticationProvider;
import org.springframework.security.config.Elements;
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.web.PortResolverImpl;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
@ -33,9 +32,8 @@ import org.springframework.security.web.authentication.preauth.PreAuthenticatedA
import org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor;
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
@ -66,14 +64,11 @@ final class AuthenticationConfigBuilder {
private static final String ATT_USER_SERVICE_REF = "user-service-ref";
private static final String ATT_REF = "ref";
private Element httpElt;
private ParserContext pc;
private final boolean autoConfig;
private final boolean allowSessionCreation;
private final String portMapperName;
private RootBeanDefinition anonymousFilter;
private BeanReference anonymousProviderRef;
@ -101,19 +96,32 @@ final class AuthenticationConfigBuilder {
final SecureRandom random;
public AuthenticationConfigBuilder(Element element, ParserContext pc, boolean allowSessionCreation,
String portMapperName) {
public AuthenticationConfigBuilder(Element element, ParserContext pc, SessionCreationPolicy sessionPolicy,
BeanReference requestCache, BeanReference authenticationManager, BeanReference sessionStrategy) {
this.httpElt = element;
this.pc = pc;
this.portMapperName = portMapperName;
this.requestCache = requestCache;
autoConfig = "true".equals(element.getAttribute(ATT_AUTO_CONFIG));
this.allowSessionCreation = allowSessionCreation;
this.allowSessionCreation = sessionPolicy != SessionCreationPolicy.never
&& sessionPolicy != SessionCreationPolicy.stateless;
try {
random = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) {
// Shouldn't happen...
throw new RuntimeException("Failed find SHA1PRNG algorithm!");
}
createAnonymousFilter();
createRememberMeFilter(authenticationManager);
createBasicFilter(authenticationManager);
createFormLoginFilter(sessionStrategy, authenticationManager);
createOpenIDLoginFilter(sessionStrategy, authenticationManager);
createX509Filter(authenticationManager);
createLogoutFilter();
createLoginPageFilterIfNeeded();
createUserServiceInjector();
createExceptionTranslationFilter();
}
void createRememberMeFilter(BeanReference authenticationManager) {
@ -166,7 +174,6 @@ final class AuthenticationConfigBuilder {
formFilter.getPropertyValues().addPropertyValue("allowSessionCreation", new Boolean(allowSessionCreation));
formFilter.getPropertyValues().addPropertyValue("authenticationManager", authManager);
// Id is required by login page filter
formFilterId = pc.getReaderContext().generateBeanName(formFilter);
pc.registerBeanComponent(new BeanComponentDefinition(formFilter, formFilterId));
@ -323,7 +330,6 @@ final class AuthenticationConfigBuilder {
x509ProviderRef = new RuntimeBeanReference(x509ProviderId);
}
void createLoginPageFilterIfNeeded() {
boolean needLoginPage = formFilter != null || openIDFilter != null;
String formLoginPage = getLoginFormUrl(formEntryPoint);
@ -414,28 +420,6 @@ final class AuthenticationConfigBuilder {
etf = etfBuilder.getBeanDefinition();
}
void createRequestCache() {
Element requestCacheElt = DomUtils.getChildElementByTagName(httpElt, Elements.REQUEST_CACHE);
if (requestCacheElt != null) {
requestCache = new RuntimeBeanReference(requestCacheElt.getAttribute(ATT_REF));
return;
}
BeanDefinitionBuilder requestCacheBldr = BeanDefinitionBuilder.rootBeanDefinition(HttpSessionRequestCache.class);
BeanDefinitionBuilder portResolver = BeanDefinitionBuilder.rootBeanDefinition(PortResolverImpl.class);
portResolver.addPropertyReference("portMapper", portMapperName);
requestCacheBldr.addPropertyValue("createSessionAllowed", allowSessionCreation);
requestCacheBldr.addPropertyValue("portResolver", portResolver.getBeanDefinition());
BeanDefinition bean = requestCacheBldr.getBeanDefinition();
String id = pc.getReaderContext().generateBeanName(bean);
pc.registerBeanComponent(new BeanComponentDefinition(bean, id));
this.requestCache = new RuntimeBeanReference(id);
}
private BeanMetadataElement createAccessDeniedHandler(Element element, ParserContext pc) {
String accessDeniedPage = element.getAttribute(ATT_ACCESS_DENIED_PAGE);
WebConfigUtils.validateHttpRedirect(accessDeniedPage, pc, pc.extractSource(element));
@ -610,8 +594,4 @@ final class AuthenticationConfigBuilder {
return providers;
}
public BeanReference getRequestCache() {
return requestCache;
}
}

View File

@ -1,7 +1,7 @@
package org.springframework.security.config.http;
import static org.springframework.security.config.http.SecurityFilters.*;
import static org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.*;
import static org.springframework.security.config.http.SecurityFilters.*;
import java.util.ArrayList;
import java.util.Collections;
@ -24,6 +24,7 @@ import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.config.Elements;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.PortResolverImpl;
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
@ -39,7 +40,11 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa
import org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy;
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.NullSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.NullRequestCache;
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.SessionManagementFilter;
@ -57,9 +62,6 @@ import org.w3c.dom.Element;
*/
class HttpConfigurationBuilder {
private static final String ATT_CREATE_SESSION = "create-session";
private static final String OPT_CREATE_SESSION_NEVER = "never";
private static final String DEF_CREATE_SESSION_IF_REQUIRED = "ifRequired";
private static final String OPT_CREATE_SESSION_ALWAYS = "always";
private static final String ATT_SESSION_FIXATION_PROTECTION = "session-fixation-protection";
private static final String OPT_SESSION_FIXATION_NO_PROTECTION = "none";
@ -75,11 +77,13 @@ class HttpConfigurationBuilder {
private static final String ATT_ACCESS_MGR = "access-decision-manager-ref";
private static final String ATT_ONCE_PER_REQUEST = "once-per-request";
private static final String ATT_REF = "ref";
private final Element httpElt;
private final ParserContext pc;
private final UrlMatcher matcher;
private final Boolean convertPathsToLowerCase;
private final boolean allowSessionCreation;
private final SessionCreationPolicy sessionPolicy;
private final List<Element> interceptUrls;
// Use ManagedMap to allow placeholder resolution
@ -90,13 +94,16 @@ class HttpConfigurationBuilder {
private BeanReference contextRepoRef;
private BeanReference sessionRegistryRef;
private BeanDefinition concurrentSessionFilter;
private BeanDefinition requestCacheAwareFilter;
private BeanReference sessionStrategyRef;
private RootBeanDefinition sfpf;
private BeanDefinition servApiFilter;
private String portMapperName;
private BeanReference fsi;
private BeanReference requestCache;
public HttpConfigurationBuilder(Element element, ParserContext pc, UrlMatcher matcher, String portMapperName) {
public HttpConfigurationBuilder(Element element, ParserContext pc, UrlMatcher matcher,
String portMapperName, BeanReference authenticationManager) {
this.httpElt = element;
this.pc = pc;
this.portMapperName = portMapperName;
@ -105,10 +112,24 @@ class HttpConfigurationBuilder {
// true if Ant path and using lower case
convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl();
interceptUrls = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);
allowSessionCreation = !OPT_CREATE_SESSION_NEVER.equals(element.getAttribute(ATT_CREATE_SESSION));
String createSession = element.getAttribute(ATT_CREATE_SESSION);
if (StringUtils.hasText(createSession)) {
sessionPolicy = SessionCreationPolicy.valueOf(createSession);
} else {
sessionPolicy = SessionCreationPolicy.ifRequired;
}
parseInterceptUrlsForEmptyFilterChains();
createSecurityContextPersistenceFilter();
createSessionManagementFilters();
createRequestCacheFilter();
createServletApiFilter();
createChannelProcessingFilter();
createFilterSecurityInterceptor(authenticationManager);
}
void parseInterceptUrlsForEmptyFilterChains() {
private void parseInterceptUrlsForEmptyFilterChains() {
filterChainMap = new ManagedMap<BeanDefinition, List<BeanMetadataElement>>();
for (Element urlElt : interceptUrls) {
@ -142,43 +163,44 @@ class HttpConfigurationBuilder {
return lowerCase ? path.toLowerCase() : path;
}
void createSecurityContextPersistenceFilter() {
private void createSecurityContextPersistenceFilter() {
BeanDefinitionBuilder scpf = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextPersistenceFilter.class);
String repoRef = httpElt.getAttribute(ATT_SECURITY_CONTEXT_REPOSITORY);
String createSession = httpElt.getAttribute(ATT_CREATE_SESSION);
String disableUrlRewriting = httpElt.getAttribute(ATT_DISABLE_URL_REWRITING);
if (StringUtils.hasText(repoRef)) {
if (OPT_CREATE_SESSION_ALWAYS.equals(createSession)) {
if (sessionPolicy == SessionCreationPolicy.always) {
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
} else if (StringUtils.hasText(createSession)) {
pc.getReaderContext().error("If using security-context-repository-ref, the only value you can set for " +
"'create-session' is 'always'. Other session creation logic should be handled by the " +
"SecurityContextRepository", httpElt);
}
} else {
BeanDefinitionBuilder contextRepo = BeanDefinitionBuilder.rootBeanDefinition(HttpSessionSecurityContextRepository.class);
if (OPT_CREATE_SESSION_ALWAYS.equals(createSession)) {
contextRepo.addPropertyValue("allowSessionCreation", Boolean.TRUE);
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
} else if (OPT_CREATE_SESSION_NEVER.equals(createSession)) {
contextRepo.addPropertyValue("allowSessionCreation", Boolean.FALSE);
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
BeanDefinitionBuilder contextRepo;
if (sessionPolicy == SessionCreationPolicy.stateless) {
contextRepo = BeanDefinitionBuilder.rootBeanDefinition(NullSecurityContextRepository.class);
} else {
createSession = DEF_CREATE_SESSION_IF_REQUIRED;
contextRepo.addPropertyValue("allowSessionCreation", Boolean.TRUE);
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
}
contextRepo = BeanDefinitionBuilder.rootBeanDefinition(HttpSessionSecurityContextRepository.class);
switch (sessionPolicy) {
case always:
contextRepo.addPropertyValue("allowSessionCreation", Boolean.TRUE);
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
break;
case never:
contextRepo.addPropertyValue("allowSessionCreation", Boolean.FALSE);
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
break;
default:
contextRepo.addPropertyValue("allowSessionCreation", Boolean.TRUE);
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
}
if ("true".equals(disableUrlRewriting)) {
contextRepo.addPropertyValue("disableUrlRewriting", Boolean.TRUE);
if ("true".equals(disableUrlRewriting)) {
contextRepo.addPropertyValue("disableUrlRewriting", Boolean.TRUE);
}
}
BeanDefinition repoBean = contextRepo.getBeanDefinition();
repoRef = pc.getReaderContext().generateBeanName(repoBean);
pc.registerBeanComponent(new BeanComponentDefinition(repoBean, repoRef));
}
contextRepoRef = new RuntimeBeanReference(repoRef);
@ -187,7 +209,7 @@ class HttpConfigurationBuilder {
securityContextPersistenceFilter = scpf.getBeanDefinition();
}
void createSessionManagementFilters() {
private void createSessionManagementFilters() {
Element sessionMgmtElt = DomUtils.getChildElementByTagName(httpElt, Elements.SESSION_MANAGEMENT);
Element sessionCtrlElt = null;
@ -197,6 +219,11 @@ class HttpConfigurationBuilder {
String errorUrl = null;
if (sessionMgmtElt != null) {
if (sessionPolicy == SessionCreationPolicy.stateless) {
pc.getReaderContext().error(Elements.SESSION_MANAGEMENT + " cannot be used" +
" in combination with " + ATT_CREATE_SESSION + "='"+ SessionCreationPolicy.stateless +"'",
pc.extractSource(sessionMgmtElt));
}
sessionFixationAttribute = sessionMgmtElt.getAttribute(ATT_SESSION_FIXATION_PROTECTION);
invalidSessionUrl = sessionMgmtElt.getAttribute(ATT_INVALID_SESSION_URL);
sessionAuthStratRef = sessionMgmtElt.getAttribute(ATT_SESSION_AUTH_STRATEGY_REF);
@ -216,7 +243,12 @@ class HttpConfigurationBuilder {
sessionFixationAttribute = OPT_SESSION_FIXATION_MIGRATE_SESSION;
} else if (StringUtils.hasText(sessionAuthStratRef)) {
pc.getReaderContext().error(ATT_SESSION_FIXATION_PROTECTION + " attribute cannot be used" +
" in combination with " + ATT_SESSION_AUTH_STRATEGY_REF, pc.extractSource(sessionCtrlElt));
" in combination with " + ATT_SESSION_AUTH_STRATEGY_REF, pc.extractSource(sessionMgmtElt));
}
if (sessionPolicy == SessionCreationPolicy.stateless) {
// SEC-1424: do nothing
return;
}
boolean sessionFixationProtectionRequired = !sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION);
@ -323,7 +355,7 @@ class HttpConfigurationBuilder {
}
// Adds the servlet-api integration filter if required
void createServletApiFilter() {
private void createServletApiFilter() {
final String ATT_SERVLET_API_PROVISION = "servlet-api-provision";
final String DEF_SERVLET_API_PROVISION = "true";
@ -337,7 +369,7 @@ class HttpConfigurationBuilder {
}
}
void createChannelProcessingFilter() {
private void createChannelProcessingFilter() {
ManagedMap<BeanDefinition,BeanDefinition> channelRequestMap = parseInterceptUrlsForChannelSecurity();
if (channelRequestMap.isEmpty()) {
@ -407,7 +439,36 @@ class HttpConfigurationBuilder {
return channelRequestMap;
}
void createFilterSecurityInterceptor(BeanReference authManager) {
private void createRequestCacheFilter() {
Element requestCacheElt = DomUtils.getChildElementByTagName(httpElt, Elements.REQUEST_CACHE);
if (requestCacheElt != null) {
requestCache = new RuntimeBeanReference(requestCacheElt.getAttribute(ATT_REF));
} else {
BeanDefinitionBuilder requestCacheBldr;
if (sessionPolicy == SessionCreationPolicy.stateless) {
requestCacheBldr = BeanDefinitionBuilder.rootBeanDefinition(NullRequestCache.class);
} else {
requestCacheBldr = BeanDefinitionBuilder.rootBeanDefinition(HttpSessionRequestCache.class);
BeanDefinitionBuilder portResolver = BeanDefinitionBuilder.rootBeanDefinition(PortResolverImpl.class);
portResolver.addPropertyReference("portMapper", portMapperName);
requestCacheBldr.addPropertyValue("createSessionAllowed", sessionPolicy == SessionCreationPolicy.ifRequired);
requestCacheBldr.addPropertyValue("portResolver", portResolver.getBeanDefinition());
}
BeanDefinition bean = requestCacheBldr.getBeanDefinition();
String id = pc.getReaderContext().generateBeanName(bean);
pc.registerBeanComponent(new BeanComponentDefinition(bean, id));
this.requestCache = new RuntimeBeanReference(id);
}
requestCacheAwareFilter = new RootBeanDefinition(RequestCacheAwareFilter.class);
requestCacheAwareFilter.getPropertyValues().addPropertyValue("requestCache", requestCache);
}
private void createFilterSecurityInterceptor(BeanReference authManager) {
boolean useExpressions = FilterInvocationSecurityMetadataSourceParser.isUseExpressions(httpElt);
BeanDefinition securityMds = FilterInvocationSecurityMetadataSourceParser.createSecurityMetadataSource(interceptUrls, httpElt, pc);
@ -459,12 +520,15 @@ class HttpConfigurationBuilder {
return sessionStrategyRef;
}
boolean isAllowSessionCreation() {
return allowSessionCreation;
SessionCreationPolicy getSessionCreationPolicy() {
return sessionPolicy;
}
public ManagedMap<BeanDefinition, List<BeanMetadataElement>> getFilterChainMap() {
BeanReference getRequestCache() {
return requestCache;
}
ManagedMap<BeanDefinition, List<BeanMetadataElement>> getFilterChainMap() {
return filterChainMap;
}
@ -491,6 +555,10 @@ class HttpConfigurationBuilder {
filters.add(new OrderDecorator(fsi, FILTER_SECURITY_INTERCEPTOR));
if (sessionPolicy != SessionCreationPolicy.stateless) {
filters.add(new OrderDecorator(requestCacheAwareFilter, REQUEST_CACHE_FILTER));
}
return filters;
}
}

View File

@ -1,7 +1,5 @@
package org.springframework.security.config.http;
import static org.springframework.security.config.http.SecurityFilters.REQUEST_CACHE_FILTER;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -28,7 +26,6 @@ import org.springframework.security.config.BeanIds;
import org.springframework.security.config.Elements;
import org.springframework.security.config.authentication.AuthenticationManagerFactoryBean;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
import org.springframework.security.web.util.AntUrlPathMatcher;
import org.springframework.security.web.util.RegexUrlPathMatcher;
import org.springframework.security.web.util.UrlMatcher;
@ -85,33 +82,15 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
final String portMapperName = createPortMapper(element, pc);
final UrlMatcher matcher = createUrlMatcher(element);
HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, pc, matcher, portMapperName);
httpBldr.parseInterceptUrlsForEmptyFilterChains();
httpBldr.createSecurityContextPersistenceFilter();
httpBldr.createSessionManagementFilters();
ManagedList<BeanReference> authenticationProviders = new ManagedList<BeanReference>();
BeanReference authenticationManager = createAuthenticationManager(element, pc, authenticationProviders, null);
httpBldr.createServletApiFilter();
httpBldr.createChannelProcessingFilter();
httpBldr.createFilterSecurityInterceptor(authenticationManager);
HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, pc, matcher,
portMapperName, authenticationManager);
AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, pc,
httpBldr.isAllowSessionCreation(), portMapperName);
authBldr.createAnonymousFilter();
authBldr.createRememberMeFilter(authenticationManager);
authBldr.createRequestCache();
authBldr.createBasicFilter(authenticationManager);
authBldr.createFormLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);
authBldr.createOpenIDLoginFilter(httpBldr.getSessionStrategy(), authenticationManager);
authBldr.createX509Filter(authenticationManager);
authBldr.createLogoutFilter();
authBldr.createLoginPageFilterIfNeeded();
authBldr.createUserServiceInjector();
authBldr.createExceptionTranslationFilter();
httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager,
httpBldr.getSessionStrategy());
List<OrderDecorator> unorderedFilterChain = new ArrayList<OrderDecorator>();
@ -120,10 +99,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
authenticationProviders.addAll(authBldr.getProviders());
BeanDefinition requestCacheAwareFilter = new RootBeanDefinition(RequestCacheAwareFilter.class);
requestCacheAwareFilter.getPropertyValues().addPropertyValue("requestCache", authBldr.getRequestCache());
unorderedFilterChain.add(new OrderDecorator(requestCacheAwareFilter, REQUEST_CACHE_FILTER));
unorderedFilterChain.addAll(buildCustomFilterList(element, pc));
Collections.sort(unorderedFilterChain, new OrderComparator());

View File

@ -0,0 +1,13 @@
package org.springframework.security.config.http;
/**
*
* @author Luke Taylor
* @since 3.1
*/
enum SessionCreationPolicy {
always,
never,
ifRequired,
stateless
}

View File

@ -258,8 +258,8 @@ http.attlist &=
http.attlist &=
use-expressions?
http.attlist &=
## Controls the eagerness with which an HTTP session is created. If not set, defaults to "ifRequired". Note that if a custom SecurityContextRepository is set using security-context-repository-ref, then the only value which can be set is "always". Otherwise the session creation behaviour will be determined by the repository bean implementation.
attribute create-session {"ifRequired" | "always" | "never" }?
## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to "ifRequired". If "stateless" is used, this implies that the application guarantees that it will not create a session. This differs from the use of "never" which mans that Spring Security will not create a session, but will make use of one if the application does.
attribute create-session {"ifRequired" | "always" | "never" | "stateless"}?
http.attlist &=
## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.
attribute security-context-repository-ref {xsd:token}?

View File

@ -671,18 +671,19 @@
</xs:attribute>
<xs:attribute name="use-expressions" type="security:boolean">
<xs:annotation>
<xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements rather than the traditional list of configuration attributes. Defaults to 'false'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted. </xs:documentation>
<xs:documentation>Enables the use of expressions in the 'access' attributes in &lt;intercept-url&gt; elements rather than the traditional list of configuration attributes. Defaults to 'false'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="create-session">
<xs:annotation>
<xs:documentation>Controls the eagerness with which an HTTP session is created. If not set, defaults to "ifRequired". Note that if a custom SecurityContextRepository is set using security-context-repository-ref, then the only value which can be set is "always". Otherwise the session creation behaviour will be determined by the repository bean implementation.</xs:documentation>
<xs:documentation>Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to "ifRequired". If "stateless" is used, this implies that the application guarantees that it will not create a session. This differs from the use of "never" which mans that Spring Security will not create a session, but will make use of one if the application does. </xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="ifRequired"/>
<xs:enumeration value="always"/>
<xs:enumeration value="never"/>
<xs:enumeration value="stateless"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>

View File

@ -78,6 +78,7 @@ import org.springframework.security.web.authentication.rememberme.TokenBasedReme
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.NullSecurityContextRepository;
import org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
@ -958,6 +959,23 @@ public class HttpSecurityBeanDefinitionParserTests {
assertNull(request.getSession(false));
}
@Test
public void settingCreateSessionToStatelessSetsFilterPropertiesCorrectly() throws Exception {
setContext("<http auto-config='true' create-session='stateless'/>" + AUTH_PROVIDER_XML);
SecurityContextPersistenceFilter filter = getFilter(SecurityContextPersistenceFilter.class);
assertEquals(Boolean.FALSE, FieldUtils.getFieldValue(filter, "forceEagerSessionCreation"));
assertTrue(FieldUtils.getFieldValue(filter, "repo") instanceof NullSecurityContextRepository);
assertNull("Session management filter should not be in stack", getFilter(SessionManagementFilter.class));
assertNull("Request cache filter should not be in stack", getFilter(RequestCacheAwareFilter.class));
// Check that an invocation doesn't create a session
FilterChainProxy fcp = (FilterChainProxy) appContext.getBean(BeanIds.FILTER_CHAIN_PROXY);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setServletPath("/anything");
fcp.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());
assertNull(request.getSession(false));
}
@Test
public void settingCreateSessionToIfRequiredDoesntCreateASessionForPublicInvocation() throws Exception {
setContext("<http auto-config='true' create-session='ifRequired'/>" + AUTH_PROVIDER_XML);
@ -1002,15 +1020,6 @@ public class HttpSecurityBeanDefinitionParserTests {
assertTrue((Boolean)FieldUtils.getFieldValue(filter, "forceEagerSessionCreation"));
}
@Test(expected=BeanDefinitionParsingException.class)
public void cantUseUnsupportedSessionCreationAttributeWithExternallyDefinedSecurityContextRepository() throws Exception {
setContext(
"<b:bean id='repo' class='" + HttpSessionSecurityContextRepository.class.getName() + "'/>" +
"<http create-session='never' security-context-repository-ref='repo'>" +
" <http-basic />" +
"</http>" + AUTH_PROVIDER_XML);
}
@Test
public void expressionBasedAccessAllowsAndDeniesAccessAsExpected() throws Exception {
setContext(
@ -1147,11 +1156,7 @@ public class HttpSecurityBeanDefinitionParserTests {
ExceptionTranslationFilter etf = getFilter(ExceptionTranslationFilter.class);
LoginUrlAuthenticationEntryPoint ap = (LoginUrlAuthenticationEntryPoint) etf.getAuthenticationEntryPoint();
assertEquals("/form_login_page", ap.getLoginFormUrl());
try {
getFilter(DefaultLoginPageGeneratingFilter.class);
fail("Login page generating filter shouldn't be present");
} catch (Exception expected) {
}
assertNull(getFilter(DefaultLoginPageGeneratingFilter.class));
}
@Test
@ -1250,7 +1255,7 @@ public class HttpSecurityBeanDefinitionParserTests {
}
}
throw new Exception("Filter not found");
return null;
}
private RememberMeServices getRememberMeServices() throws Exception {

View File

@ -22,11 +22,11 @@ public class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext
Resource inMemoryXml;
public InMemoryXmlApplicationContext(String xml) {
this(xml, "3.0", null);
this(xml, "3.1", null);
}
public InMemoryXmlApplicationContext(String xml, ApplicationContext parent) {
this(xml, "3.0", parent);
this(xml, "3.1", parent);
}
public InMemoryXmlApplicationContext(String xml, String secVersion, ApplicationContext parent) {

View File

@ -0,0 +1,26 @@
package org.springframework.security.web.context;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* @author Luke Taylor
* @since 3.1
*/
public final class NullSecurityContextRepository implements SecurityContextRepository {
public boolean containsContext(HttpServletRequest request) {
return false;
}
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
return SecurityContextHolder.createEmptyContext();
}
public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
}
}