mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-30 22:28:46 +00:00 
			
		
		
		
	Add DisableUrlRewritingFilter
Closes gh-11084
This commit is contained in:
		
							parent
							
								
									32b83aae63
								
							
						
					
					
						commit
						39b0620a84
					
				| @ -42,6 +42,7 @@ import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter; | |||||||
| import org.springframework.security.web.savedrequest.RequestCacheAwareFilter; | 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.SessionManagementFilter; | import org.springframework.security.web.session.SessionManagementFilter; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -124,6 +125,7 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> | |||||||
| 	 * The ordering of the Filters is: | 	 * The ordering of the Filters is: | ||||||
| 	 * | 	 * | ||||||
| 	 * <ul> | 	 * <ul> | ||||||
|  | 	 * <li>{@link DisableEncodeUrlFilter}</li> | ||||||
| 	 * <li>{@link ChannelProcessingFilter}</li> | 	 * <li>{@link ChannelProcessingFilter}</li> | ||||||
| 	 * <li>{@link SecurityContextPersistenceFilter}</li> | 	 * <li>{@link SecurityContextPersistenceFilter}</li> | ||||||
| 	 * <li>{@link LogoutFilter}</li> | 	 * <li>{@link LogoutFilter}</li> | ||||||
|  | |||||||
| @ -46,6 +46,7 @@ import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter; | |||||||
| import org.springframework.security.web.savedrequest.RequestCacheAwareFilter; | 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.SessionManagementFilter; | import org.springframework.security.web.session.SessionManagementFilter; | ||||||
| import org.springframework.web.filter.CorsFilter; | import org.springframework.web.filter.CorsFilter; | ||||||
| 
 | 
 | ||||||
| @ -68,6 +69,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(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()); | ||||||
|  | |||||||
| @ -52,6 +52,7 @@ import org.springframework.security.web.context.SecurityContextRepository; | |||||||
| import org.springframework.security.web.savedrequest.NullRequestCache; | 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.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; | ||||||
| @ -376,6 +377,9 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>> | |||||||
| 			concurrentSessionFilter = postProcess(concurrentSessionFilter); | 			concurrentSessionFilter = postProcess(concurrentSessionFilter); | ||||||
| 			http.addFilter(concurrentSessionFilter); | 			http.addFilter(concurrentSessionFilter); | ||||||
| 		} | 		} | ||||||
|  | 		if (!this.enableSessionUrlRewriting) { | ||||||
|  | 			http.addFilter(new DisableEncodeUrlFilter()); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private ConcurrentSessionFilter createConcurrencyFilter(H http) { | 	private ConcurrentSessionFilter createConcurrencyFilter(H http) { | ||||||
|  | |||||||
| @ -68,6 +68,7 @@ import org.springframework.security.web.savedrequest.NullRequestCache; | |||||||
| import org.springframework.security.web.savedrequest.RequestCacheAwareFilter; | 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.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; | ||||||
| @ -179,6 +180,8 @@ class HttpConfigurationBuilder { | |||||||
| 
 | 
 | ||||||
| 	private BeanDefinition csrfFilter; | 	private BeanDefinition csrfFilter; | ||||||
| 
 | 
 | ||||||
|  | 	private BeanDefinition disableUrlRewriteFilter; | ||||||
|  | 
 | ||||||
| 	private BeanDefinition wellKnownChangePasswordRedirectFilter; | 	private BeanDefinition wellKnownChangePasswordRedirectFilter; | ||||||
| 
 | 
 | ||||||
| 	private BeanMetadataElement csrfLogoutHandler; | 	private BeanMetadataElement csrfLogoutHandler; | ||||||
| @ -204,6 +207,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); | ||||||
|  | 		createDisableEncodeUrlFilter(); | ||||||
| 		createCsrfFilter(); | 		createCsrfFilter(); | ||||||
| 		createSecurityPersistence(); | 		createSecurityPersistence(); | ||||||
| 		createSessionManagementFilters(); | 		createSessionManagementFilters(); | ||||||
| @ -319,10 +323,6 @@ class HttpConfigurationBuilder { | |||||||
| 
 | 
 | ||||||
| 	private void createSecurityContextRepository() { | 	private void createSecurityContextRepository() { | ||||||
| 		String repoRef = this.httpElt.getAttribute(ATT_SECURITY_CONTEXT_REPOSITORY); | 		String repoRef = this.httpElt.getAttribute(ATT_SECURITY_CONTEXT_REPOSITORY); | ||||||
| 		String disableUrlRewriting = this.httpElt.getAttribute(ATT_DISABLE_URL_REWRITING); |  | ||||||
| 		if (!StringUtils.hasText(disableUrlRewriting)) { |  | ||||||
| 			disableUrlRewriting = "true"; |  | ||||||
| 		} |  | ||||||
| 		if (!StringUtils.hasText(repoRef)) { | 		if (!StringUtils.hasText(repoRef)) { | ||||||
| 			BeanDefinitionBuilder contextRepo; | 			BeanDefinitionBuilder contextRepo; | ||||||
| 			if (this.sessionPolicy == SessionCreationPolicy.STATELESS) { | 			if (this.sessionPolicy == SessionCreationPolicy.STATELESS) { | ||||||
| @ -340,7 +340,7 @@ class HttpConfigurationBuilder { | |||||||
| 				default: | 				default: | ||||||
| 					contextRepo.addPropertyValue("allowSessionCreation", Boolean.TRUE); | 					contextRepo.addPropertyValue("allowSessionCreation", Boolean.TRUE); | ||||||
| 				} | 				} | ||||||
| 				if ("true".equals(disableUrlRewriting)) { | 				if (isDisableUrlRewriting()) { | ||||||
| 					contextRepo.addPropertyValue("disableUrlRewriting", Boolean.TRUE); | 					contextRepo.addPropertyValue("disableUrlRewriting", Boolean.TRUE); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| @ -352,6 +352,11 @@ class HttpConfigurationBuilder { | |||||||
| 		this.contextRepoRef = new RuntimeBeanReference(repoRef); | 		this.contextRepoRef = new RuntimeBeanReference(repoRef); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	private boolean isDisableUrlRewriting() { | ||||||
|  | 		String disableUrlRewriting = this.httpElt.getAttribute(ATT_DISABLE_URL_REWRITING); | ||||||
|  | 		return !"false".equals(disableUrlRewriting); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	private void createSecurityContextHolderFilter() { | 	private void createSecurityContextHolderFilter() { | ||||||
| 		BeanDefinitionBuilder filter = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextHolderFilter.class); | 		BeanDefinitionBuilder filter = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextHolderFilter.class); | ||||||
| 		filter.addConstructorArgValue(this.contextRepoRef); | 		filter.addConstructorArgValue(this.contextRepoRef); | ||||||
| @ -718,6 +723,12 @@ class HttpConfigurationBuilder { | |||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	private void createDisableEncodeUrlFilter() { | ||||||
|  | 		if (isDisableUrlRewriting()) { | ||||||
|  | 			this.disableUrlRewriteFilter = new RootBeanDefinition(DisableEncodeUrlFilter.class); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	private void createCsrfFilter() { | 	private void createCsrfFilter() { | ||||||
| 		Element elmt = DomUtils.getChildElementByTagName(this.httpElt, Elements.CSRF); | 		Element elmt = DomUtils.getChildElementByTagName(this.httpElt, Elements.CSRF); | ||||||
| 		this.csrfParser = new CsrfBeanDefinitionParser(); | 		this.csrfParser = new CsrfBeanDefinitionParser(); | ||||||
| @ -757,6 +768,9 @@ class HttpConfigurationBuilder { | |||||||
| 
 | 
 | ||||||
| 	List<OrderDecorator> getFilters() { | 	List<OrderDecorator> getFilters() { | ||||||
| 		List<OrderDecorator> filters = new ArrayList<>(); | 		List<OrderDecorator> filters = new ArrayList<>(); | ||||||
|  | 		if (this.disableUrlRewriteFilter != null) { | ||||||
|  | 			filters.add(new OrderDecorator(this.disableUrlRewriteFilter, SecurityFilters.DISABLE_ENCODE_URL_FILTER)); | ||||||
|  | 		} | ||||||
| 		if (this.cpf != null) { | 		if (this.cpf != null) { | ||||||
| 			filters.add(new OrderDecorator(this.cpf, SecurityFilters.CHANNEL_FILTER)); | 			filters.add(new OrderDecorator(this.cpf, SecurityFilters.CHANNEL_FILTER)); | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -29,6 +29,8 @@ enum SecurityFilters { | |||||||
| 
 | 
 | ||||||
| 	FIRST(Integer.MIN_VALUE), | 	FIRST(Integer.MIN_VALUE), | ||||||
| 
 | 
 | ||||||
|  | 	DISABLE_ENCODE_URL_FILTER, | ||||||
|  | 
 | ||||||
| 	CHANNEL_FILTER, | 	CHANNEL_FILTER, | ||||||
| 
 | 
 | ||||||
| 	SECURITY_CONTEXT_FILTER, | 	SECURITY_CONTEXT_FILTER, | ||||||
|  | |||||||
| @ -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" | "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" | "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" | ||||||
|  | |||||||
| @ -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> | ||||||
| @ -3720,6 +3720,7 @@ | |||||||
|   <xs:simpleType name="named-security-filter"> |   <xs:simpleType name="named-security-filter"> | ||||||
|       <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="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"/> | ||||||
| @ -3759,4 +3760,4 @@ | |||||||
|          <xs:enumeration value="LAST"/> |          <xs:enumeration value="LAST"/> | ||||||
|       </xs:restriction> |       </xs:restriction> | ||||||
|   </xs:simpleType> |   </xs:simpleType> | ||||||
| </xs:schema> | </xs:schema> | ||||||
|  | |||||||
| @ -53,7 +53,7 @@ public class FilterOrderRegistrationTests { | |||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test | ||||||
| 	public void putWhenPredefinedFilterThenDoesNotOverride() { | 	public void putWhenPredefinedFilterThenDoesNotOverride() { | ||||||
| 		int position = 100; | 		int position = 200; | ||||||
| 		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); | ||||||
|  | |||||||
| @ -38,6 +38,7 @@ import org.springframework.security.config.test.SpringTestContextExtension; | |||||||
| import org.springframework.security.core.context.SecurityContext; | import org.springframework.security.core.context.SecurityContext; | ||||||
| import org.springframework.security.core.session.SessionRegistry; | import org.springframework.security.core.session.SessionRegistry; | ||||||
| import org.springframework.security.core.userdetails.PasswordEncodedUser; | import org.springframework.security.core.userdetails.PasswordEncodedUser; | ||||||
|  | import org.springframework.security.web.DefaultSecurityFilterChain; | ||||||
| import org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy; | import org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy; | ||||||
| import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy; | import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy; | ||||||
| import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy; | import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy; | ||||||
| @ -51,19 +52,26 @@ import org.springframework.security.web.session.SessionManagementFilter; | |||||||
| import org.springframework.test.web.servlet.MockMvc; | import org.springframework.test.web.servlet.MockMvc; | ||||||
| import org.springframework.test.web.servlet.MvcResult; | import org.springframework.test.web.servlet.MvcResult; | ||||||
| import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; | import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; | ||||||
|  | import org.springframework.test.web.servlet.setup.MockMvcBuilders; | ||||||
|  | import org.springframework.web.bind.annotation.RequestMapping; | ||||||
|  | import org.springframework.web.bind.annotation.RestController; | ||||||
| 
 | 
 | ||||||
| import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||||
| import static org.mockito.ArgumentMatchers.any; | import static org.mockito.ArgumentMatchers.any; | ||||||
| import static org.mockito.BDDMockito.given; | import static org.mockito.BDDMockito.given; | ||||||
|  | import static org.mockito.Mockito.atLeastOnce; | ||||||
| import static org.mockito.Mockito.mock; | import static org.mockito.Mockito.mock; | ||||||
|  | import static org.mockito.Mockito.never; | ||||||
| import static org.mockito.Mockito.spy; | import static org.mockito.Mockito.spy; | ||||||
| import static org.mockito.Mockito.verify; | import static org.mockito.Mockito.verify; | ||||||
| import static org.mockito.Mockito.verifyNoInteractions; | import static org.mockito.Mockito.verifyNoInteractions; | ||||||
| import static org.springframework.security.config.Customizer.withDefaults; | import static org.springframework.security.config.Customizer.withDefaults; | ||||||
| import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; | ||||||
| import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; | ||||||
|  | import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; | ||||||
| import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; | ||||||
| import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; | ||||||
|  | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; | ||||||
| import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; | ||||||
| import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||||||
| 
 | 
 | ||||||
| @ -295,6 +303,46 @@ public class SessionManagementConfigurerTests { | |||||||
| 		verifyNoInteractions(SessionRegistryTwoBeansConfig.SESSION_REGISTRY_TWO); | 		verifyNoInteractions(SessionRegistryTwoBeansConfig.SESSION_REGISTRY_TWO); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void whenEnableSessionUrlRewritingTrueThenEncodeNotInvoked() throws Exception { | ||||||
|  | 		this.spring.register(EnableUrlRewriteConfig.class).autowire(); | ||||||
|  | 		// @formatter:off | ||||||
|  | 		this.mvc = MockMvcBuilders.webAppContextSetup(this.spring.getContext()) | ||||||
|  | 			.addFilters((request, response, chain) -> { | ||||||
|  | 				HttpServletResponse responseToSpy = spy((HttpServletResponse) response); | ||||||
|  | 				chain.doFilter(request, responseToSpy); | ||||||
|  | 				verify(responseToSpy, atLeastOnce()).encodeRedirectURL(any()); | ||||||
|  | 				verify(responseToSpy, atLeastOnce()).encodeRedirectUrl(any()); | ||||||
|  | 				verify(responseToSpy, atLeastOnce()).encodeURL(any()); | ||||||
|  | 				verify(responseToSpy, atLeastOnce()).encodeUrl(any()); | ||||||
|  | 			}) | ||||||
|  | 			.apply(springSecurity()) | ||||||
|  | 			.build(); | ||||||
|  | 		// @formatter:on | ||||||
|  | 
 | ||||||
|  | 		this.mvc.perform(get("/")).andExpect(content().string("encoded")); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void whenDefaultThenEncodeNotInvoked() throws Exception { | ||||||
|  | 		this.spring.register(DefaultUrlRewriteConfig.class).autowire(); | ||||||
|  | 		// @formatter:off | ||||||
|  | 		this.mvc = MockMvcBuilders.webAppContextSetup(this.spring.getContext()) | ||||||
|  | 			.addFilters((request, response, chain) -> { | ||||||
|  | 				HttpServletResponse responseToSpy = spy((HttpServletResponse) response); | ||||||
|  | 				chain.doFilter(request, responseToSpy); | ||||||
|  | 				verify(responseToSpy, never()).encodeRedirectURL(any()); | ||||||
|  | 				verify(responseToSpy, never()).encodeRedirectUrl(any()); | ||||||
|  | 				verify(responseToSpy, never()).encodeURL(any()); | ||||||
|  | 				verify(responseToSpy, never()).encodeUrl(any()); | ||||||
|  | 			}) | ||||||
|  | 			.apply(springSecurity()) | ||||||
|  | 			.build(); | ||||||
|  | 		// @formatter:on | ||||||
|  | 
 | ||||||
|  | 		this.mvc.perform(get("/")).andExpect(content().string("encoded")); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@EnableWebSecurity | 	@EnableWebSecurity | ||||||
| 	static class SessionManagementRequestCacheConfig extends WebSecurityConfigurerAdapter { | 	static class SessionManagementRequestCacheConfig extends WebSecurityConfigurerAdapter { | ||||||
| 
 | 
 | ||||||
| @ -569,4 +617,49 @@ public class SessionManagementConfigurerTests { | |||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@EnableWebSecurity | ||||||
|  | 	static class DefaultUrlRewriteConfig { | ||||||
|  | 
 | ||||||
|  | 		@Bean | ||||||
|  | 		DefaultSecurityFilterChain configure(HttpSecurity http) throws Exception { | ||||||
|  | 			return http.build(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Bean | ||||||
|  | 		EncodesUrls encodesUrls() { | ||||||
|  | 			return new EncodesUrls(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@EnableWebSecurity | ||||||
|  | 	static class EnableUrlRewriteConfig { | ||||||
|  | 
 | ||||||
|  | 		@Bean | ||||||
|  | 		DefaultSecurityFilterChain configure(HttpSecurity http) throws Exception { | ||||||
|  | 			http.sessionManagement((sessions) -> sessions.enableSessionUrlRewriting(true)); | ||||||
|  | 			return http.build(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Bean | ||||||
|  | 		EncodesUrls encodesUrls() { | ||||||
|  | 			return new EncodesUrls(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@RestController | ||||||
|  | 	static class EncodesUrls { | ||||||
|  | 
 | ||||||
|  | 		@RequestMapping("/") | ||||||
|  | 		String encoded(HttpServletResponse response) { | ||||||
|  | 			response.encodeURL("/foo"); | ||||||
|  | 			response.encodeUrl("/foo"); | ||||||
|  | 			response.encodeRedirectURL("/foo"); | ||||||
|  | 			response.encodeRedirectUrl("/foo"); | ||||||
|  | 			return "encoded"; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -103,6 +103,7 @@ import org.springframework.security.web.header.HeaderWriterFilter; | |||||||
| import org.springframework.security.web.savedrequest.RequestCache; | import org.springframework.security.web.savedrequest.RequestCache; | ||||||
| import org.springframework.security.web.savedrequest.RequestCacheAwareFilter; | 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.DisableEncodeUrlFilter; | ||||||
| import org.springframework.security.web.session.SessionManagementFilter; | import org.springframework.security.web.session.SessionManagementFilter; | ||||||
| import org.springframework.test.util.ReflectionTestUtils; | import org.springframework.test.util.ReflectionTestUtils; | ||||||
| import org.springframework.test.web.servlet.MockMvc; | import org.springframework.test.web.servlet.MockMvc; | ||||||
| @ -121,6 +122,8 @@ import static org.mockito.BDDMockito.given; | |||||||
| import static org.mockito.BDDMockito.willAnswer; | import static org.mockito.BDDMockito.willAnswer; | ||||||
| import static org.mockito.Mockito.atLeastOnce; | import static org.mockito.Mockito.atLeastOnce; | ||||||
| import static org.mockito.Mockito.mock; | import static org.mockito.Mockito.mock; | ||||||
|  | import static org.mockito.Mockito.never; | ||||||
|  | import static org.mockito.Mockito.spy; | ||||||
| import static org.mockito.Mockito.verify; | import static org.mockito.Mockito.verify; | ||||||
| import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; | ||||||
| import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; | ||||||
| @ -540,6 +543,28 @@ public class MiscHttpConfigTests { | |||||||
| 		assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost/login"); | 		assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost/login"); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void configureWhenUsingDisableUrlRewritingAndCustomRepositoryThenRedirectIsNotEncodedByResponse() | ||||||
|  | 			throws IOException, ServletException { | ||||||
|  | 		this.spring.configLocations(xml("DisableUrlRewriting-NullSecurityContextRepository")).autowire(); | ||||||
|  | 		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/"); | ||||||
|  | 		MockHttpServletResponse responseToSpy = spy(new MockHttpServletResponse()); | ||||||
|  | 		FilterChainProxy proxy = this.spring.getContext().getBean(FilterChainProxy.class); | ||||||
|  | 		proxy.doFilter(request, responseToSpy, (req, resp) -> { | ||||||
|  | 			HttpServletResponse httpResponse = (HttpServletResponse) resp; | ||||||
|  | 			httpResponse.encodeUrl("/"); | ||||||
|  | 			httpResponse.encodeURL("/"); | ||||||
|  | 			httpResponse.encodeRedirectUrl("/"); | ||||||
|  | 			httpResponse.encodeRedirectURL("/"); | ||||||
|  | 			httpResponse.getWriter().write("encodeRedirect"); | ||||||
|  | 		}); | ||||||
|  | 		verify(responseToSpy, never()).encodeRedirectURL(any()); | ||||||
|  | 		verify(responseToSpy, never()).encodeRedirectUrl(any()); | ||||||
|  | 		verify(responseToSpy, never()).encodeURL(any()); | ||||||
|  | 		verify(responseToSpy, never()).encodeUrl(any()); | ||||||
|  | 		assertThat(responseToSpy.getContentAsString()).isEqualTo("encodeRedirect"); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@Test | 	@Test | ||||||
| 	public void configureWhenUserDetailsServiceInParentContextThenLocatesSuccessfully() { | 	public void configureWhenUserDetailsServiceInParentContextThenLocatesSuccessfully() { | ||||||
| 		assertThatExceptionOfType(BeansException.class).isThrownBy( | 		assertThatExceptionOfType(BeansException.class).isThrownBy( | ||||||
| @ -755,6 +780,7 @@ public class MiscHttpConfigTests { | |||||||
| 
 | 
 | ||||||
| 	private void assertThatFiltersMatchExpectedAutoConfigList(String url) { | 	private void assertThatFiltersMatchExpectedAutoConfigList(String url) { | ||||||
| 		Iterator<Filter> filters = getFilters(url).iterator(); | 		Iterator<Filter> filters = getFilters(url).iterator(); | ||||||
|  | 		assertThat(filters.next()).isInstanceOf(DisableEncodeUrlFilter.class); | ||||||
| 		assertThat(filters.next()).isInstanceOf(SecurityContextPersistenceFilter.class); | 		assertThat(filters.next()).isInstanceOf(SecurityContextPersistenceFilter.class); | ||||||
| 		assertThat(filters.next()).isInstanceOf(WebAsyncManagerIntegrationFilter.class); | 		assertThat(filters.next()).isInstanceOf(WebAsyncManagerIntegrationFilter.class); | ||||||
| 		assertThat(filters.next()).isInstanceOf(HeaderWriterFilter.class); | 		assertThat(filters.next()).isInstanceOf(HeaderWriterFilter.class); | ||||||
|  | |||||||
| @ -0,0 +1,32 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- | ||||||
|  |   ~ Copyright 2002-2018 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. | ||||||
|  |   --> | ||||||
|  | 
 | ||||||
|  | <b:beans xmlns:b="http://www.springframework.org/schema/beans" | ||||||
|  | 		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||||
|  | 		xmlns="http://www.springframework.org/schema/security" | ||||||
|  | 		xsi:schemaLocation=" | ||||||
|  | 			http://www.springframework.org/schema/security | ||||||
|  | 			https://www.springframework.org/schema/security/spring-security.xsd | ||||||
|  | 			http://www.springframework.org/schema/beans | ||||||
|  | 			https://www.springframework.org/schema/beans/spring-beans.xsd"> | ||||||
|  | 
 | ||||||
|  | 	<http auto-config="true" disable-url-rewriting="true" security-context-repository-ref="securityContextRepository"> | ||||||
|  | 		<intercept-url pattern="/**" access="permitAll"/> | ||||||
|  | 	</http> | ||||||
|  | 	<b:bean id="securityContextRepository" class="org.springframework.security.web.context.NullSecurityContextRepository"/> | ||||||
|  | 	<b:import resource="userservice.xml"/> | ||||||
|  | </b:beans> | ||||||
| @ -253,6 +253,10 @@ The filters are listed in the order in which they occur in the filter chain. | |||||||
| |=== | |=== | ||||||
| | Alias | Filter Class | Namespace Element or Attribute | | Alias | Filter Class | Namespace Element or Attribute | ||||||
| 
 | 
 | ||||||
|  | | DISABLE_ENCODE_URL_FILTER | ||||||
|  | | `DisableEncodeUrlFilter` | ||||||
|  | | `http@disable-url-rewriting` | ||||||
|  | 
 | ||||||
| |  CHANNEL_FILTER | |  CHANNEL_FILTER | ||||||
| | `ChannelProcessingFilter` | | `ChannelProcessingFilter` | ||||||
| | `http/intercept-url@requires-channel` | | `http/intercept-url@requires-channel` | ||||||
|  | |||||||
| @ -0,0 +1,86 @@ | |||||||
|  | /* | ||||||
|  |  * 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.HttpServletResponseWrapper; | ||||||
|  | 
 | ||||||
|  | import org.springframework.web.filter.OncePerRequestFilter; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Disables encoding URLs using the {@link HttpServletResponse} to prevent including the | ||||||
|  |  * session id in URLs which is not considered URL because the session id can be leaked in | ||||||
|  |  * things like HTTP access logs. | ||||||
|  |  * | ||||||
|  |  * @author Rob Winch | ||||||
|  |  * @since 5.7 | ||||||
|  |  */ | ||||||
|  | public class DisableEncodeUrlFilter extends OncePerRequestFilter { | ||||||
|  | 
 | ||||||
|  | 	@Override | ||||||
|  | 	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) | ||||||
|  | 			throws ServletException, IOException { | ||||||
|  | 		filterChain.doFilter(request, new DisableEncodeUrlResponseWrapper(response)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Disables URL rewriting for the {@link HttpServletResponse} to prevent including the | ||||||
|  | 	 * session id in URLs which is not considered URL because the session id can be leaked | ||||||
|  | 	 * in things like HTTP access logs. | ||||||
|  | 	 * | ||||||
|  | 	 * @author Rob Winch | ||||||
|  | 	 * @since 5.7 | ||||||
|  | 	 */ | ||||||
|  | 	private static final class DisableEncodeUrlResponseWrapper extends HttpServletResponseWrapper { | ||||||
|  | 
 | ||||||
|  | 		/** | ||||||
|  | 		 * Constructs a response adaptor wrapping the given response. | ||||||
|  | 		 * @param response the {@link HttpServletResponse} to be wrapped. | ||||||
|  | 		 * @throws IllegalArgumentException if the response is null | ||||||
|  | 		 */ | ||||||
|  | 		private DisableEncodeUrlResponseWrapper(HttpServletResponse response) { | ||||||
|  | 			super(response); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public String encodeRedirectUrl(String url) { | ||||||
|  | 			return url; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public String encodeRedirectURL(String url) { | ||||||
|  | 			return url; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public String encodeUrl(String url) { | ||||||
|  | 			return url; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public String encodeURL(String url) { | ||||||
|  | 			return url; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,73 @@ | |||||||
|  | /* | ||||||
|  |  * 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.util.function.Consumer; | ||||||
|  | 
 | ||||||
|  | import javax.servlet.http.HttpServletRequest; | ||||||
|  | import javax.servlet.http.HttpServletResponse; | ||||||
|  | 
 | ||||||
|  | import org.junit.jupiter.api.Test; | ||||||
|  | import org.junit.jupiter.api.extension.ExtendWith; | ||||||
|  | import org.mockito.Mock; | ||||||
|  | import org.mockito.junit.jupiter.MockitoExtension; | ||||||
|  | 
 | ||||||
|  | import static org.mockito.Mockito.verifyNoInteractions; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * @author Rob Winch | ||||||
|  |  */ | ||||||
|  | @ExtendWith(MockitoExtension.class) | ||||||
|  | class DisableEncodeUrlFilterTests { | ||||||
|  | 
 | ||||||
|  | 	@Mock | ||||||
|  | 	private HttpServletRequest request; | ||||||
|  | 
 | ||||||
|  | 	@Mock | ||||||
|  | 	private HttpServletResponse response; | ||||||
|  | 
 | ||||||
|  | 	private DisableEncodeUrlFilter filter = new DisableEncodeUrlFilter(); | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	void doFilterDisablesEncodeURL() throws Exception { | ||||||
|  | 		verifyDoFilterDoesNotInteractWithResponse((httpResponse) -> httpResponse.encodeURL("/")); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	void doFilterDisablesEncodeUrl() throws Exception { | ||||||
|  | 		verifyDoFilterDoesNotInteractWithResponse((httpResponse) -> httpResponse.encodeUrl("/")); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	void doFilterDisablesEncodeRedirectURL() throws Exception { | ||||||
|  | 		verifyDoFilterDoesNotInteractWithResponse((httpResponse) -> httpResponse.encodeRedirectURL("/")); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	void doFilterDisablesEncodeRedirectUrl() throws Exception { | ||||||
|  | 		verifyDoFilterDoesNotInteractWithResponse((httpResponse) -> httpResponse.encodeRedirectUrl("/")); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void verifyDoFilterDoesNotInteractWithResponse(Consumer<HttpServletResponse> toInvoke) throws Exception { | ||||||
|  | 		this.filter.doFilter(this.request, this.response, (request, response) -> { | ||||||
|  | 			HttpServletResponse httpResponse = (HttpServletResponse) response; | ||||||
|  | 			toInvoke.accept(httpResponse); | ||||||
|  | 		}); | ||||||
|  | 		verifyNoInteractions(this.response); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user