mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-05-30 08:42:13 +00:00
Add SecurityContextHolderFilter
Closes gh-9635
This commit is contained in:
parent
f9619cef68
commit
972039e65c
@ -37,6 +37,7 @@ import org.springframework.security.web.authentication.ui.DefaultLoginPageGenera
|
|||||||
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
|
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
|
||||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||||
import org.springframework.security.web.authentication.www.DigestAuthenticationFilter;
|
import org.springframework.security.web.authentication.www.DigestAuthenticationFilter;
|
||||||
|
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.request.async.WebAsyncManagerIntegrationFilter;
|
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
|
||||||
import org.springframework.security.web.csrf.CsrfFilter;
|
import org.springframework.security.web.csrf.CsrfFilter;
|
||||||
@ -70,6 +71,7 @@ final class FilterOrderRegistration {
|
|||||||
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());
|
||||||
|
put(SecurityContextHolderFilter.class, order.next());
|
||||||
put(SecurityContextPersistenceFilter.class, order.next());
|
put(SecurityContextPersistenceFilter.class, order.next());
|
||||||
put(HeaderWriterFilter.class, order.next());
|
put(HeaderWriterFilter.class, order.next());
|
||||||
put(CorsFilter.class, order.next());
|
put(CorsFilter.class, order.next());
|
||||||
|
@ -37,6 +37,7 @@ import org.springframework.security.web.authentication.SavedRequestAwareAuthenti
|
|||||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||||
|
import org.springframework.security.web.context.SecurityContextRepository;
|
||||||
import org.springframework.security.web.savedrequest.RequestCache;
|
import org.springframework.security.web.savedrequest.RequestCache;
|
||||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
|
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
|
||||||
@ -144,6 +145,11 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
|
|||||||
return getSelf();
|
return getSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public T securityContextRepository(SecurityContextRepository securityContextRepository) {
|
||||||
|
this.authFilter.setSecurityContextRepository(securityContextRepository);
|
||||||
|
return getSelf();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the {@link RequestMatcher} given a loginProcessingUrl
|
* Create the {@link RequestMatcher} given a loginProcessingUrl
|
||||||
* @param loginProcessingUrl creates the {@link RequestMatcher} based upon the
|
* @param loginProcessingUrl creates the {@link RequestMatcher} based upon the
|
||||||
@ -285,6 +291,12 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
|
|||||||
if (rememberMeServices != null) {
|
if (rememberMeServices != null) {
|
||||||
this.authFilter.setRememberMeServices(rememberMeServices);
|
this.authFilter.setRememberMeServices(rememberMeServices);
|
||||||
}
|
}
|
||||||
|
SecurityContextConfigurer securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);
|
||||||
|
if (securityContextConfigurer != null && securityContextConfigurer.isRequireExplicitSave()) {
|
||||||
|
SecurityContextRepository securityContextRepository = securityContextConfigurer
|
||||||
|
.getSecurityContextRepository();
|
||||||
|
this.authFilter.setSecurityContextRepository(securityContextRepository);
|
||||||
|
}
|
||||||
F filter = postProcess(this.authFilter);
|
F filter = postProcess(this.authFilter);
|
||||||
http.addFilter(filter);
|
http.addFilter(filter);
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import org.springframework.security.config.http.SessionCreationPolicy;
|
|||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||||
|
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;
|
||||||
|
|
||||||
@ -62,6 +63,8 @@ import org.springframework.security.web.context.SecurityContextRepository;
|
|||||||
public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>>
|
public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
extends AbstractHttpConfigurer<SecurityContextConfigurer<H>, H> {
|
extends AbstractHttpConfigurer<SecurityContextConfigurer<H>, H> {
|
||||||
|
|
||||||
|
private boolean requireExplicitSave;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance
|
* Creates a new instance
|
||||||
* @see HttpSecurity#securityContext()
|
* @see HttpSecurity#securityContext()
|
||||||
@ -79,23 +82,45 @@ public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>>
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public SecurityContextConfigurer<H> requireExplicitSave(boolean requireExplicitSave) {
|
||||||
@SuppressWarnings("unchecked")
|
this.requireExplicitSave = requireExplicitSave;
|
||||||
public void configure(H http) {
|
return this;
|
||||||
SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
|
}
|
||||||
|
|
||||||
|
boolean isRequireExplicitSave() {
|
||||||
|
return this.requireExplicitSave;
|
||||||
|
}
|
||||||
|
|
||||||
|
SecurityContextRepository getSecurityContextRepository() {
|
||||||
|
SecurityContextRepository securityContextRepository = getBuilder()
|
||||||
|
.getSharedObject(SecurityContextRepository.class);
|
||||||
if (securityContextRepository == null) {
|
if (securityContextRepository == null) {
|
||||||
securityContextRepository = new HttpSessionSecurityContextRepository();
|
securityContextRepository = new HttpSessionSecurityContextRepository();
|
||||||
}
|
}
|
||||||
SecurityContextPersistenceFilter securityContextFilter = new SecurityContextPersistenceFilter(
|
return securityContextRepository;
|
||||||
securityContextRepository);
|
}
|
||||||
SessionManagementConfigurer<?> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);
|
|
||||||
SessionCreationPolicy sessionCreationPolicy = (sessionManagement != null)
|
@Override
|
||||||
? sessionManagement.getSessionCreationPolicy() : null;
|
@SuppressWarnings("unchecked")
|
||||||
if (SessionCreationPolicy.ALWAYS == sessionCreationPolicy) {
|
public void configure(H http) {
|
||||||
securityContextFilter.setForceEagerSessionCreation(true);
|
SecurityContextRepository securityContextRepository = getSecurityContextRepository();
|
||||||
|
if (this.requireExplicitSave) {
|
||||||
|
SecurityContextHolderFilter securityContextHolderFilter = postProcess(
|
||||||
|
new SecurityContextHolderFilter(securityContextRepository));
|
||||||
|
http.addFilter(securityContextHolderFilter);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SecurityContextPersistenceFilter securityContextFilter = new SecurityContextPersistenceFilter(
|
||||||
|
securityContextRepository);
|
||||||
|
SessionManagementConfigurer<?> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);
|
||||||
|
SessionCreationPolicy sessionCreationPolicy = (sessionManagement != null)
|
||||||
|
? sessionManagement.getSessionCreationPolicy() : null;
|
||||||
|
if (SessionCreationPolicy.ALWAYS == sessionCreationPolicy) {
|
||||||
|
securityContextFilter.setForceEagerSessionCreation(true);
|
||||||
|
}
|
||||||
|
securityContextFilter = postProcess(securityContextFilter);
|
||||||
|
http.addFilter(securityContextFilter);
|
||||||
}
|
}
|
||||||
securityContextFilter = postProcess(securityContextFilter);
|
|
||||||
http.addFilter(securityContextFilter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -216,8 +216,8 @@ final class AuthenticationConfigBuilder {
|
|||||||
|
|
||||||
AuthenticationConfigBuilder(Element element, boolean forceAutoConfig, ParserContext pc,
|
AuthenticationConfigBuilder(Element element, boolean forceAutoConfig, ParserContext pc,
|
||||||
SessionCreationPolicy sessionPolicy, BeanReference requestCache, BeanReference authenticationManager,
|
SessionCreationPolicy sessionPolicy, BeanReference requestCache, BeanReference authenticationManager,
|
||||||
BeanReference sessionStrategy, BeanReference portMapper, BeanReference portResolver,
|
BeanReference authenticationFilterSecurityContextRepositoryRef, BeanReference sessionStrategy,
|
||||||
BeanMetadataElement csrfLogoutHandler) {
|
BeanReference portMapper, BeanReference portResolver, BeanMetadataElement csrfLogoutHandler) {
|
||||||
this.httpElt = element;
|
this.httpElt = element;
|
||||||
this.pc = pc;
|
this.pc = pc;
|
||||||
this.requestCache = requestCache;
|
this.requestCache = requestCache;
|
||||||
@ -231,9 +231,10 @@ final class AuthenticationConfigBuilder {
|
|||||||
createRememberMeFilter(authenticationManager);
|
createRememberMeFilter(authenticationManager);
|
||||||
createBasicFilter(authenticationManager);
|
createBasicFilter(authenticationManager);
|
||||||
createBearerTokenAuthenticationFilter(authenticationManager);
|
createBearerTokenAuthenticationFilter(authenticationManager);
|
||||||
createFormLoginFilter(sessionStrategy, authenticationManager);
|
createFormLoginFilter(sessionStrategy, authenticationManager, authenticationFilterSecurityContextRepositoryRef);
|
||||||
createOAuth2ClientFilters(sessionStrategy, requestCache, authenticationManager);
|
createOAuth2ClientFilters(sessionStrategy, requestCache, authenticationManager,
|
||||||
createSaml2LoginFilter(authenticationManager);
|
authenticationFilterSecurityContextRepositoryRef);
|
||||||
|
createSaml2LoginFilter(authenticationManager, authenticationFilterSecurityContextRepositoryRef);
|
||||||
createX509Filter(authenticationManager);
|
createX509Filter(authenticationManager);
|
||||||
createJeeFilter(authenticationManager);
|
createJeeFilter(authenticationManager);
|
||||||
createLogoutFilter();
|
createLogoutFilter();
|
||||||
@ -269,7 +270,8 @@ final class AuthenticationConfigBuilder {
|
|||||||
this.rememberMeProviderRef = new RuntimeBeanReference(id);
|
this.rememberMeProviderRef = new RuntimeBeanReference(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void createFormLoginFilter(BeanReference sessionStrategy, BeanReference authManager) {
|
void createFormLoginFilter(BeanReference sessionStrategy, BeanReference authManager,
|
||||||
|
BeanReference authenticationFilterSecurityContextRepositoryRef) {
|
||||||
Element formLoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.FORM_LOGIN);
|
Element formLoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.FORM_LOGIN);
|
||||||
RootBeanDefinition formFilter = null;
|
RootBeanDefinition formFilter = null;
|
||||||
if (formLoginElt != null || this.autoConfig) {
|
if (formLoginElt != null || this.autoConfig) {
|
||||||
@ -285,6 +287,10 @@ final class AuthenticationConfigBuilder {
|
|||||||
if (formFilter != null) {
|
if (formFilter != null) {
|
||||||
formFilter.getPropertyValues().addPropertyValue("allowSessionCreation", this.allowSessionCreation);
|
formFilter.getPropertyValues().addPropertyValue("allowSessionCreation", this.allowSessionCreation);
|
||||||
formFilter.getPropertyValues().addPropertyValue("authenticationManager", authManager);
|
formFilter.getPropertyValues().addPropertyValue("authenticationManager", authManager);
|
||||||
|
if (authenticationFilterSecurityContextRepositoryRef != null) {
|
||||||
|
formFilter.getPropertyValues().addPropertyValue("securityContextRepository",
|
||||||
|
authenticationFilterSecurityContextRepositoryRef);
|
||||||
|
}
|
||||||
// Id is required by login page filter
|
// Id is required by login page filter
|
||||||
this.formFilterId = this.pc.getReaderContext().generateBeanName(formFilter);
|
this.formFilterId = this.pc.getReaderContext().generateBeanName(formFilter);
|
||||||
this.pc.registerBeanComponent(new BeanComponentDefinition(formFilter, this.formFilterId));
|
this.pc.registerBeanComponent(new BeanComponentDefinition(formFilter, this.formFilterId));
|
||||||
@ -293,13 +299,15 @@ final class AuthenticationConfigBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void createOAuth2ClientFilters(BeanReference sessionStrategy, BeanReference requestCache,
|
void createOAuth2ClientFilters(BeanReference sessionStrategy, BeanReference requestCache,
|
||||||
BeanReference authenticationManager) {
|
BeanReference authenticationManager, BeanReference authenticationFilterSecurityContextRepositoryRef) {
|
||||||
createOAuth2LoginFilter(sessionStrategy, authenticationManager);
|
createOAuth2LoginFilter(sessionStrategy, authenticationManager,
|
||||||
createOAuth2ClientFilter(requestCache, authenticationManager);
|
authenticationFilterSecurityContextRepositoryRef);
|
||||||
|
createOAuth2ClientFilter(requestCache, authenticationManager, authenticationFilterSecurityContextRepositoryRef);
|
||||||
registerOAuth2ClientPostProcessors();
|
registerOAuth2ClientPostProcessors();
|
||||||
}
|
}
|
||||||
|
|
||||||
void createOAuth2LoginFilter(BeanReference sessionStrategy, BeanReference authManager) {
|
void createOAuth2LoginFilter(BeanReference sessionStrategy, BeanReference authManager,
|
||||||
|
BeanReference authenticationFilterSecurityContextRepositoryRef) {
|
||||||
Element oauth2LoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OAUTH2_LOGIN);
|
Element oauth2LoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OAUTH2_LOGIN);
|
||||||
if (oauth2LoginElt == null) {
|
if (oauth2LoginElt == null) {
|
||||||
return;
|
return;
|
||||||
@ -311,6 +319,10 @@ final class AuthenticationConfigBuilder {
|
|||||||
BeanDefinition defaultAuthorizedClientRepository = parser.getDefaultAuthorizedClientRepository();
|
BeanDefinition defaultAuthorizedClientRepository = parser.getDefaultAuthorizedClientRepository();
|
||||||
registerDefaultAuthorizedClientRepositoryIfNecessary(defaultAuthorizedClientRepository);
|
registerDefaultAuthorizedClientRepositoryIfNecessary(defaultAuthorizedClientRepository);
|
||||||
oauth2LoginFilterBean.getPropertyValues().addPropertyValue("authenticationManager", authManager);
|
oauth2LoginFilterBean.getPropertyValues().addPropertyValue("authenticationManager", authManager);
|
||||||
|
if (authenticationFilterSecurityContextRepositoryRef != null) {
|
||||||
|
oauth2LoginFilterBean.getPropertyValues().addPropertyValue("securityContextRepository",
|
||||||
|
authenticationFilterSecurityContextRepositoryRef);
|
||||||
|
}
|
||||||
|
|
||||||
// retrieve the other bean result
|
// retrieve the other bean result
|
||||||
BeanDefinition oauth2LoginAuthProvider = parser.getOAuth2LoginAuthenticationProvider();
|
BeanDefinition oauth2LoginAuthProvider = parser.getOAuth2LoginAuthenticationProvider();
|
||||||
@ -340,14 +352,15 @@ final class AuthenticationConfigBuilder {
|
|||||||
this.oauth2LoginOidcAuthenticationProviderRef = new RuntimeBeanReference(oauth2LoginOidcAuthProviderId);
|
this.oauth2LoginOidcAuthenticationProviderRef = new RuntimeBeanReference(oauth2LoginOidcAuthProviderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void createOAuth2ClientFilter(BeanReference requestCache, BeanReference authenticationManager) {
|
void createOAuth2ClientFilter(BeanReference requestCache, BeanReference authenticationManager,
|
||||||
|
BeanReference authenticationFilterSecurityContextRepositoryRef) {
|
||||||
Element oauth2ClientElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OAUTH2_CLIENT);
|
Element oauth2ClientElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OAUTH2_CLIENT);
|
||||||
if (oauth2ClientElt == null) {
|
if (oauth2ClientElt == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.oauth2ClientEnabled = true;
|
this.oauth2ClientEnabled = true;
|
||||||
OAuth2ClientBeanDefinitionParser parser = new OAuth2ClientBeanDefinitionParser(requestCache,
|
OAuth2ClientBeanDefinitionParser parser = new OAuth2ClientBeanDefinitionParser(requestCache,
|
||||||
authenticationManager);
|
authenticationManager, authenticationFilterSecurityContextRepositoryRef);
|
||||||
parser.parse(oauth2ClientElt, this.pc);
|
parser.parse(oauth2ClientElt, this.pc);
|
||||||
BeanDefinition defaultAuthorizedClientRepository = parser.getDefaultAuthorizedClientRepository();
|
BeanDefinition defaultAuthorizedClientRepository = parser.getDefaultAuthorizedClientRepository();
|
||||||
registerDefaultAuthorizedClientRepositoryIfNecessary(defaultAuthorizedClientRepository);
|
registerDefaultAuthorizedClientRepositoryIfNecessary(defaultAuthorizedClientRepository);
|
||||||
@ -392,14 +405,16 @@ final class AuthenticationConfigBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createSaml2LoginFilter(BeanReference authenticationManager) {
|
private void createSaml2LoginFilter(BeanReference authenticationManager,
|
||||||
|
BeanReference authenticationFilterSecurityContextRepositoryRef) {
|
||||||
Element saml2LoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.SAML2_LOGIN);
|
Element saml2LoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.SAML2_LOGIN);
|
||||||
if (saml2LoginElt == null) {
|
if (saml2LoginElt == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Saml2LoginBeanDefinitionParser parser = new Saml2LoginBeanDefinitionParser(this.csrfIgnoreRequestMatchers,
|
Saml2LoginBeanDefinitionParser parser = new Saml2LoginBeanDefinitionParser(this.csrfIgnoreRequestMatchers,
|
||||||
this.portMapper, this.portResolver, this.requestCache, this.allowSessionCreation, authenticationManager,
|
this.portMapper, this.portResolver, this.requestCache, this.allowSessionCreation, authenticationManager,
|
||||||
this.authenticationProviders, this.defaultEntryPointMappings);
|
authenticationFilterSecurityContextRepositoryRef, this.authenticationProviders,
|
||||||
|
this.defaultEntryPointMappings);
|
||||||
BeanDefinition saml2WebSsoAuthenticationFilter = parser.parse(saml2LoginElt, this.pc);
|
BeanDefinition saml2WebSsoAuthenticationFilter = parser.parse(saml2LoginElt, this.pc);
|
||||||
this.saml2AuthorizationRequestFilter = parser.getSaml2WebSsoAuthenticationRequestFilter();
|
this.saml2AuthorizationRequestFilter = parser.getSaml2WebSsoAuthenticationRequestFilter();
|
||||||
|
|
||||||
|
@ -59,6 +59,7 @@ import org.springframework.security.web.authentication.session.RegisterSessionAu
|
|||||||
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
|
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
|
||||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||||
import org.springframework.security.web.context.NullSecurityContextRepository;
|
import org.springframework.security.web.context.NullSecurityContextRepository;
|
||||||
|
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.request.async.WebAsyncManagerIntegrationFilter;
|
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
|
||||||
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
||||||
@ -104,6 +105,8 @@ class HttpConfigurationBuilder {
|
|||||||
|
|
||||||
private static final String ATT_SECURITY_CONTEXT_REPOSITORY = "security-context-repository-ref";
|
private static final String ATT_SECURITY_CONTEXT_REPOSITORY = "security-context-repository-ref";
|
||||||
|
|
||||||
|
private static final String ATT_SECURITY_CONTEXT_EXPLICIT_SAVE = "security-context-explicit-save";
|
||||||
|
|
||||||
private static final String ATT_INVALID_SESSION_STRATEGY_REF = "invalid-session-strategy-ref";
|
private static final String ATT_INVALID_SESSION_STRATEGY_REF = "invalid-session-strategy-ref";
|
||||||
|
|
||||||
private static final String ATT_DISABLE_URL_REWRITING = "disable-url-rewriting";
|
private static final String ATT_DISABLE_URL_REWRITING = "disable-url-rewriting";
|
||||||
@ -202,8 +205,7 @@ class HttpConfigurationBuilder {
|
|||||||
this.sessionPolicy = !StringUtils.hasText(createSession) ? SessionCreationPolicy.IF_REQUIRED
|
this.sessionPolicy = !StringUtils.hasText(createSession) ? SessionCreationPolicy.IF_REQUIRED
|
||||||
: createPolicy(createSession);
|
: createPolicy(createSession);
|
||||||
createCsrfFilter();
|
createCsrfFilter();
|
||||||
createSecurityContextRepository();
|
createSecurityPersistence();
|
||||||
createSecurityContextPersistenceFilter();
|
|
||||||
createSessionManagementFilters();
|
createSessionManagementFilters();
|
||||||
createWebAsyncManagerFilter();
|
createWebAsyncManagerFilter();
|
||||||
createRequestCacheFilter();
|
createRequestCacheFilter();
|
||||||
@ -279,9 +281,27 @@ class HttpConfigurationBuilder {
|
|||||||
return lowerCase ? path.toLowerCase() : path;
|
return lowerCase ? path.toLowerCase() : path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BeanReference getSecurityContextRepositoryForAuthenticationFilters() {
|
||||||
|
return (isExplicitSave()) ? this.contextRepoRef : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createSecurityPersistence() {
|
||||||
|
createSecurityContextRepository();
|
||||||
|
if (isExplicitSave()) {
|
||||||
|
createSecurityContextHolderFilter();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
createSecurityContextPersistenceFilter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isExplicitSave() {
|
||||||
|
String explicitSaveAttr = this.httpElt.getAttribute(ATT_SECURITY_CONTEXT_EXPLICIT_SAVE);
|
||||||
|
return Boolean.parseBoolean(explicitSaveAttr);
|
||||||
|
}
|
||||||
|
|
||||||
private void createSecurityContextPersistenceFilter() {
|
private void createSecurityContextPersistenceFilter() {
|
||||||
BeanDefinitionBuilder scpf = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextPersistenceFilter.class);
|
BeanDefinitionBuilder scpf = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextPersistenceFilter.class);
|
||||||
String disableUrlRewriting = this.httpElt.getAttribute(ATT_DISABLE_URL_REWRITING);
|
|
||||||
switch (this.sessionPolicy) {
|
switch (this.sessionPolicy) {
|
||||||
case ALWAYS:
|
case ALWAYS:
|
||||||
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
|
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
|
||||||
@ -332,6 +352,12 @@ class HttpConfigurationBuilder {
|
|||||||
this.contextRepoRef = new RuntimeBeanReference(repoRef);
|
this.contextRepoRef = new RuntimeBeanReference(repoRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createSecurityContextHolderFilter() {
|
||||||
|
BeanDefinitionBuilder filter = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextHolderFilter.class);
|
||||||
|
filter.addConstructorArgValue(this.contextRepoRef);
|
||||||
|
this.securityContextPersistenceFilter = filter.getBeanDefinition();
|
||||||
|
}
|
||||||
|
|
||||||
private void createSessionManagementFilters() {
|
private void createSessionManagementFilters() {
|
||||||
Element sessionMgmtElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.SESSION_MANAGEMENT);
|
Element sessionMgmtElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.SESSION_MANAGEMENT);
|
||||||
Element sessionCtrlElt = null;
|
Element sessionCtrlElt = null;
|
||||||
|
@ -144,9 +144,11 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
boolean forceAutoConfig = isDefaultHttpConfig(element);
|
boolean forceAutoConfig = isDefaultHttpConfig(element);
|
||||||
HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper,
|
HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper,
|
||||||
portResolver, authenticationManager);
|
portResolver, authenticationManager);
|
||||||
|
httpBldr.getSecurityContextRepositoryForAuthenticationFilters();
|
||||||
AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, forceAutoConfig, pc,
|
AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, forceAutoConfig, pc,
|
||||||
httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager,
|
httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager,
|
||||||
httpBldr.getSessionStrategy(), portMapper, portResolver, httpBldr.getCsrfLogoutHandler());
|
httpBldr.getSecurityContextRepositoryForAuthenticationFilters(), httpBldr.getSessionStrategy(),
|
||||||
|
portMapper, portResolver, httpBldr.getCsrfLogoutHandler());
|
||||||
httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
|
httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
|
||||||
httpBldr.setEntryPoint(authBldr.getEntryPointBean());
|
httpBldr.setEntryPoint(authBldr.getEntryPointBean());
|
||||||
httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());
|
httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());
|
||||||
|
@ -50,6 +50,8 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
|
|
||||||
private final BeanReference authenticationManager;
|
private final BeanReference authenticationManager;
|
||||||
|
|
||||||
|
private final BeanReference authenticationFilterSecurityContextRepositoryRef;
|
||||||
|
|
||||||
private BeanDefinition defaultAuthorizedClientRepository;
|
private BeanDefinition defaultAuthorizedClientRepository;
|
||||||
|
|
||||||
private BeanDefinition authorizationRequestRedirectFilter;
|
private BeanDefinition authorizationRequestRedirectFilter;
|
||||||
@ -58,9 +60,11 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
|
|
||||||
private BeanDefinition authorizationCodeAuthenticationProvider;
|
private BeanDefinition authorizationCodeAuthenticationProvider;
|
||||||
|
|
||||||
OAuth2ClientBeanDefinitionParser(BeanReference requestCache, BeanReference authenticationManager) {
|
OAuth2ClientBeanDefinitionParser(BeanReference requestCache, BeanReference authenticationManager,
|
||||||
|
BeanReference authenticationFilterSecurityContextRepositoryRef) {
|
||||||
this.requestCache = requestCache;
|
this.requestCache = requestCache;
|
||||||
this.authenticationManager = authenticationManager;
|
this.authenticationManager = authenticationManager;
|
||||||
|
this.authenticationFilterSecurityContextRepositoryRef = authenticationFilterSecurityContextRepositoryRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -92,11 +96,16 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
this.authorizationRequestRedirectFilter = authorizationRequestRedirectFilterBuilder
|
this.authorizationRequestRedirectFilter = authorizationRequestRedirectFilterBuilder
|
||||||
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository)
|
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository)
|
||||||
.addPropertyValue("requestCache", this.requestCache).getBeanDefinition();
|
.addPropertyValue("requestCache", this.requestCache).getBeanDefinition();
|
||||||
this.authorizationCodeGrantFilter = BeanDefinitionBuilder
|
BeanDefinitionBuilder authorizationCodeGrantFilterBldr = BeanDefinitionBuilder
|
||||||
.rootBeanDefinition(OAuth2AuthorizationCodeGrantFilter.class)
|
.rootBeanDefinition(OAuth2AuthorizationCodeGrantFilter.class)
|
||||||
.addConstructorArgValue(clientRegistrationRepository).addConstructorArgValue(authorizedClientRepository)
|
.addConstructorArgValue(clientRegistrationRepository).addConstructorArgValue(authorizedClientRepository)
|
||||||
.addConstructorArgValue(this.authenticationManager)
|
.addConstructorArgValue(this.authenticationManager)
|
||||||
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository).getBeanDefinition();
|
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository);
|
||||||
|
if (this.authenticationFilterSecurityContextRepositoryRef != null) {
|
||||||
|
authorizationCodeGrantFilterBldr.addPropertyValue("securityContextRepository",
|
||||||
|
this.authenticationFilterSecurityContextRepositoryRef);
|
||||||
|
}
|
||||||
|
this.authorizationCodeGrantFilter = authorizationCodeGrantFilterBldr.getBeanDefinition();
|
||||||
|
|
||||||
BeanMetadataElement accessTokenResponseClient = getAccessTokenResponseClient(authorizationCodeGrantElt);
|
BeanMetadataElement accessTokenResponseClient = getAccessTokenResponseClient(authorizationCodeGrantElt);
|
||||||
this.authorizationCodeAuthenticationProvider = BeanDefinitionBuilder
|
this.authorizationCodeAuthenticationProvider = BeanDefinitionBuilder
|
||||||
|
@ -85,6 +85,8 @@ final class Saml2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
|
|
||||||
private final BeanReference authenticationManager;
|
private final BeanReference authenticationManager;
|
||||||
|
|
||||||
|
private final BeanReference authenticationFilterSecurityContextRepositoryRef;
|
||||||
|
|
||||||
private final List<BeanReference> authenticationProviders;
|
private final List<BeanReference> authenticationProviders;
|
||||||
|
|
||||||
private final Map<BeanDefinition, BeanMetadataElement> entryPoints;
|
private final Map<BeanDefinition, BeanMetadataElement> entryPoints;
|
||||||
@ -97,14 +99,15 @@ final class Saml2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
|
|
||||||
Saml2LoginBeanDefinitionParser(List<BeanDefinition> csrfIgnoreRequestMatchers, BeanReference portMapper,
|
Saml2LoginBeanDefinitionParser(List<BeanDefinition> csrfIgnoreRequestMatchers, BeanReference portMapper,
|
||||||
BeanReference portResolver, BeanReference requestCache, boolean allowSessionCreation,
|
BeanReference portResolver, BeanReference requestCache, boolean allowSessionCreation,
|
||||||
BeanReference authenticationManager, List<BeanReference> authenticationProviders,
|
BeanReference authenticationManager, BeanReference authenticationFilterSecurityContextRepositoryRef,
|
||||||
Map<BeanDefinition, BeanMetadataElement> entryPoints) {
|
List<BeanReference> authenticationProviders, Map<BeanDefinition, BeanMetadataElement> entryPoints) {
|
||||||
this.csrfIgnoreRequestMatchers = csrfIgnoreRequestMatchers;
|
this.csrfIgnoreRequestMatchers = csrfIgnoreRequestMatchers;
|
||||||
this.portMapper = portMapper;
|
this.portMapper = portMapper;
|
||||||
this.portResolver = portResolver;
|
this.portResolver = portResolver;
|
||||||
this.requestCache = requestCache;
|
this.requestCache = requestCache;
|
||||||
this.allowSessionCreation = allowSessionCreation;
|
this.allowSessionCreation = allowSessionCreation;
|
||||||
this.authenticationManager = authenticationManager;
|
this.authenticationManager = authenticationManager;
|
||||||
|
this.authenticationFilterSecurityContextRepositoryRef = authenticationFilterSecurityContextRepositoryRef;
|
||||||
this.authenticationProviders = authenticationProviders;
|
this.authenticationProviders = authenticationProviders;
|
||||||
this.entryPoints = entryPoints;
|
this.entryPoints = entryPoints;
|
||||||
}
|
}
|
||||||
@ -148,6 +151,7 @@ final class Saml2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
resolveAuthenticationSuccessHandler(element, saml2WebSsoAuthenticationFilterBuilder);
|
resolveAuthenticationSuccessHandler(element, saml2WebSsoAuthenticationFilterBuilder);
|
||||||
resolveAuthenticationFailureHandler(element, saml2WebSsoAuthenticationFilterBuilder);
|
resolveAuthenticationFailureHandler(element, saml2WebSsoAuthenticationFilterBuilder);
|
||||||
resolveAuthenticationManager(element, saml2WebSsoAuthenticationFilterBuilder);
|
resolveAuthenticationManager(element, saml2WebSsoAuthenticationFilterBuilder);
|
||||||
|
resolveSecurityContextRepository(element, saml2WebSsoAuthenticationFilterBuilder);
|
||||||
// Configure the Saml2WebSsoAuthenticationRequestFilter
|
// Configure the Saml2WebSsoAuthenticationRequestFilter
|
||||||
this.saml2WebSsoAuthenticationRequestFilter = BeanDefinitionBuilder
|
this.saml2WebSsoAuthenticationRequestFilter = BeanDefinitionBuilder
|
||||||
.rootBeanDefinition(Saml2WebSsoAuthenticationRequestFilter.class)
|
.rootBeanDefinition(Saml2WebSsoAuthenticationRequestFilter.class)
|
||||||
@ -176,6 +180,14 @@ final class Saml2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void resolveSecurityContextRepository(Element element,
|
||||||
|
BeanDefinitionBuilder saml2WebSsoAuthenticationFilterBuilder) {
|
||||||
|
if (this.authenticationFilterSecurityContextRepositoryRef != null) {
|
||||||
|
saml2WebSsoAuthenticationFilterBuilder.addPropertyValue("securityContextRepository",
|
||||||
|
this.authenticationFilterSecurityContextRepositoryRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void resolveLoginPage(Element element, ParserContext parserContext) {
|
private void resolveLoginPage(Element element, ParserContext parserContext) {
|
||||||
String loginPage = element.getAttribute(ATT_LOGIN_PAGE);
|
String loginPage = element.getAttribute(ATT_LOGIN_PAGE);
|
||||||
Object source = parserContext.extractSource(element);
|
Object source = parserContext.extractSource(element);
|
||||||
|
@ -333,6 +333,9 @@ http.attlist &=
|
|||||||
http.attlist &=
|
http.attlist &=
|
||||||
## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.
|
## 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}?
|
attribute security-context-repository-ref {xsd:token}?
|
||||||
|
http.attlist &=
|
||||||
|
## Optional attribute that specifies that the SecurityContext should require explicit saving rather than being synchronized from the SecurityContextHolder. Defaults to "false".
|
||||||
|
attribute security-context-explicit-save {xsd:boolean}?
|
||||||
http.attlist &=
|
http.attlist &=
|
||||||
request-matcher?
|
request-matcher?
|
||||||
http.attlist &=
|
http.attlist &=
|
||||||
|
@ -1215,6 +1215,13 @@
|
|||||||
</xs:documentation>
|
</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="security-context-explicit-save" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Optional attribute that specifies that the SecurityContext should require explicit saving
|
||||||
|
rather than being synchronized from the SecurityContextHolder. Defaults to "false".
|
||||||
|
</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
<xs:attribute name="request-matcher">
|
<xs:attribute name="request-matcher">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'
|
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'
|
||||||
|
@ -16,6 +16,10 @@
|
|||||||
|
|
||||||
package org.springframework.security.config.annotation.web.configurers;
|
package org.springframework.security.config.annotation.web.configurers;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import jakarta.servlet.Filter;
|
||||||
import jakarta.servlet.http.HttpSession;
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -33,8 +37,11 @@ import org.springframework.security.config.test.SpringTestContext;
|
|||||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
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.userdetails.PasswordEncodedUser;
|
import org.springframework.security.core.userdetails.PasswordEncodedUser;
|
||||||
|
import org.springframework.security.web.FilterChainProxy;
|
||||||
import org.springframework.security.web.context.HttpRequestResponseHolder;
|
import org.springframework.security.web.context.HttpRequestResponseHolder;
|
||||||
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||||
import org.springframework.security.web.context.NullSecurityContextRepository;
|
import org.springframework.security.web.context.NullSecurityContextRepository;
|
||||||
|
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.context.request.async.WebAsyncManagerIntegrationFilter;
|
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
|
||||||
@ -110,6 +117,27 @@ public class SecurityContextConfigurerTests {
|
|||||||
assertThat(session).isNull();
|
assertThat(session).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requireExplicitSave() throws Exception {
|
||||||
|
HttpSessionSecurityContextRepository repository = new HttpSessionSecurityContextRepository();
|
||||||
|
SpringTestContext testContext = this.spring.register(RequireExplicitSaveConfig.class);
|
||||||
|
testContext.autowire();
|
||||||
|
FilterChainProxy filterChainProxy = testContext.getContext().getBean(FilterChainProxy.class);
|
||||||
|
// @formatter:off
|
||||||
|
List<Class<? extends Filter>> filterTypes = filterChainProxy.getFilters("/")
|
||||||
|
.stream()
|
||||||
|
.map(Filter::getClass)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
assertThat(filterTypes)
|
||||||
|
.contains(SecurityContextHolderFilter.class)
|
||||||
|
.doesNotContain(SecurityContextPersistenceFilter.class);
|
||||||
|
// @formatter:on
|
||||||
|
MvcResult mvcResult = this.mvc.perform(formLogin()).andReturn();
|
||||||
|
SecurityContext securityContext = repository
|
||||||
|
.loadContext(new HttpRequestResponseHolder(mvcResult.getRequest(), mvcResult.getResponse()));
|
||||||
|
assertThat(securityContext.getAuthentication()).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
static class ObjectPostProcessorConfig extends WebSecurityConfigurerAdapter {
|
static class ObjectPostProcessorConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
@ -241,14 +269,39 @@ public class SecurityContextConfigurerTests {
|
|||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
static class NullSecurityContextRepositoryInLambdaConfig extends WebSecurityConfigurerAdapter {
|
static class NullSecurityContextRepositoryInLambdaConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.formLogin(withDefaults())
|
||||||
|
.securityContext((securityContext) ->
|
||||||
|
securityContext
|
||||||
|
.securityContextRepository(new NullSecurityContextRepository())
|
||||||
|
);
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
auth
|
||||||
|
.inMemoryAuthentication()
|
||||||
|
.withUser(PasswordEncodedUser.user());
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class RequireExplicitSaveConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
http
|
http
|
||||||
.formLogin(withDefaults())
|
.formLogin(withDefaults())
|
||||||
.securityContext((securityContext) ->
|
.securityContext((securityContext) -> securityContext
|
||||||
securityContext
|
.requireExplicitSave(true)
|
||||||
.securityContextRepository(new NullSecurityContextRepository())
|
|
||||||
);
|
);
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
@ -258,7 +311,7 @@ public class SecurityContextConfigurerTests {
|
|||||||
// @formatter:off
|
// @formatter:off
|
||||||
auth
|
auth
|
||||||
.inMemoryAuthentication()
|
.inMemoryAuthentication()
|
||||||
.withUser(PasswordEncodedUser.user());
|
.withUser(PasswordEncodedUser.user());
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,9 +121,11 @@ 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.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.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.request.SecurityMockMvcRequestPostProcessors.x509;
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.x509;
|
||||||
|
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||||
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;
|
||||||
@ -461,6 +463,37 @@ public class MiscHttpConfigTests {
|
|||||||
any(HttpServletResponse.class));
|
any(HttpServletResponse.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenExplicitSaveAndRepositoryAndAuthenticatingThenConsultsCustomSecurityContextRepository()
|
||||||
|
throws Exception {
|
||||||
|
this.spring.configLocations(xml("ExplicitSaveAndExplicitRepository")).autowire();
|
||||||
|
SecurityContextRepository repository = this.spring.getContext().getBean(SecurityContextRepository.class);
|
||||||
|
SecurityContext context = new SecurityContextImpl(new TestingAuthenticationToken("user", "password"));
|
||||||
|
given(repository.loadContext(any(HttpRequestResponseHolder.class))).willReturn(context);
|
||||||
|
// @formatter:off
|
||||||
|
MvcResult result = this.mvc.perform(formLogin())
|
||||||
|
.andExpect(status().is3xxRedirection())
|
||||||
|
.andExpect(authenticated())
|
||||||
|
.andReturn();
|
||||||
|
// @formatter:on
|
||||||
|
verify(repository, atLeastOnce()).saveContext(any(SecurityContext.class), any(HttpServletRequest.class),
|
||||||
|
any(HttpServletResponse.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenExplicitSaveAndExplicitSaveAndAuthenticatingThenConsultsCustomSecurityContextRepository()
|
||||||
|
throws Exception {
|
||||||
|
this.spring.configLocations(xml("ExplicitSave")).autowire();
|
||||||
|
SecurityContextRepository repository = this.spring.getContext().getBean(SecurityContextRepository.class);
|
||||||
|
// @formatter:off
|
||||||
|
MvcResult result = this.mvc.perform(formLogin())
|
||||||
|
.andExpect(status().is3xxRedirection())
|
||||||
|
.andReturn();
|
||||||
|
// @formatter:on
|
||||||
|
assertThat(repository.loadContext(new HttpRequestResponseHolder(result.getRequest(), result.getResponse()))
|
||||||
|
.getAuthentication()).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getWhenUsingInterceptUrlExpressionsThenAuthorizesAccordingly() throws Exception {
|
public void getWhenUsingInterceptUrlExpressionsThenAuthorizesAccordingly() throws Exception {
|
||||||
this.spring.configLocations(xml("InterceptUrlExpressions")).autowire();
|
this.spring.configLocations(xml("InterceptUrlExpressions")).autowire();
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
<?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 security-context-explicit-save="true">
|
||||||
|
<form-login/>
|
||||||
|
<intercept-url pattern="/**" access="authenticated"/>
|
||||||
|
</http>
|
||||||
|
|
||||||
|
<b:import resource="MiscHttpConfigTests-controllers.xml"/>
|
||||||
|
<b:import resource="userservice.xml"/>
|
||||||
|
</b:beans>
|
@ -0,0 +1,38 @@
|
|||||||
|
<?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 create-session="always" security-context-repository-ref="repo" security-context-explicit-save="true">
|
||||||
|
<form-login/>
|
||||||
|
<intercept-url pattern="/**" access="authenticated"/>
|
||||||
|
</http>
|
||||||
|
|
||||||
|
<b:bean name="repo" class="org.mockito.Mockito" factory-method="mock">
|
||||||
|
<b:constructor-arg value="org.springframework.security.web.context.SecurityContextRepository"/>
|
||||||
|
</b:bean>
|
||||||
|
|
||||||
|
<b:import resource="MiscHttpConfigTests-controllers.xml"/>
|
||||||
|
<b:import resource="userservice.xml"/>
|
||||||
|
</b:beans>
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 102 KiB |
@ -125,6 +125,12 @@ A request pattern can be mapped to an empty filter chain, by setting this attrib
|
|||||||
No security will be applied and none of Spring Security's features will be available.
|
No security will be applied and none of Spring Security's features will be available.
|
||||||
|
|
||||||
|
|
||||||
|
[[nsa-http-security-context-explicit-save]]
|
||||||
|
* **security-context-explicit-save**
|
||||||
|
If true, use `SecurityContextHolderFilter` instead of `SecurityContextPersistenceFilter`.
|
||||||
|
Requires explicit save
|
||||||
|
|
||||||
|
|
||||||
[[nsa-http-security-context-repository-ref]]
|
[[nsa-http-security-context-repository-ref]]
|
||||||
* **security-context-repository-ref**
|
* **security-context-repository-ref**
|
||||||
Allows injection of a custom `SecurityContextRepository` into the `SecurityContextPersistenceFilter`.
|
Allows injection of a custom `SecurityContextRepository` into the `SecurityContextPersistenceFilter`.
|
||||||
|
@ -88,6 +88,34 @@ Depending on the servlet container implementation, the error means that any `Sec
|
|||||||
When the error dispatch is made, there is no `SecurityContext` established.
|
When the error dispatch is made, there is no `SecurityContext` established.
|
||||||
This means that the error page cannot use the `SecurityContext` for authorization or displaying the current user unless the `SecurityContext` is persisted somehow.
|
This means that the error page cannot use the `SecurityContext` for authorization or displaying the current user unless the `SecurityContext` is persisted somehow.
|
||||||
|
|
||||||
|
.Use RequestAttributeSecurityContextRepository
|
||||||
|
====
|
||||||
|
.Java
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http) {
|
||||||
|
http
|
||||||
|
// ...
|
||||||
|
.securityContext((securityContext) -> securityContext
|
||||||
|
.securityContextRepository(new RequestAttributeSecurityContextRepository())
|
||||||
|
);
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
.XML
|
||||||
|
[source,xml,role="secondary"]
|
||||||
|
----
|
||||||
|
<http security-context-repository-ref="contextRepository">
|
||||||
|
<!-- ... -->
|
||||||
|
</http>
|
||||||
|
<b:bean name="contextRepository"
|
||||||
|
class="org.springframework.security.web.context.RequestAttributeSecurityContextRepository" />
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
|
||||||
|
[[securitycontextpersistencefilter]]
|
||||||
== SecurityContextPersistenceFilter
|
== SecurityContextPersistenceFilter
|
||||||
|
|
||||||
The {security-api-url}org/springframework/security/web/context/SecurityContextPersistenceFilter.html[`SecurityContextPersistenceFilter`] is responsible for persisting the `SecurityContext` between requests using the xref::servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`].
|
The {security-api-url}org/springframework/security/web/context/SecurityContextPersistenceFilter.html[`SecurityContextPersistenceFilter`] is responsible for persisting the `SecurityContext` between requests using the xref::servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`].
|
||||||
@ -104,4 +132,41 @@ For example, if a redirect is sent to the client the response is immediately wri
|
|||||||
This means that establishing an `HttpSession` would not be possible in step 3 because the session id could not be included in the already written response.
|
This means that establishing an `HttpSession` would not be possible in step 3 because the session id could not be included in the already written response.
|
||||||
Another situation that can happen is that if a client authenticates successfully, the response is committed before `SecurityContextPersistenceFilter` completes, and the client makes a second request before the `SecurityContextPersistenceFilter` completes the wrong authentication could be present in the second request.
|
Another situation that can happen is that if a client authenticates successfully, the response is committed before `SecurityContextPersistenceFilter` completes, and the client makes a second request before the `SecurityContextPersistenceFilter` completes the wrong authentication could be present in the second request.
|
||||||
|
|
||||||
To avoid these problems, the `SecurityContextPersistenceFilter` wraps both the `HttpServletRequest` and the `HttpServletResponse` to detect if the `SecurityContext` has changed and if so save the `SecurityContext` just before the response is committed.
|
To avoid these problems, the `SecurityContextPersistenceFilter` wraps both the `HttpServletRequest` and the `HttpServletResponse` to detect if the `SecurityContext` has changed and if so save the `SecurityContext` just before the response is committed.
|
||||||
|
|
||||||
|
[[securitycontextholderfilter]]
|
||||||
|
== SecurityContextHolderFilter
|
||||||
|
|
||||||
|
The {security-api-url}org/springframework/security/web/context/SecurityContextHolderFilter.html[`SecurityContextHolderFilter`] is responsible for loading the `SecurityContext` between requests using the xref::servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`].
|
||||||
|
|
||||||
|
image::{figures}/securitycontextholderfilter.png[]
|
||||||
|
|
||||||
|
<1> Before running the rest of the application, `SecurityContextHolderFilter` loads the `SecurityContext` from the `SecurityContextRepository` and sets it on the `SecurityContextHolder`.
|
||||||
|
<2> Next, the application is ran.
|
||||||
|
|
||||||
|
Unlike, xref:servlet/authentication/persistence.adoc#securitycontextpersistencefilter[`SecurityContextPersisteneFilter`], `SecurityContextHolderFilter` only loads the `SecurityContext` it does not save the `SecurityContext`.
|
||||||
|
This means that when using `SecurityContextHolderFilter`, it is required that the `SecurityContext` is explicitly saved.
|
||||||
|
|
||||||
|
.Explicit Saving of SecurityContext
|
||||||
|
====
|
||||||
|
.Java
|
||||||
|
[source,java,role="primary"]
|
||||||
|
----
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http) {
|
||||||
|
http
|
||||||
|
// ...
|
||||||
|
.securityContext((securityContext) -> securityContext
|
||||||
|
.requireExplicitSave(true)
|
||||||
|
);
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
.XML
|
||||||
|
[source,xml,role="secondary"]
|
||||||
|
----
|
||||||
|
<http security-context-explicit-save="true">
|
||||||
|
<!-- ... -->
|
||||||
|
</http>
|
||||||
|
----
|
||||||
|
====
|
@ -26,6 +26,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|||||||
import org.springframework.security.config.BeanIds;
|
import org.springframework.security.config.BeanIds;
|
||||||
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
|
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
|
||||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||||
|
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.csrf.CsrfFilter;
|
import org.springframework.security.web.csrf.CsrfFilter;
|
||||||
@ -61,10 +62,14 @@ public abstract class WebTestUtils {
|
|||||||
*/
|
*/
|
||||||
public static SecurityContextRepository getSecurityContextRepository(HttpServletRequest request) {
|
public static SecurityContextRepository getSecurityContextRepository(HttpServletRequest request) {
|
||||||
SecurityContextPersistenceFilter filter = findFilter(request, SecurityContextPersistenceFilter.class);
|
SecurityContextPersistenceFilter filter = findFilter(request, SecurityContextPersistenceFilter.class);
|
||||||
if (filter == null) {
|
if (filter != null) {
|
||||||
return DEFAULT_CONTEXT_REPO;
|
return (SecurityContextRepository) ReflectionTestUtils.getField(filter, "repo");
|
||||||
}
|
}
|
||||||
return (SecurityContextRepository) ReflectionTestUtils.getField(filter, "repo");
|
SecurityContextHolderFilter holderFilter = findFilter(request, SecurityContextHolderFilter.class);
|
||||||
|
if (holderFilter != null) {
|
||||||
|
return (SecurityContextRepository) ReflectionTestUtils.getField(holderFilter, "securityContextRepository");
|
||||||
|
}
|
||||||
|
return DEFAULT_CONTEXT_REPO;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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.context;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link jakarta.servlet.Filter} that uses the {@link SecurityContextRepository} to
|
||||||
|
* obtain the {@link SecurityContext} and set it on the {@link SecurityContextHolder}.
|
||||||
|
* This is similar to {@link SecurityContextPersistenceFilter} except that the
|
||||||
|
* {@link SecurityContextRepository#saveContext(SecurityContext, HttpServletRequest, HttpServletResponse)}
|
||||||
|
* must be explicitly invoked to save the {@link SecurityContext}. This improves the
|
||||||
|
* efficiency and provides better flexibility by allowing different authentication
|
||||||
|
* mechanisms to choose individually if authentication should be persisted.
|
||||||
|
*
|
||||||
|
* @author Rob Winch
|
||||||
|
* @since 5.7
|
||||||
|
*/
|
||||||
|
public class SecurityContextHolderFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private final SecurityContextRepository securityContextRepository;
|
||||||
|
|
||||||
|
private boolean shouldNotFilterErrorDispatch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
* @param securityContextRepository the repository to use. Cannot be null.
|
||||||
|
*/
|
||||||
|
public SecurityContextHolderFilter(SecurityContextRepository securityContextRepository) {
|
||||||
|
Assert.notNull(securityContextRepository, "securityContextRepository cannot be null");
|
||||||
|
this.securityContextRepository = securityContextRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
SecurityContext securityContext = this.securityContextRepository
|
||||||
|
.loadContext(new HttpRequestResponseHolder(request, response));
|
||||||
|
try {
|
||||||
|
SecurityContextHolder.setContext(securityContext);
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldNotFilterErrorDispatch() {
|
||||||
|
return this.shouldNotFilterErrorDispatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables {@link SecurityContextHolderFilter} for error dispatch.
|
||||||
|
* @param shouldNotFilterErrorDispatch if the Filter should be disabled for error
|
||||||
|
* dispatch. Default is false.
|
||||||
|
*/
|
||||||
|
public void setShouldNotFilterErrorDispatch(boolean shouldNotFilterErrorDispatch) {
|
||||||
|
this.shouldNotFilterErrorDispatch = shouldNotFilterErrorDispatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* 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.context;
|
||||||
|
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.TestAuthentication;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.context.SecurityContextImpl;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class SecurityContextHolderFilterTests {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private SecurityContextRepository repository;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private HttpServletRequest request;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private HttpServletResponse response;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private FilterChain chain;
|
||||||
|
|
||||||
|
@Captor
|
||||||
|
private ArgumentCaptor<HttpRequestResponseHolder> requestResponse;
|
||||||
|
|
||||||
|
private SecurityContextHolderFilter filter;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup() {
|
||||||
|
this.filter = new SecurityContextHolderFilter(this.repository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void cleanup() {
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void doFilterThenSetsAndClearsSecurityContextHolder() throws Exception {
|
||||||
|
Authentication authentication = TestAuthentication.authenticatedUser();
|
||||||
|
SecurityContext expectedContext = new SecurityContextImpl(authentication);
|
||||||
|
given(this.repository.loadContext(this.requestResponse.capture())).willReturn(expectedContext);
|
||||||
|
FilterChain filterChain = (request, response) -> assertThat(SecurityContextHolder.getContext())
|
||||||
|
.isEqualTo(expectedContext);
|
||||||
|
|
||||||
|
this.filter.doFilter(this.request, this.response, filterChain);
|
||||||
|
|
||||||
|
assertThat(SecurityContextHolder.getContext()).isEqualTo(SecurityContextHolder.createEmptyContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotFilterErrorDispatchWhenDefault() {
|
||||||
|
assertThat(this.filter.shouldNotFilterErrorDispatch()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotFilterErrorDispatchWhenOverridden() {
|
||||||
|
this.filter.setShouldNotFilterErrorDispatch(true);
|
||||||
|
assertThat(this.filter.shouldNotFilterErrorDispatch()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user