浏览代码

ForceEagerSessionCreationFilter

Closes gh-11109
Rob Winch 3 年之前
父节点
当前提交
aaf78330b1

+ 2 - 0
config/src/main/java/org/springframework/security/config/annotation/web/HttpSecurityBuilder.java

@@ -43,6 +43,7 @@ import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
 import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
 import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
 import org.springframework.security.web.session.ConcurrentSessionFilter;
 import org.springframework.security.web.session.ConcurrentSessionFilter;
 import org.springframework.security.web.session.DisableEncodeUrlFilter;
 import org.springframework.security.web.session.DisableEncodeUrlFilter;
+import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
 import org.springframework.security.web.session.SessionManagementFilter;
 import org.springframework.security.web.session.SessionManagementFilter;
 
 
 /**
 /**
@@ -125,6 +126,7 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>>
 	 * The ordering of the Filters is:
 	 * The ordering of the Filters is:
 	 *
 	 *
 	 * <ul>
 	 * <ul>
+	 * <li>{@link ForceEagerSessionCreationFilter}</li>
 	 * <li>{@link DisableEncodeUrlFilter}</li>
 	 * <li>{@link DisableEncodeUrlFilter}</li>
 	 * <li>{@link ChannelProcessingFilter}</li>
 	 * <li>{@link ChannelProcessingFilter}</li>
 	 * <li>{@link SecurityContextPersistenceFilter}</li>
 	 * <li>{@link SecurityContextPersistenceFilter}</li>

+ 2 - 0
config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterOrderRegistration.java

@@ -47,6 +47,7 @@ import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
 import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
 import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
 import org.springframework.security.web.session.ConcurrentSessionFilter;
 import org.springframework.security.web.session.ConcurrentSessionFilter;
 import org.springframework.security.web.session.DisableEncodeUrlFilter;
 import org.springframework.security.web.session.DisableEncodeUrlFilter;
+import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
 import org.springframework.security.web.session.SessionManagementFilter;
 import org.springframework.security.web.session.SessionManagementFilter;
 import org.springframework.web.filter.CorsFilter;
 import org.springframework.web.filter.CorsFilter;
 
 
@@ -70,6 +71,7 @@ final class FilterOrderRegistration {
 	FilterOrderRegistration() {
 	FilterOrderRegistration() {
 		Step order = new Step(INITIAL_ORDER, ORDER_STEP);
 		Step order = new Step(INITIAL_ORDER, ORDER_STEP);
 		put(DisableEncodeUrlFilter.class, order.next());
 		put(DisableEncodeUrlFilter.class, order.next());
+		put(ForceEagerSessionCreationFilter.class, order.next());
 		put(ChannelProcessingFilter.class, order.next());
 		put(ChannelProcessingFilter.class, order.next());
 		order.next(); // gh-8105
 		order.next(); // gh-8105
 		put(WebAsyncManagerIntegrationFilter.class, order.next());
 		put(WebAsyncManagerIntegrationFilter.class, order.next());

+ 2 - 0
config/src/main/java/org/springframework/security/config/annotation/web/configurers/SecurityContextConfigurer.java

@@ -25,6 +25,7 @@ import org.springframework.security.web.context.HttpSessionSecurityContextReposi
 import org.springframework.security.web.context.SecurityContextHolderFilter;
 import org.springframework.security.web.context.SecurityContextHolderFilter;
 import org.springframework.security.web.context.SecurityContextPersistenceFilter;
 import org.springframework.security.web.context.SecurityContextPersistenceFilter;
 import org.springframework.security.web.context.SecurityContextRepository;
 import org.springframework.security.web.context.SecurityContextRepository;
+import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
 
 
 /**
 /**
  * Allows persisting and restoring of the {@link SecurityContext} found on the
  * Allows persisting and restoring of the {@link SecurityContext} found on the
@@ -117,6 +118,7 @@ public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>>
 					? sessionManagement.getSessionCreationPolicy() : null;
 					? sessionManagement.getSessionCreationPolicy() : null;
 			if (SessionCreationPolicy.ALWAYS == sessionCreationPolicy) {
 			if (SessionCreationPolicy.ALWAYS == sessionCreationPolicy) {
 				securityContextFilter.setForceEagerSessionCreation(true);
 				securityContextFilter.setForceEagerSessionCreation(true);
+				http.addFilter(postProcess(new ForceEagerSessionCreationFilter()));
 			}
 			}
 			securityContextFilter = postProcess(securityContextFilter);
 			securityContextFilter = postProcess(securityContextFilter);
 			http.addFilter(securityContextFilter);
 			http.addFilter(securityContextFilter);

+ 4 - 0
config/src/main/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurer.java

@@ -53,6 +53,7 @@ import org.springframework.security.web.savedrequest.NullRequestCache;
 import org.springframework.security.web.savedrequest.RequestCache;
 import org.springframework.security.web.savedrequest.RequestCache;
 import org.springframework.security.web.session.ConcurrentSessionFilter;
 import org.springframework.security.web.session.ConcurrentSessionFilter;
 import org.springframework.security.web.session.DisableEncodeUrlFilter;
 import org.springframework.security.web.session.DisableEncodeUrlFilter;
+import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
 import org.springframework.security.web.session.InvalidSessionStrategy;
 import org.springframework.security.web.session.InvalidSessionStrategy;
 import org.springframework.security.web.session.SessionInformationExpiredStrategy;
 import org.springframework.security.web.session.SessionInformationExpiredStrategy;
 import org.springframework.security.web.session.SessionManagementFilter;
 import org.springframework.security.web.session.SessionManagementFilter;
@@ -380,6 +381,9 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
 		if (!this.enableSessionUrlRewriting) {
 		if (!this.enableSessionUrlRewriting) {
 			http.addFilter(new DisableEncodeUrlFilter());
 			http.addFilter(new DisableEncodeUrlFilter());
 		}
 		}
+		if (this.sessionPolicy == SessionCreationPolicy.ALWAYS) {
+			http.addFilter(new ForceEagerSessionCreationFilter());
+		}
 	}
 	}
 
 
 	private ConcurrentSessionFilter createConcurrencyFilter(H http) {
 	private ConcurrentSessionFilter createConcurrencyFilter(H http) {

+ 14 - 0
config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java

@@ -69,6 +69,7 @@ import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
 import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
 import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
 import org.springframework.security.web.session.ConcurrentSessionFilter;
 import org.springframework.security.web.session.ConcurrentSessionFilter;
 import org.springframework.security.web.session.DisableEncodeUrlFilter;
 import org.springframework.security.web.session.DisableEncodeUrlFilter;
+import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
 import org.springframework.security.web.session.SessionManagementFilter;
 import org.springframework.security.web.session.SessionManagementFilter;
 import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
 import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
 import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;
 import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;
@@ -148,6 +149,8 @@ class HttpConfigurationBuilder {
 
 
 	private BeanDefinition securityContextPersistenceFilter;
 	private BeanDefinition securityContextPersistenceFilter;
 
 
+	private BeanDefinition forceEagerSessionCreationFilter;
+
 	private BeanReference contextRepoRef;
 	private BeanReference contextRepoRef;
 
 
 	private BeanReference sessionRegistryRef;
 	private BeanReference sessionRegistryRef;
@@ -207,6 +210,7 @@ class HttpConfigurationBuilder {
 		String createSession = element.getAttribute(ATT_CREATE_SESSION);
 		String createSession = element.getAttribute(ATT_CREATE_SESSION);
 		this.sessionPolicy = !StringUtils.hasText(createSession) ? SessionCreationPolicy.IF_REQUIRED
 		this.sessionPolicy = !StringUtils.hasText(createSession) ? SessionCreationPolicy.IF_REQUIRED
 				: createPolicy(createSession);
 				: createPolicy(createSession);
+		createForceEagerSessionCreationFilter();
 		createDisableEncodeUrlFilter();
 		createDisableEncodeUrlFilter();
 		createCsrfFilter();
 		createCsrfFilter();
 		createSecurityPersistence();
 		createSecurityPersistence();
@@ -304,6 +308,12 @@ class HttpConfigurationBuilder {
 		return Boolean.parseBoolean(explicitSaveAttr);
 		return Boolean.parseBoolean(explicitSaveAttr);
 	}
 	}
 
 
+	private void createForceEagerSessionCreationFilter() {
+		if (this.sessionPolicy == SessionCreationPolicy.ALWAYS) {
+			this.forceEagerSessionCreationFilter = new RootBeanDefinition(ForceEagerSessionCreationFilter.class);
+		}
+	}
+
 	private void createSecurityContextPersistenceFilter() {
 	private void createSecurityContextPersistenceFilter() {
 		BeanDefinitionBuilder scpf = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextPersistenceFilter.class);
 		BeanDefinitionBuilder scpf = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextPersistenceFilter.class);
 		switch (this.sessionPolicy) {
 		switch (this.sessionPolicy) {
@@ -768,6 +778,10 @@ class HttpConfigurationBuilder {
 
 
 	List<OrderDecorator> getFilters() {
 	List<OrderDecorator> getFilters() {
 		List<OrderDecorator> filters = new ArrayList<>();
 		List<OrderDecorator> filters = new ArrayList<>();
+		if (this.forceEagerSessionCreationFilter != null) {
+			filters.add(new OrderDecorator(this.forceEagerSessionCreationFilter,
+					SecurityFilters.FORCE_EAGER_SESSION_FILTER));
+		}
 		if (this.disableUrlRewriteFilter != null) {
 		if (this.disableUrlRewriteFilter != null) {
 			filters.add(new OrderDecorator(this.disableUrlRewriteFilter, SecurityFilters.DISABLE_ENCODE_URL_FILTER));
 			filters.add(new OrderDecorator(this.disableUrlRewriteFilter, SecurityFilters.DISABLE_ENCODE_URL_FILTER));
 		}
 		}

+ 2 - 0
config/src/main/java/org/springframework/security/config/http/SecurityFilters.java

@@ -31,6 +31,8 @@ enum SecurityFilters {
 
 
 	DISABLE_ENCODE_URL_FILTER,
 	DISABLE_ENCODE_URL_FILTER,
 
 
+	FORCE_EAGER_SESSION_FILTER,
+
 	CHANNEL_FILTER,
 	CHANNEL_FILTER,
 
 
 	SECURITY_CONTEXT_FILTER,
 	SECURITY_CONTEXT_FILTER,

+ 1 - 1
config/src/main/resources/org/springframework/security/config/spring-security-5.7.rnc

@@ -1318,4 +1318,4 @@ position =
 	## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.
 	## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.
 	attribute position {named-security-filter}
 	attribute position {named-security-filter}
 
 
-named-security-filter = "FIRST" | "DISABLE_ENCODE_URL_FILTER" | "CHANNEL_FILTER" | "SECURITY_CONTEXT_FILTER" | "CONCURRENT_SESSION_FILTER" | "WEB_ASYNC_MANAGER_FILTER" | "HEADERS_FILTER" | "CORS_FILTER" | "SAML2_LOGOUT_REQUEST_FILTER" | "SAML2_LOGOUT_RESPONSE_FILTER" | "CSRF_FILTER" | "SAML2_LOGOUT_FILTER" | "LOGOUT_FILTER" | "OAUTH2_AUTHORIZATION_REQUEST_FILTER" | "SAML2_AUTHENTICATION_REQUEST_FILTER" | "X509_FILTER" | "PRE_AUTH_FILTER" | "CAS_FILTER" | "OAUTH2_LOGIN_FILTER" | "SAML2_AUTHENTICATION_FILTER" | "FORM_LOGIN_FILTER" | "OPENID_FILTER" | "LOGIN_PAGE_FILTER" |"LOGOUT_PAGE_FILTER" | "DIGEST_AUTH_FILTER" | "BEARER_TOKEN_AUTH_FILTER" | "BASIC_AUTH_FILTER" | "REQUEST_CACHE_FILTER" | "SERVLET_API_SUPPORT_FILTER" | "JAAS_API_SUPPORT_FILTER" | "REMEMBER_ME_FILTER" | "ANONYMOUS_FILTER" | "OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER" | "WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER" | "SESSION_MANAGEMENT_FILTER" | "EXCEPTION_TRANSLATION_FILTER" | "FILTER_SECURITY_INTERCEPTOR" | "SWITCH_USER_FILTER" | "LAST"
+named-security-filter = "FIRST" | "DISABLE_ENCODE_URL_FILTER" | "FORCE_EAGER_SESSION_FILTER" | "CHANNEL_FILTER" | "SECURITY_CONTEXT_FILTER" | "CONCURRENT_SESSION_FILTER" | "WEB_ASYNC_MANAGER_FILTER" | "HEADERS_FILTER" | "CORS_FILTER" | "SAML2_LOGOUT_REQUEST_FILTER" | "SAML2_LOGOUT_RESPONSE_FILTER" | "CSRF_FILTER" | "SAML2_LOGOUT_FILTER" | "LOGOUT_FILTER" | "OAUTH2_AUTHORIZATION_REQUEST_FILTER" | "SAML2_AUTHENTICATION_REQUEST_FILTER" | "X509_FILTER" | "PRE_AUTH_FILTER" | "CAS_FILTER" | "OAUTH2_LOGIN_FILTER" | "SAML2_AUTHENTICATION_FILTER" | "FORM_LOGIN_FILTER" | "OPENID_FILTER" | "LOGIN_PAGE_FILTER" |"LOGOUT_PAGE_FILTER" | "DIGEST_AUTH_FILTER" | "BEARER_TOKEN_AUTH_FILTER" | "BASIC_AUTH_FILTER" | "REQUEST_CACHE_FILTER" | "SERVLET_API_SUPPORT_FILTER" | "JAAS_API_SUPPORT_FILTER" | "REMEMBER_ME_FILTER" | "ANONYMOUS_FILTER" | "OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER" | "WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER" | "SESSION_MANAGEMENT_FILTER" | "EXCEPTION_TRANSLATION_FILTER" | "FILTER_SECURITY_INTERCEPTOR" | "SWITCH_USER_FILTER" | "LAST"

+ 30 - 29
config/src/main/resources/org/springframework/security/config/spring-security-5.7.xsd

@@ -124,7 +124,7 @@
       </xs:annotation>
       </xs:annotation>
       <xs:complexType/>
       <xs:complexType/>
    </xs:element>
    </xs:element>
-
+  
   <xs:attributeGroup name="password-encoder.attlist">
   <xs:attributeGroup name="password-encoder.attlist">
       <xs:attribute name="ref" type="xs:token">
       <xs:attribute name="ref" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -408,7 +408,7 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:attributeGroup name="ldap-ap.attlist">
   <xs:attributeGroup name="ldap-ap.attlist">
       <xs:attribute name="server-ref" type="xs:token">
       <xs:attribute name="server-ref" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -488,7 +488,7 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:attributeGroup name="password-compare.attlist">
   <xs:attributeGroup name="password-compare.attlist">
       <xs:attribute name="password-attribute" type="xs:token">
       <xs:attribute name="password-attribute" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -541,7 +541,7 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:attributeGroup name="protect.attlist">
   <xs:attributeGroup name="protect.attlist">
       <xs:attribute name="method" use="required" type="xs:token">
       <xs:attribute name="method" use="required" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -842,13 +842,13 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
-
-
-
-
-
-
+  
+  
+  
+  
+  
+  
+  
   <xs:attributeGroup name="protect-pointcut.attlist">
   <xs:attributeGroup name="protect-pointcut.attlist">
       <xs:attribute name="expression" use="required" type="xs:string">
       <xs:attribute name="expression" use="required" type="xs:string">
          <xs:annotation>
          <xs:annotation>
@@ -1323,7 +1323,7 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:attributeGroup name="access-denied-handler.attlist">
   <xs:attributeGroup name="access-denied-handler.attlist">
       <xs:attribute name="ref" type="xs:token">
       <xs:attribute name="ref" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -1348,7 +1348,7 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:attributeGroup name="intercept-url.attlist">
   <xs:attributeGroup name="intercept-url.attlist">
       <xs:attribute name="pattern" type="xs:token">
       <xs:attribute name="pattern" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -1405,7 +1405,7 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:attributeGroup name="logout.attlist">
   <xs:attributeGroup name="logout.attlist">
       <xs:attribute name="logout-url" type="xs:token">
       <xs:attribute name="logout-url" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -1452,7 +1452,7 @@
          <xs:attributeGroup ref="security:ref"/>
          <xs:attributeGroup ref="security:ref"/>
       </xs:complexType>
       </xs:complexType>
    </xs:element>
    </xs:element>
-
+  
   <xs:attributeGroup name="form-login.attlist">
   <xs:attributeGroup name="form-login.attlist">
       <xs:attribute name="login-processing-url" type="xs:token">
       <xs:attribute name="login-processing-url" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -1967,7 +1967,7 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:element name="attribute-exchange">
   <xs:element name="attribute-exchange">
       <xs:annotation>
       <xs:annotation>
          <xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the
          <xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the
@@ -2034,7 +2034,7 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:attributeGroup name="saml2-login.attlist">
   <xs:attributeGroup name="saml2-login.attlist">
       <xs:attribute name="relying-party-registration-repository-ref" type="xs:token">
       <xs:attribute name="relying-party-registration-repository-ref" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -2091,7 +2091,7 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:attributeGroup name="saml2-logout.attlist">
   <xs:attributeGroup name="saml2-logout.attlist">
       <xs:attribute name="logout-url" type="xs:token">
       <xs:attribute name="logout-url" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -2544,7 +2544,7 @@
          </xs:simpleType>
          </xs:simpleType>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:attributeGroup name="http-basic.attlist">
   <xs:attributeGroup name="http-basic.attlist">
       <xs:attribute name="entry-point-ref" type="xs:token">
       <xs:attribute name="entry-point-ref" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -2577,7 +2577,7 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:attributeGroup name="session-management.attlist">
   <xs:attributeGroup name="session-management.attlist">
       <xs:attribute name="session-fixation-protection">
       <xs:attribute name="session-fixation-protection">
          <xs:annotation>
          <xs:annotation>
@@ -2633,7 +2633,7 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:attributeGroup name="concurrency-control.attlist">
   <xs:attributeGroup name="concurrency-control.attlist">
       <xs:attribute name="max-sessions" type="xs:token">
       <xs:attribute name="max-sessions" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -2680,7 +2680,7 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:attributeGroup name="remember-me.attlist">
   <xs:attributeGroup name="remember-me.attlist">
       <xs:attribute name="key" type="xs:token">
       <xs:attribute name="key" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -2778,7 +2778,7 @@
   <xs:attributeGroup name="remember-me-data-source-ref">
   <xs:attributeGroup name="remember-me-data-source-ref">
       <xs:attributeGroup ref="security:data-source-ref"/>
       <xs:attributeGroup ref="security:data-source-ref"/>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:attributeGroup name="anonymous.attlist">
   <xs:attributeGroup name="anonymous.attlist">
       <xs:attribute name="key" type="xs:token">
       <xs:attribute name="key" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -2811,8 +2811,8 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
-
+  
+  
   <xs:attributeGroup name="http-port">
   <xs:attributeGroup name="http-port">
       <xs:attribute name="http" use="required" type="xs:token">
       <xs:attribute name="http" use="required" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -2829,7 +2829,7 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:attributeGroup name="x509.attlist">
   <xs:attributeGroup name="x509.attlist">
       <xs:attribute name="subject-principal-regex" type="xs:token">
       <xs:attribute name="subject-principal-regex" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -2966,7 +2966,7 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:attributeGroup name="ap.attlist">
   <xs:attributeGroup name="ap.attlist">
       <xs:attribute name="ref" type="xs:token">
       <xs:attribute name="ref" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -3018,7 +3018,7 @@
          </xs:annotation>
          </xs:annotation>
       </xs:attribute>
       </xs:attribute>
   </xs:attributeGroup>
   </xs:attributeGroup>
-
+  
   <xs:attributeGroup name="user.attlist">
   <xs:attributeGroup name="user.attlist">
       <xs:attribute name="name" use="required" type="xs:token">
       <xs:attribute name="name" use="required" type="xs:token">
          <xs:annotation>
          <xs:annotation>
@@ -3721,6 +3721,7 @@
       <xs:restriction base="xs:token">
       <xs:restriction base="xs:token">
          <xs:enumeration value="FIRST"/>
          <xs:enumeration value="FIRST"/>
          <xs:enumeration value="DISABLE_ENCODE_URL_FILTER"/>
          <xs:enumeration value="DISABLE_ENCODE_URL_FILTER"/>
+         <xs:enumeration value="FORCE_EAGER_SESSION_FILTER"/>
          <xs:enumeration value="CHANNEL_FILTER"/>
          <xs:enumeration value="CHANNEL_FILTER"/>
          <xs:enumeration value="SECURITY_CONTEXT_FILTER"/>
          <xs:enumeration value="SECURITY_CONTEXT_FILTER"/>
          <xs:enumeration value="CONCURRENT_SESSION_FILTER"/>
          <xs:enumeration value="CONCURRENT_SESSION_FILTER"/>
@@ -3760,4 +3761,4 @@
          <xs:enumeration value="LAST"/>
          <xs:enumeration value="LAST"/>
       </xs:restriction>
       </xs:restriction>
   </xs:simpleType>
   </xs:simpleType>
-</xs:schema>
+</xs:schema>

+ 1 - 1
config/src/test/java/org/springframework/security/config/annotation/web/builders/FilterOrderRegistrationTests.java

@@ -53,7 +53,7 @@ public class FilterOrderRegistrationTests {
 
 
 	@Test
 	@Test
 	public void putWhenPredefinedFilterThenDoesNotOverride() {
 	public void putWhenPredefinedFilterThenDoesNotOverride() {
-		int position = 200;
+		int position = 300;
 		Integer predefinedFilterOrderBefore = this.filterOrderRegistration.getOrder(ChannelProcessingFilter.class);
 		Integer predefinedFilterOrderBefore = this.filterOrderRegistration.getOrder(ChannelProcessingFilter.class);
 		this.filterOrderRegistration.put(MyFilter.class, position);
 		this.filterOrderRegistration.put(MyFilter.class, position);
 		Integer myFilterOrder = this.filterOrderRegistration.getOrder(MyFilter.class);
 		Integer myFilterOrder = this.filterOrderRegistration.getOrder(MyFilter.class);

+ 1 - 0
docs/modules/ROOT/pages/servlet/architecture.adoc

@@ -165,6 +165,7 @@ However, there are times that it is beneficial to know the ordering
 
 
 Below is a comprehensive list of Spring Security Filter ordering:
 Below is a comprehensive list of Spring Security Filter ordering:
 
 
+* xref:servlet/authentication/session-management.adoc#session-mgmt-force-session-creation[`ForceEagerSessionCreationFilter`]
 * ChannelProcessingFilter
 * ChannelProcessingFilter
 * WebAsyncManagerIntegrationFilter
 * WebAsyncManagerIntegrationFilter
 * SecurityContextPersistenceFilter
 * SecurityContextPersistenceFilter

+ 29 - 0
docs/modules/ROOT/pages/servlet/authentication/session-management.adoc

@@ -3,6 +3,35 @@
 HTTP session related functionality is handled by a combination of the `SessionManagementFilter` and the `SessionAuthenticationStrategy` interface, which the filter delegates to.
 HTTP session related functionality is handled by a combination of the `SessionManagementFilter` and the `SessionAuthenticationStrategy` interface, which the filter delegates to.
 Typical usage includes session-fixation protection attack prevention, detection of session timeouts and restrictions on how many sessions an authenticated user may have open concurrently.
 Typical usage includes session-fixation protection attack prevention, detection of session timeouts and restrictions on how many sessions an authenticated user may have open concurrently.
 
 
+[[session-mgmt-force-session-creation]]
+== Force Eager Session Creation
+
+At times it can be valuable to eagerly create sessions.
+This can be done by using the {security-api-url}org/springframework/security/web/session/ForceEagerSessionCreationFilter.html[`ForceEagerSessionCreationFilter`] which can be configured using:
+
+====
+.Java
+[source,java,role="primary"]
+----
+@Bean
+public SecurityFilterChain filterChain(HttpSecurity http) {
+    http
+        .sessionManagement(session -> session
+            .sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
+        );
+    return http.build();
+}
+----
+
+.XML
+[source,xml,role="secondary"]
+----
+<http create-session="ALWAYS">
+
+</http>
+----
+====
+
 == Detecting Timeouts
 == Detecting Timeouts
 You can configure Spring Security to detect the submission of an invalid session ID and redirect the user to an appropriate URL.
 You can configure Spring Security to detect the submission of an invalid session ID and redirect the user to an appropriate URL.
 This is achieved through the `session-management` element:
 This is achieved through the `session-management` element:

+ 4 - 0
docs/modules/ROOT/pages/servlet/configuration/xml-namespace.adoc

@@ -257,6 +257,10 @@ The filters are listed in the order in which they occur in the filter chain.
 | `DisableEncodeUrlFilter`
 | `DisableEncodeUrlFilter`
 | `http@disable-url-rewriting`
 | `http@disable-url-rewriting`
 
 
+| FORCE_EAGER_SESSION_FILTER
+| `ForceEagerSessionCreationFilter`
+| `http@create-session="ALWAYS"`
+
 |  CHANNEL_FILTER
 |  CHANNEL_FILTER
 | `ChannelProcessingFilter`
 | `ChannelProcessingFilter`
 | `http/intercept-url@requires-channel`
 | `http/intercept-url@requires-channel`

+ 48 - 0
web/src/main/java/org/springframework/security/web/session/ForceEagerSessionCreationFilter.java

@@ -0,0 +1,48 @@
+/*
+ * Copyright 2002-2022 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.session;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.springframework.core.log.LogMessage;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+/**
+ * Eagerly creates {@link HttpSession} if it does not already exist.
+ *
+ * @author Rob Winch
+ * @since 5.7
+ */
+public class ForceEagerSessionCreationFilter extends OncePerRequestFilter {
+
+	@Override
+	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+			throws ServletException, IOException {
+		HttpSession session = request.getSession();
+		if (this.logger.isDebugEnabled() && session.isNew()) {
+			this.logger.debug(LogMessage.format("Created session eagerly"));
+		}
+		filterChain.doFilter(request, response);
+	}
+
+}

+ 41 - 0
web/src/test/java/org/springframework/security/web/session/ForceEagerSessionCreationFilterTests.java

@@ -0,0 +1,41 @@
+/*
+ * Copyright 2002-2022 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.web.session;
+
+import org.junit.jupiter.api.Test;
+
+import org.springframework.mock.web.MockFilterChain;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ForceEagerSessionCreationFilterTests {
+
+	@Test
+	void createsSession() throws Exception {
+		ForceEagerSessionCreationFilter filter = new ForceEagerSessionCreationFilter();
+		MockHttpServletRequest request = new MockHttpServletRequest();
+		MockFilterChain chain = new MockFilterChain();
+
+		filter.doFilter(request, new MockHttpServletResponse(), chain);
+
+		assertThat(request.getSession(false)).isNotNull();
+		assertThat(chain.getRequest()).isEqualTo(request);
+	}
+
+}