SEC-2199: Support multiple AuthenticationEntryPoint defaults

This commit is contained in:
Rob Winch 2013-07-19 16:27:06 -05:00
parent 87c9a14bff
commit 90bd241ce2
9 changed files with 296 additions and 25 deletions

View File

@ -37,6 +37,8 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
/**
* Provides a convenient base class for creating a {@link WebSecurityConfigurer}
@ -51,6 +53,8 @@ public abstract class WebSecurityConfigurerAdapter implements SecurityConfigurer
private ApplicationContext context;
private ContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
private ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<Object>() {
@Override
public <T> T postProcess(T object) {
@ -145,6 +149,7 @@ public abstract class WebSecurityConfigurerAdapter implements SecurityConfigurer
authenticationBuilder.parentAuthenticationManager(authenticationManager);
http = new HttpSecurity(objectPostProcessor,authenticationBuilder, parentAuthenticationBuilder.getSharedObjects());
http.setSharedObject(UserDetailsService.class, userDetailsService());
http.setSharedObject(ContentNegotiationStrategy.class, contentNegotiationStrategy);
if(!disableDefaults) {
http
.exceptionHandling().and()
@ -304,6 +309,11 @@ public abstract class WebSecurityConfigurerAdapter implements SecurityConfigurer
this.context = context;
}
@Autowired(required=false)
public void setContentNegotationStrategy(ContentNegotiationStrategy contentNegotiationStrategy) {
this.contentNegotiationStrategy = contentNegotiationStrategy;
}
@Autowired(required=false)
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;

View File

@ -17,11 +17,11 @@ package org.springframework.security.config.annotation.web.configurers;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.openid.OpenIDLoginConfigurer;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.PortMapper;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
@ -33,6 +33,10 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.util.MediaTypeRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
/**
* Base class for confuring {@link AbstractAuthenticationFilterConfigurer}. This is intended for internal use only.
@ -221,7 +225,21 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecu
if(permitAll) {
PermitAllSupport.permitAll(http, loginPage, loginProcessingUrl, failureUrl);
}
http.setSharedObject(AuthenticationEntryPoint.class, postProcess(authenticationEntryPoint));
registerDefaultAuthenticationEntryPoint(http);
}
@SuppressWarnings("unchecked")
private void registerDefaultAuthenticationEntryPoint(B http) {
ExceptionHandlingConfigurer<B> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);
if(exceptionHandling == null) {
return;
}
ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);
if(contentNegotiationStrategy == null) {
contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
}
RequestMatcher preferredMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.APPLICATION_XHTML_XML, new MediaType("image","*"), MediaType.TEXT_HTML, MediaType.TEXT_PLAIN);
exceptionHandling.defaultAuthenticationEntryPointFor(postProcess(authenticationEntryPoint), preferredMatcher);
}
@Override

View File

@ -15,15 +15,19 @@
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.LinkedHashMap;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.util.RequestMatcher;
/**
* Adds exception handling for Spring Security related exceptions to an application. All properties have reasonable
@ -47,8 +51,6 @@ import org.springframework.security.web.savedrequest.RequestCache;
* The following shared objects are used:
*
* <ul>
* <li>{@link HttpSecurity#authenticationEntryPoint()} is used to process requests that require
* authentication</li>
* <li>If no explicit {@link RequestCache}, is provided a {@link RequestCache} shared object is used to replay
* the request after authentication is successful</li>
* <li>{@link AuthenticationEntryPoint} - see {@link #authenticationEntryPoint(AuthenticationEntryPoint)} </li>
@ -63,6 +65,8 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
private AccessDeniedHandler accessDeniedHandler;
private LinkedHashMap<RequestMatcher,AuthenticationEntryPoint> defaultEntryPointMappings = new LinkedHashMap<RequestMatcher, AuthenticationEntryPoint>();
/**
* Creates a new instance
* @see HttpSecurity#exceptionHandling()
@ -96,18 +100,51 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
}
/**
* Sets the {@link AuthenticationEntryPoint} to be used. Defaults to the
* {@link HttpSecurity#getSharedObject(Class)} value. If that is not
* provided defaults to {@link Http403ForbiddenEntryPoint}.
* Sets the {@link AuthenticationEntryPoint} to be used.
*
* @param authenticationEntryPoint the {@link AuthenticationEntryPoint} to use
* @return the {@link ExceptionHandlingConfigurer} for further customizations
* <p>
* If no {@link #authenticationEntryPoint(AuthenticationEntryPoint)} is
* specified, then
* {@link #defaultAuthenticationEntryPointFor(AuthenticationEntryPoint, RequestMatcher)}
* will be used. The first {@link AuthenticationEntryPoint} will be used as
* the default is no matches were found.
* </p>
*
* <p>
* If that is not provided defaults to {@link Http403ForbiddenEntryPoint}.
* </p>
*
* @param authenticationEntryPoint
* the {@link AuthenticationEntryPoint} to use
* @return the {@link ExceptionHandlingConfigurer} for further
* customizations
*/
public ExceptionHandlingConfigurer<H> authenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
this.authenticationEntryPoint = authenticationEntryPoint;
return this;
}
/**
* Sets a default {@link AuthenticationEntryPoint} to be used which prefers
* being invoked for the provided {@link RequestMatcher}. If only a single
* default {@link AuthenticationEntryPoint} is specified, it will be what is
* used for the default {@link AuthenticationEntryPoint}. If multiple
* default {@link AuthenticationEntryPoint} instances are configured, then a
* {@link DelegatingAuthenticationEntryPoint} will be used.
*
* @param entryPoint
* the {@link AuthenticationEntryPoint} to use
* @param preferredMatcher
* the {@link RequestMatcher} for this default
* {@link AuthenticationEntryPoint}
* @return the {@link ExceptionHandlingConfigurer} for further
* customizations
*/
public ExceptionHandlingConfigurer<H> defaultAuthenticationEntryPointFor(AuthenticationEntryPoint entryPoint, RequestMatcher preferredMatcher) {
this.defaultEntryPointMappings.put(preferredMatcher, entryPoint);
return this;
}
/**
* Gets any explicitly configured {@link AuthenticationEntryPoint}
* @return
@ -118,7 +155,7 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
@Override
public void configure(H http) throws Exception {
AuthenticationEntryPoint entryPoint = getEntryPoint(http);
AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);
ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint, getRequestCache(http));
if(accessDeniedHandler != null) {
exceptionTranslationFilter.setAccessDeniedHandler(accessDeniedHandler);
@ -126,25 +163,31 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>
exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
http.addFilter(exceptionTranslationFilter);
}
/**
* Gets the {@link AuthenticationEntryPoint} according to the rules specified by {@link #authenticationEntryPoint(AuthenticationEntryPoint)}
* @param http the {@link HttpSecurity} used to look up shared {@link AuthenticationEntryPoint}
* @return the {@link AuthenticationEntryPoint} to use
*/
AuthenticationEntryPoint getEntryPoint(H http) {
AuthenticationEntryPoint getAuthenticationEntryPoint(H http) {
AuthenticationEntryPoint entryPoint = this.authenticationEntryPoint;
if(entryPoint == null) {
AuthenticationEntryPoint sharedEntryPoint = http.getSharedObject(AuthenticationEntryPoint.class);
if(sharedEntryPoint != null) {
entryPoint = sharedEntryPoint;
} else {
entryPoint = new Http403ForbiddenEntryPoint();
}
entryPoint = createDefaultEntryPoint(http);
}
return entryPoint;
}
private AuthenticationEntryPoint createDefaultEntryPoint(H http) {
if(defaultEntryPointMappings.isEmpty()) {
return new Http403ForbiddenEntryPoint();
}
if(defaultEntryPointMappings.size() == 1) {
return defaultEntryPointMappings.values().iterator().next();
}
DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(defaultEntryPointMappings);
entryPoint.setDefaultEntryPoint(defaultEntryPointMappings.values().iterator().next());
return entryPoint;
}
/**
* Gets the {@link RequestCache} to use. If one is defined using
* {@link #requestCache(org.springframework.security.web.savedrequest.RequestCache)}, then it is used. Otherwise, an

View File

@ -15,8 +15,11 @@
*/
package org.springframework.security.config.annotation.web.configurers;
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
@ -25,6 +28,10 @@ import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.util.MediaTypeRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
/**
* Adds HTTP basic based authentication. All attributes have reasonable defaults
@ -118,8 +125,22 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> extends
}
public void init(B http) throws Exception {
http
.setSharedObject(AuthenticationEntryPoint.class, authenticationEntryPoint);
registerDefaultAuthenticationEntryPoint(http);
}
@SuppressWarnings("unchecked")
private void registerDefaultAuthenticationEntryPoint(B http) {
ExceptionHandlingConfigurer<B> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);
if(exceptionHandling == null) {
return;
}
ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);
if(contentNegotiationStrategy == null) {
contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
}
MediaTypeRequestMatcher preferredMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML, MediaType.MULTIPART_FORM_DATA, MediaType.TEXT_XML);
preferredMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
exceptionHandling.defaultAuthenticationEntryPointFor(postProcess(authenticationEntryPoint), preferredMatcher);
}
@Override

View File

@ -75,7 +75,7 @@ public final class ServletApiConfigurer<H extends HttpSecurityBuilder<H>> extend
public void configure(H http) throws Exception {
securityContextRequestFilter.setAuthenticationManager(http.getAuthenticationManager());
ExceptionHandlingConfigurer<H> exceptionConf = http.getConfigurer(ExceptionHandlingConfigurer.class);
AuthenticationEntryPoint authenticationEntryPoint = exceptionConf == null ? null : exceptionConf.getEntryPoint(http);
AuthenticationEntryPoint authenticationEntryPoint = exceptionConf == null ? null : exceptionConf.getAuthenticationEntryPoint(http);
securityContextRequestFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
LogoutConfigurer<H> logoutConf = http.getConfigurer(LogoutConfigurer.class);
List<LogoutHandler> logoutHandlers = logoutConf == null ? null : logoutConf.getLogoutHandlers();

View File

@ -40,6 +40,8 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.security.core.Authentication
import org.springframework.security.core.authority.AuthorityUtils
import org.springframework.security.ldap.DefaultSpringSecurityContextSource
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
/**
* @author Rob Winch
@ -99,4 +101,35 @@ class WebSecurityConfigurerAdapterTests extends BaseSpringSpec {
EVENTS.add(e)
}
}
def "Override ContentNegotiationStrategy with @Bean"() {
setup:
OverrideContentNegotiationStrategySharedObjectConfig.CNS = Mock(ContentNegotiationStrategy)
when:
loadConfig(OverrideContentNegotiationStrategySharedObjectConfig)
then:
context.getBean(OverrideContentNegotiationStrategySharedObjectConfig).http.getSharedObject(ContentNegotiationStrategy) == OverrideContentNegotiationStrategySharedObjectConfig.CNS
}
@EnableWebSecurity
@Configuration
static class OverrideContentNegotiationStrategySharedObjectConfig extends WebSecurityConfigurerAdapter {
static ContentNegotiationStrategy CNS
@Bean
public ContentNegotiationStrategy cns() {
return CNS
}
}
def "ContentNegotiationStrategy shareObject defaults to Header with no @Bean"() {
when:
loadConfig(ContentNegotiationStrategyDefaultSharedObjectConfig)
then:
context.getBean(ContentNegotiationStrategyDefaultSharedObjectConfig).http.getSharedObject(ContentNegotiationStrategy).class == HeaderContentNegotiationStrategy
}
@EnableWebSecurity
@Configuration
static class ContentNegotiationStrategyDefaultSharedObjectConfig extends WebSecurityConfigurerAdapter {}
}

View File

@ -29,6 +29,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
import org.springframework.security.web.FilterChainProxy
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler
@ -331,6 +332,8 @@ public class DefaultLoginPageConfigurerTests extends BaseSpringSpec {
http
// must set builder manually due to groovy not selecting correct method
.apply(defaultLoginConfig).and()
.exceptionHandling()
.and()
.formLogin()
.and()
.build()
@ -339,5 +342,6 @@ public class DefaultLoginPageConfigurerTests extends BaseSpringSpec {
1 * objectPostProcessor.postProcess(_ as DefaultLoginPageViewFilter) >> {DefaultLoginPageViewFilter o -> o}
1 * objectPostProcessor.postProcess(_ as UsernamePasswordAuthenticationFilter) >> {UsernamePasswordAuthenticationFilter o -> o}
1 * objectPostProcessor.postProcess(_ as LoginUrlAuthenticationEntryPoint) >> {LoginUrlAuthenticationEntryPoint o -> o}
1 * objectPostProcessor.postProcess(_ as ExceptionTranslationFilter) >> {ExceptionTranslationFilter o -> o}
}
}

View File

@ -15,11 +15,29 @@
*/
package org.springframework.security.config.annotation.web.configurers
import javax.servlet.http.HttpServletResponse
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType
import org.springframework.mock.web.MockFilterChain
import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse
import org.springframework.security.config.annotation.AnyObjectPostProcessor
import org.springframework.security.config.annotation.BaseSpringSpec
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.ExceptionTranslationFilter
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
import spock.lang.Unroll
/**
*
@ -40,4 +58,124 @@ class ExceptionHandlingConfigurerTests extends BaseSpringSpec {
then: "ExceptionTranslationFilter is registered with LifecycleManager"
1 * opp.postProcess(_ as ExceptionTranslationFilter) >> {ExceptionTranslationFilter o -> o}
}
@Unroll
def "SEC-2199: defaultEntryPoint for httpBasic and formLogin"(String acceptHeader, int httpStatus) {
setup:
loadConfig(HttpBasicAndFormLoginEntryPointsConfig)
when:
request.addHeader("Accept", acceptHeader)
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.status == httpStatus
where:
acceptHeader | httpStatus
MediaType.ALL_VALUE | HttpServletResponse.SC_MOVED_TEMPORARILY
MediaType.APPLICATION_XHTML_XML_VALUE | HttpServletResponse.SC_MOVED_TEMPORARILY
MediaType.IMAGE_GIF_VALUE | HttpServletResponse.SC_MOVED_TEMPORARILY
MediaType.IMAGE_JPEG_VALUE | HttpServletResponse.SC_MOVED_TEMPORARILY
MediaType.IMAGE_PNG_VALUE | HttpServletResponse.SC_MOVED_TEMPORARILY
MediaType.TEXT_HTML_VALUE | HttpServletResponse.SC_MOVED_TEMPORARILY
MediaType.TEXT_PLAIN_VALUE | HttpServletResponse.SC_MOVED_TEMPORARILY
MediaType.APPLICATION_ATOM_XML_VALUE | HttpServletResponse.SC_UNAUTHORIZED
MediaType.APPLICATION_FORM_URLENCODED_VALUE | HttpServletResponse.SC_UNAUTHORIZED
MediaType.APPLICATION_JSON_VALUE | HttpServletResponse.SC_UNAUTHORIZED
MediaType.APPLICATION_OCTET_STREAM_VALUE | HttpServletResponse.SC_UNAUTHORIZED
MediaType.APPLICATION_XML_VALUE | HttpServletResponse.SC_UNAUTHORIZED
MediaType.MULTIPART_FORM_DATA_VALUE | HttpServletResponse.SC_UNAUTHORIZED
MediaType.TEXT_XML_VALUE | HttpServletResponse.SC_UNAUTHORIZED
}
def "ContentNegotiationStrategy defaults to HeaderContentNegotiationStrategy"() {
when:
loadConfig(HttpBasicAndFormLoginEntryPointsConfig)
DelegatingAuthenticationEntryPoint delegateEntryPoint = findFilter(ExceptionTranslationFilter).authenticationEntryPoint
then:
delegateEntryPoint.entryPoints.keySet().collect {it.contentNegotiationStrategy.class} == [HeaderContentNegotiationStrategy,HeaderContentNegotiationStrategy]
}
@EnableWebSecurity
@Configuration
static class HttpBasicAndFormLoginEntryPointsConfig extends WebSecurityConfigurerAdapter {
@Override
protected void registerAuthentication(AuthenticationManagerBuilder auth)
throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeUrls()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.formLogin()
}
}
def "ContentNegotiationStrategy overrides with @Bean"() {
setup:
OverrideContentNegotiationStrategySharedObjectConfig.CNS = Mock(ContentNegotiationStrategy)
when:
loadConfig(OverrideContentNegotiationStrategySharedObjectConfig)
DelegatingAuthenticationEntryPoint delegateEntryPoint = findFilter(ExceptionTranslationFilter).authenticationEntryPoint
then:
delegateEntryPoint.entryPoints.keySet().collect {it.contentNegotiationStrategy} == [OverrideContentNegotiationStrategySharedObjectConfig.CNS,OverrideContentNegotiationStrategySharedObjectConfig.CNS]
}
def "Override ContentNegotiationStrategy with @Bean"() {
setup:
OverrideContentNegotiationStrategySharedObjectConfig.CNS = Mock(ContentNegotiationStrategy)
when:
loadConfig(OverrideContentNegotiationStrategySharedObjectConfig)
then:
context.getBean(OverrideContentNegotiationStrategySharedObjectConfig).http.getSharedObject(ContentNegotiationStrategy) == OverrideContentNegotiationStrategySharedObjectConfig.CNS
}
@EnableWebSecurity
@Configuration
static class OverrideContentNegotiationStrategySharedObjectConfig extends WebSecurityConfigurerAdapter {
static ContentNegotiationStrategy CNS
@Bean
public ContentNegotiationStrategy cns() {
return CNS
}
}
def "delegatingAuthenticationEntryPoint.defaultEntryPoint is LoginUrlAuthenticationEntryPoint when using DefaultHttpConf"() {
when:
loadConfig(DefaultHttpConf)
then:
findFilter(ExceptionTranslationFilter).authenticationEntryPoint.defaultEntryPoint.class == LoginUrlAuthenticationEntryPoint
}
@EnableWebSecurity
@Configuration
static class DefaultHttpConf extends WebSecurityConfigurerAdapter {
}
def "delegatingAuthenticationEntryPoint.defaultEntryPoint is BasicAuthenticationEntryPoint when httpBasic before formLogin"() {
when:
loadConfig(BasicAuthenticationEntryPointBeforeFormLoginConf)
then:
findFilter(ExceptionTranslationFilter).authenticationEntryPoint.defaultEntryPoint.class == BasicAuthenticationEntryPoint
}
@EnableWebSecurity
@Configuration
static class BasicAuthenticationEntryPointBeforeFormLoginConf extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.formLogin()
}
}
}

View File

@ -202,13 +202,17 @@ class FormLoginConfigurerTests extends BaseSpringSpec {
HttpSecurity http = new HttpSecurity(opp, authenticationBldr, [:])
when:
http
.exceptionHandling()
.and()
.formLogin()
.and()
.build()
then: "UsernamePasswordAuthenticationFilter is registered with LifecycleManager"
then: "UsernamePasswordAuthenticationFilter is registered with ObjectPostProcessor"
1 * opp.postProcess(_ as UsernamePasswordAuthenticationFilter) >> {UsernamePasswordAuthenticationFilter o -> o}
and: "LoginUrlAuthenticationEntryPoint is registered with LifecycleManager"
and: "LoginUrlAuthenticationEntryPoint is registered with ObjectPostProcessor"
1 * opp.postProcess(_ as LoginUrlAuthenticationEntryPoint) >> {LoginUrlAuthenticationEntryPoint o -> o}
and: "ExceptionTranslationFilter is registered with ObjectPostProcessor"
1 * opp.postProcess(_ as ExceptionTranslationFilter) >> {ExceptionTranslationFilter o -> o}
}
}