/*
 * 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.
 */
package org.springframework.security.config.annotation.web.builders;

import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer;
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer;
import org.springframework.security.config.annotation.web.configurers.CorsConfigurer;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer;
import org.springframework.security.config.annotation.web.configurers.JeeConfigurer;
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
import org.springframework.security.config.annotation.web.configurers.PortMapperConfigurer;
import org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer;
import org.springframework.security.config.annotation.web.configurers.RequestCacheConfigurer;
import org.springframework.security.config.annotation.web.configurers.SecurityContextConfigurer;
import org.springframework.security.config.annotation.web.configurers.ServletApiConfigurer;
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;
import org.springframework.security.config.annotation.web.configurers.X509Configurer;
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2ClientConfigurer;
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.config.annotation.web.configurers.openid.OpenIDLoginConfigurer;
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.userdetails.UserDetailsService;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.PortMapper;
import org.springframework.security.web.PortMapperImpl;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;

import javax.servlet.Filter;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * A {@link HttpSecurity} is similar to Spring Security's XML <http> element in the
 * namespace configuration. It allows configuring web based security for specific http
 * requests. By default it will be applied to all requests, but can be restricted using
 * {@link #requestMatcher(RequestMatcher)} or other similar methods.
 *
 *
Example Usage

 *
 * The most basic form based configuration can be seen below. The configuration will
 * require that any URL that is requested will require a User with the role "ROLE_USER".
 * It also defines an in memory authentication scheme with a user that has the username
 * "user", the password "password", and the role "ROLE_USER". For additional examples,
 * refer to the Java Doc of individual methods on {@link HttpSecurity}.
 *
 *

 * @Configuration
 * @EnableWebSecurity
 * public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {
 *
 * 	@Override
 * 	protected void configure(HttpSecurity http) throws Exception {
 * 		http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin();
 * 	}
 *
 * 	@Override
 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
 * 		auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
 * 	}
 * }
 *

 *
 * @author Rob Winch
 * @author Joe Grandja
 * @since 3.2
 * @see EnableWebSecurity
 */
public final class HttpSecurity extends
		AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
		implements SecurityBuilder<DefaultSecurityFilterChain>,
		HttpSecurityBuilder<HttpSecurity> {
	private final RequestMatcherConfigurer requestMatcherConfigurer;
	private List<Filter> filters = new ArrayList<>();
	private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;
	private FilterComparator comparator = new FilterComparator();

	/**
	 * Creates a new instance
	 * @param objectPostProcessor the {@link ObjectPostProcessor} that should be used
	 * @param authenticationBuilder the {@link AuthenticationManagerBuilder} to use for
	 * additional updates
	 * @param sharedObjects the shared Objects to initialize the {@link HttpSecurity} with
	 * @see WebSecurityConfiguration
	 */
	@SuppressWarnings("unchecked")
	public HttpSecurity(ObjectPostProcessor<Object> objectPostProcessor,
			AuthenticationManagerBuilder authenticationBuilder,
			Map<Class<? extends Object>, Object> sharedObjects) {
		super(objectPostProcessor);
		Assert.notNull(authenticationBuilder, "authenticationBuilder cannot be null");
		setSharedObject(AuthenticationManagerBuilder.class, authenticationBuilder);
		for (Map.Entry<Class<? extends Object>, Object> entry : sharedObjects
				.entrySet()) {
			setSharedObject((Class<Object>) entry.getKey(), entry.getValue());
		}
		ApplicationContext context = (ApplicationContext) sharedObjects
				.get(ApplicationContext.class);
		this.requestMatcherConfigurer = new RequestMatcherConfigurer(context);
	}

	private ApplicationContext getContext() {
		return getSharedObject(ApplicationContext.class);
	}

	/**
	 * Allows configuring OpenID based authentication.
	 *
	 *
Example Configurations

	 *
	 * A basic example accepting the defaults and not using attribute exchange:
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class OpenIDLoginConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) {
	 * 		http.authorizeRequests().antMatchers("/**").hasRole("USER").and().openidLogin()
	 * 				.permitAll();
	 * 	}
	 *
	 * 	@Override
	 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 * 		auth.inMemoryAuthentication()
	 * 				// the username must match the OpenID of the user you are
	 * 				// logging in with
	 * 				.withUser(
	 * 						"https://www.google.com/accounts/o8/id?id=lmkCn9xzPdsxVwG7pjYMuDgNNdASFmobNkcRPaWU")
	 * 				.password("password").roles("USER");
	 * 	}
	 * }
	 *

	 *
	 * A more advanced example demonstrating using attribute exchange and providing a
	 * custom AuthenticationUserDetailsService that will make any user that authenticates
	 * a valid user.
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class OpenIDLoginConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) {
	 * 		http.authorizeRequests()
	 * 				.antMatchers("/**")
	 * 				.hasRole("USER")
	 * 				.and()
	 * 				.openidLogin()
	 * 				.loginPage("/login")
	 * 				.permitAll()
	 * 				.authenticationUserDetailsService(
	 * 						new AutoProvisioningUserDetailsService())
	 * 				.attributeExchange("https://www.google.com/.*").attribute("email")
	 * 				.type("https://axschema.org/contact/email").required(true).and()
	 * 				.attribute("firstname").type("https://axschema.org/namePerson/first")
	 * 				.required(true).and().attribute("lastname")
	 * 				.type("https://axschema.org/namePerson/last").required(true).and().and()
	 * 				.attributeExchange(".*yahoo.com.*").attribute("email")
	 * 				.type("https://schema.openid.net/contact/email").required(true).and()
	 * 				.attribute("fullname").type("https://axschema.org/namePerson")
	 * 				.required(true).and().and().attributeExchange(".*myopenid.com.*")
	 * 				.attribute("email").type("https://schema.openid.net/contact/email")
	 * 				.required(true).and().attribute("fullname")
	 * 				.type("https://schema.openid.net/namePerson").required(true);
	 * 	}
	 * }
	 *
	 * public class AutoProvisioningUserDetailsService implements
	 * 		AuthenticationUserDetailsService<OpenIDAuthenticationToken> {
	 * 	public UserDetails loadUserDetails(OpenIDAuthenticationToken token)
	 * 			throws UsernameNotFoundException {
	 * 		return new User(token.getName(), "NOTUSED",
	 * 				AuthorityUtils.createAuthorityList("ROLE_USER"));
	 * 	}
	 * }
	 *

	 *
	 * @return the {@link OpenIDLoginConfigurer} for further customizations.
	 *
	 * @throws Exception
	 * @see OpenIDLoginConfigurer
	 */
	public OpenIDLoginConfigurer<HttpSecurity> openidLogin() throws Exception {
		return getOrApply(new OpenIDLoginConfigurer<>());
	}

	/**
	 * Adds the Security headers to the response. This is activated by default when using
	 * {@link WebSecurityConfigurerAdapter}'s default constructor. Accepting the
	 * default provided by {@link WebSecurityConfigurerAdapter} or only invoking
	 * {@link #headers()} without invoking additional methods on it, is the equivalent of:
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 *     protected void configure(HttpSecurity http) throws Exception {
	 *         http
	 *             .headers()
	 *                 .contentTypeOptions()
	 *                 .and()
	 *                 .xssProtection()
	 *                 .and()
	 *                 .cacheControl()
	 *                 .and()
	 *                 .httpStrictTransportSecurity()
	 *                 .and()
	 *                 .frameOptions()
	 *                 .and()
	 *             ...;
	 *     }
	 * }
	 *

	 *
	 * You can disable the headers using the following:
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 *     protected void configure(HttpSecurity http) throws Exception {
	 *         http
	 *             .headers().disable()
	 *             ...;
	 *     }
	 * }
	 *

	 *
	 * You can enable only a few of the headers by first invoking
	 * {@link HeadersConfigurer#defaultsDisabled()}
	 * and then invoking the appropriate methods on the {@link #headers()} result.
	 * For example, the following will enable {@link HeadersConfigurer#cacheControl()} and
	 * {@link HeadersConfigurer#frameOptions()} only.
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 *     protected void configure(HttpSecurity http) throws Exception {
	 *         http
	 *             .headers()
	 *                  .defaultsDisabled()
	 *                  .cacheControl()
	 *                  .and()
	 *                  .frameOptions()
	 *                  .and()
	 *             ...;
	 *     }
	 * }
	 *

	 *
	 * You can also choose to keep the defaults but explicitly disable a subset of headers.
	 * For example, the following will enable all the default headers except
	 * {@link HeadersConfigurer#frameOptions()}.
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 *     protected void configure(HttpSecurity http) throws Exception {
	 *         http
	 *             .headers()
	 *                  .frameOptions()
	 *                  	.disable()
	 *                  .and()
	 *             ...;
	 *     }
	 * }
	 *

	 *
	 * @return the {@link HeadersConfigurer} for further customizations
	 * @throws Exception
	 * @see HeadersConfigurer
	 */
	public HeadersConfigurer<HttpSecurity> headers() throws Exception {
		return getOrApply(new HeadersConfigurer<>());
	}

	/**
	 * Adds a {@link CorsFilter} to be used. If a bean by the name of corsFilter is
	 * provided, that {@link CorsFilter} is used. Else if corsConfigurationSource is
	 * defined, then that {@link CorsConfiguration} is used. Otherwise, if Spring MVC is
	 * on the classpath a {@link HandlerMappingIntrospector} is used.
	 *
	 * @return the {@link CorsConfigurer} for customizations
	 * @throws Exception
	 */
	public CorsConfigurer<HttpSecurity> cors() throws Exception {
		return getOrApply(new CorsConfigurer<>());
	}

	/**
	 * Allows configuring of Session Management.
	 *
	 *
Example Configuration

	 *
	 * The following configuration demonstrates how to enforce that only a single instance
	 * of a user is authenticated at a time. If a user authenticates with the username
	 * "user" without logging out and an attempt to authenticate with "user" is made the
	 * first session will be forcibly terminated and sent to the "/login?expired" URL.
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class SessionManagementSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http.authorizeRequests().anyRequest().hasRole("USER").and().formLogin()
	 * 				.permitAll().and().sessionManagement().maximumSessions(1)
	 * 				.expiredUrl("/login?expired");
	 * 	}
	 *
	 * 	@Override
	 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 * 		auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
	 * 	}
	 * }
	 *

	 *
	 * When using {@link SessionManagementConfigurer#maximumSessions(int)}, do not forget
	 * to configure {@link HttpSessionEventPublisher} for the application to ensure that
	 * expired sessions are cleaned up.
	 *
	 * In a web.xml this can be configured using the following:
	 *
	 *

	 * <listener>
	 *      <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
	 * </listener>
	 *

	 *
	 * Alternatively,
	 * {@link AbstractSecurityWebApplicationInitializer#enableHttpSessionEventPublisher()}
	 * could return true.
	 *
	 * @return the {@link SessionManagementConfigurer} for further customizations
	 * @throws Exception
	 */
	public SessionManagementConfigurer<HttpSecurity> sessionManagement() throws Exception {
		return getOrApply(new SessionManagementConfigurer<>());
	}

	/**
	 * Allows configuring a {@link PortMapper} that is available from
	 * {@link HttpSecurity#getSharedObject(Class)}. Other provided
	 * {@link SecurityConfigurer} objects use this configured {@link PortMapper} as a
	 * default {@link PortMapper} when redirecting from HTTP to HTTPS or from HTTPS to
	 * HTTP (for example when used in combination with {@link #requiresChannel()}. By
	 * default Spring Security uses a {@link PortMapperImpl} which maps the HTTP port 8080
	 * to the HTTPS port 8443 and the HTTP port of 80 to the HTTPS port of 443.
	 *
	 *
Example Configuration

	 *
	 * The following configuration will ensure that redirects within Spring Security from
	 * HTTP of a port of 9090 will redirect to HTTPS port of 9443 and the HTTP port of 80
	 * to the HTTPS port of 443.
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class PortMapperSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin()
	 * 				.permitAll().and()
	 * 				// Example portMapper() configuration
	 * 				.portMapper().http(9090).mapsTo(9443).http(80).mapsTo(443);
	 * 	}
	 *
	 * 	@Override
	 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 * 		auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
	 * 	}
	 * }
	 *

	 *
	 * @return the {@link PortMapperConfigurer} for further customizations
	 * @throws Exception
	 * @see #requiresChannel()
	 */
	public PortMapperConfigurer<HttpSecurity> portMapper() throws Exception {
		return getOrApply(new PortMapperConfigurer<>());
	}

	/**
	 * Configures container based pre authentication. In this case, authentication
	 * is managed by the Servlet Container.
	 *
	 *
Example Configuration

	 *
	 * The following configuration will use the principal found on the
	 * {@link HttpServletRequest} and if the user is in the role "ROLE_USER" or
	 * "ROLE_ADMIN" will add that to the resulting {@link Authentication}.
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class JeeSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http.authorizeRequests().antMatchers("/**").hasRole("USER").and()
	 * 		// Example jee() configuration
	 * 				.jee().mappableRoles("ROLE_USER", "ROLE_ADMIN");
	 * 	}
	 * }
	 *

	 *
	 * Developers wishing to use pre authentication with the container will need to ensure
	 * their web.xml configures the security constraints. For example, the web.xml (there
	 * is no equivalent Java based configuration supported by the Servlet specification)
	 * might look like:
	 *
	 *

	 * <login-config>
	 *     <auth-method>FORM</auth-method>
	 *     <form-login-config>
	 *         <form-login-page>/login</form-login-page>
	 *         <form-error-page>/login?error</form-error-page>
	 *     </form-login-config>
	 * </login-config>
	 *
	 * <security-role>
	 *     <role-name>ROLE_USER</role-name>
	 * </security-role>
	 * <security-constraint>
	 *     <web-resource-collection>
	 *     <web-resource-name>Public</web-resource-name>
	 *         <description>Matches unconstrained pages</description>
	 *         <url-pattern>/login</url-pattern>
	 *         <url-pattern>/logout</url-pattern>
	 *         <url-pattern>/resources/*</url-pattern>
	 *     </web-resource-collection>
	 * </security-constraint>
	 * <security-constraint>
	 *     <web-resource-collection>
	 *         <web-resource-name>Secured Areas</web-resource-name>
	 *         <url-pattern>/*</url-pattern>
	 *     </web-resource-collection>
	 *     <auth-constraint>
	 *         <role-name>ROLE_USER</role-name>
	 *     </auth-constraint>
	 * </security-constraint>
	 *

	 *
	 * Last you will need to configure your container to contain the user with the correct
	 * roles. This configuration is specific to the Servlet Container, so consult your
	 * Servlet Container's documentation.
	 *
	 * @return the {@link JeeConfigurer} for further customizations
	 * @throws Exception
	 */
	public JeeConfigurer<HttpSecurity> jee() throws Exception {
		return getOrApply(new JeeConfigurer<>());
	}

	/**
	 * Configures X509 based pre authentication.
	 *
	 *
Example Configuration

	 *
	 * The following configuration will attempt to extract the username from the X509
	 * certificate. Remember that the Servlet Container will need to be configured to
	 * request client certificates in order for this to work.
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class X509SecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http.authorizeRequests().antMatchers("/**").hasRole("USER").and()
	 * 		// Example x509() configuration
	 * 				.x509();
	 * 	}
	 * }
	 *

	 *
	 * @return the {@link X509Configurer} for further customizations
	 * @throws Exception
	 */
	public X509Configurer<HttpSecurity> x509() throws Exception {
		return getOrApply(new X509Configurer<>());
	}

	/**
	 * Allows configuring of Remember Me authentication.
	 *
	 *
Example Configuration

	 *
	 * The following configuration demonstrates how to allow token based remember me
	 * authentication. Upon authenticating if the HTTP parameter named "remember-me"
	 * exists, then the user will be remembered even after their
	 * {@link javax.servlet.http.HttpSession} expires.
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class RememberMeSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 * 		auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
	 * 	}
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin()
	 * 				.permitAll().and()
	 * 				// Example Remember Me Configuration
	 * 				.rememberMe();
	 * 	}
	 * }
	 *

	 *
	 * @return the {@link RememberMeConfigurer} for further customizations
	 * @throws Exception
	 */
	public RememberMeConfigurer<HttpSecurity> rememberMe() throws Exception {
		return getOrApply(new RememberMeConfigurer<>());
	}

	/**
	 * Allows restricting access based upon the {@link HttpServletRequest} using
	 *
	 *
Example Configurations

	 *
	 * The most basic example is to configure all URLs to require the role "ROLE_USER".
	 * The configuration below requires authentication to every URL and will grant access
	 * to both the user "admin" and "user".
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin();
	 * 	}
	 *
	 * 	@Override
	 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 * 		auth.inMemoryAuthentication().withUser("user").password("password").roles("USER")
	 * 				.and().withUser("admin").password("password").roles("ADMIN", "USER");
	 * 	}
	 * }
	 *

	 *
	 * We can also configure multiple URLs. The configuration below requires
	 * authentication to every URL and will grant access to URLs starting with /admin/ to
	 * only the "admin" user. All other URLs either user can access.
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN")
	 * 				.antMatchers("/**").hasRole("USER").and().formLogin();
	 * 	}
	 *
	 * 	@Override
	 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 * 		auth.inMemoryAuthentication().withUser("user").password("password").roles("USER")
	 * 				.and().withUser("admin").password("password").roles("ADMIN", "USER");
	 * 	}
	 * }
	 *

	 *
	 * Note that the matchers are considered in order. Therefore, the following is invalid
	 * because the first matcher matches every request and will never get to the second
	 * mapping:
	 *
	 *

	 * http.authorizeRequests().antMatchers("/**").hasRole("USER").antMatchers("/admin/**")
	 * 		.hasRole("ADMIN")
	 *

	 *
	 * @see #requestMatcher(RequestMatcher)
	 *
	 * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customizations
	 * @throws Exception
	 */
	public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests()
			throws Exception {
		ApplicationContext context = getContext();
		return getOrApply(new ExpressionUrlAuthorizationConfigurer<>(context))
				.getRegistry();
	}

	/**
	 * Allows configuring the Request Cache. For example, a protected page (/protected)
	 * may be requested prior to authentication. The application will redirect the user to
	 * a login page. After authentication, Spring Security will redirect the user to the
	 * originally requested protected page (/protected). This is automatically applied
	 * when using {@link WebSecurityConfigurerAdapter}.
	 *
	 * @return the {@link RequestCacheConfigurer} for further customizations
	 * @throws Exception
	 */
	public RequestCacheConfigurer<HttpSecurity> requestCache() throws Exception {
		return getOrApply(new RequestCacheConfigurer<>());
	}

	/**
	 * Allows configuring exception handling. This is automatically applied when using
	 * {@link WebSecurityConfigurerAdapter}.
	 *
	 * @return the {@link ExceptionHandlingConfigurer} for further customizations
	 * @throws Exception
	 */
	public ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling() throws Exception {
		return getOrApply(new ExceptionHandlingConfigurer<>());
	}

	/**
	 * Sets up management of the {@link SecurityContext} on the
	 * {@link SecurityContextHolder} between {@link HttpServletRequest}'s. This is
	 * automatically applied when using {@link WebSecurityConfigurerAdapter}.
	 *
	 * @return the {@link SecurityContextConfigurer} for further customizations
	 * @throws Exception
	 */
	public SecurityContextConfigurer<HttpSecurity> securityContext() throws Exception {
		return getOrApply(new SecurityContextConfigurer<>());
	}

	/**
	 * Integrates the {@link HttpServletRequest} methods with the values found on the
	 * {@link SecurityContext}. This is automatically applied when using
	 * {@link WebSecurityConfigurerAdapter}.
	 *
	 * @return the {@link ServletApiConfigurer} for further customizations
	 * @throws Exception
	 */
	public ServletApiConfigurer<HttpSecurity> servletApi() throws Exception {
		return getOrApply(new ServletApiConfigurer<>());
	}

	/**
	 * Adds CSRF support. This is activated by default when using
	 * {@link WebSecurityConfigurerAdapter}'s default constructor. You can disable it
	 * using:
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 *     protected void configure(HttpSecurity http) throws Exception {
	 *         http
	 *             .csrf().disable()
	 *             ...;
	 *     }
	 * }
	 *

	 *
	 * @return the {@link ServletApiConfigurer} for further customizations
	 * @throws Exception
	 */
	public CsrfConfigurer<HttpSecurity> csrf() throws Exception {
		ApplicationContext context = getContext();
		return getOrApply(new CsrfConfigurer<>(context));
	}

	/**
	 * Provides logout support. This is automatically applied when using
	 * {@link WebSecurityConfigurerAdapter}. The default is that accessing the URL
	 * "/logout" will log the user out by invalidating the HTTP Session, cleaning up any
	 * {@link #rememberMe()} authentication that was configured, clearing the
	 * {@link SecurityContextHolder}, and then redirect to "/login?success".
	 *
	 *
Example Custom Configuration

	 *
	 * The following customization to log out when the URL "/custom-logout" is invoked.
	 * Log out will remove the cookie named "remove", not invalidate the HttpSession,
	 * clear the SecurityContextHolder, and upon completion redirect to "/logout-success".
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class LogoutSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin()
	 * 				.and()
	 * 				// sample logout customization
	 * 				.logout().deleteCookies("remove").invalidateHttpSession(false)
	 * 				.logoutUrl("/custom-logout").logoutSuccessUrl("/logout-success");
	 * 	}
	 *
	 * 	@Override
	 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 * 		auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
	 * 	}
	 * }
	 *

	 *
	 * @return the {@link LogoutConfigurer} for further customizations
	 * @throws Exception
	 */
	public LogoutConfigurer<HttpSecurity> logout() throws Exception {
		return getOrApply(new LogoutConfigurer<>());
	}

	/**
	 * Allows configuring how an anonymous user is represented. This is automatically
	 * applied when used in conjunction with {@link WebSecurityConfigurerAdapter}. By
	 * default anonymous users will be represented with an
	 * {@link org.springframework.security.authentication.AnonymousAuthenticationToken}
	 * and contain the role "ROLE_ANONYMOUS".
	 *
	 *
Example Configuration

	 *
	 * The following configuration demonstrates how to specify that anonymous users should
	 * contain the role "ROLE_ANON" instead.
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class AnononymousSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin()
	 * 				.and()
	 * 				// sample anonymous customization
	 * 				.anonymous().authorities("ROLE_ANON");
	 * 	}
	 *
	 * 	@Override
	 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 * 		auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
	 * 	}
	 * }
	 *

	 *
	 * The following demonstrates how to represent anonymous users as null. Note that this
	 * can cause {@link NullPointerException} in code that assumes anonymous
	 * authentication is enabled.
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class AnononymousSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin()
	 * 				.and()
	 * 				// sample anonymous customization
	 * 				.anonymous().disabled();
	 * 	}
	 *
	 * 	@Override
	 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 * 		auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
	 * 	}
	 * }
	 *

	 *
	 * @return the {@link AnonymousConfigurer} for further customizations
	 * @throws Exception
	 */
	public AnonymousConfigurer<HttpSecurity> anonymous() throws Exception {
		return getOrApply(new AnonymousConfigurer<>());
	}

	/**
	 * Specifies to support form based authentication. If
	 * {@link FormLoginConfigurer#loginPage(String)} is not specified a default login page
	 * will be generated.
	 *
	 *
Example Configurations

	 *
	 * The most basic configuration defaults to automatically generating a login page at
	 * the URL "/login", redirecting to "/login?error" for authentication failure. The
	 * details of the login page can be found on
	 * {@link FormLoginConfigurer#loginPage(String)}
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin();
	 * 	}
	 *
	 * 	@Override
	 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 * 		auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
	 * 	}
	 * }
	 *

	 *
	 * The configuration below demonstrates customizing the defaults.
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class FormLoginSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin()
	 * 				.usernameParameter("username") // default is username
	 * 				.passwordParameter("password") // default is password
	 * 				.loginPage("/authentication/login") // default is /login with an HTTP get
	 * 				.failureUrl("/authentication/login?failed") // default is /login?error
	 * 				.loginProcessingUrl("/authentication/login/process"); // default is /login
	 * 																		// with an HTTP
	 * 																		// post
	 * 	}
	 *
	 * 	@Override
	 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 * 		auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
	 * 	}
	 * }
	 *

	 *
	 * @see FormLoginConfigurer#loginPage(String)
	 *
	 * @return the {@link FormLoginConfigurer} for further customizations
	 * @throws Exception
	 */
	public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
		return getOrApply(new FormLoginConfigurer<>());
	}

	/**
	 * Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider.
	 *

	 *

	 *
	 * The "authentication flow" is implemented using the Authorization Code Grant, as specified in the
	 * OAuth 2.0 Authorization Framework
	 * and OpenID Connect Core 1.0
	 * specification.
	 *

	 *

	 *
	 * As a prerequisite to using this feature, you must register a client with a provider.
	 * The client registration information may than be used for configuring
	 * a {@link org.springframework.security.oauth2.client.registration.ClientRegistration} using a
	 * {@link org.springframework.security.oauth2.client.registration.ClientRegistration.Builder}.
	 *

	 *

	 *
	 * {@link org.springframework.security.oauth2.client.registration.ClientRegistration}(s) are composed within a
	 * {@link org.springframework.security.oauth2.client.registration.ClientRegistrationRepository},
	 * which is required and must be registered with the {@link ApplicationContext} or
	 * configured via oauth2Login().clientRegistrationRepository(..).
	 *

	 *

	 *
	 * The default configuration provides an auto-generated login page at "/login" and
	 * redirects to "/login?error" when an authentication error occurs.
	 * The login page will display each of the clients with a link
	 * that is capable of initiating the "authentication flow".
	 *

	 *

	 *
	 *

	 *
Example Configuration

	 *
	 * The following example shows the minimal configuration required, using Google as the Authentication Provider.
	 *
	 *

	 * @Configuration
	 * public class OAuth2LoginConfig {
	 *
	 * 	@EnableWebSecurity
	 * 	public static class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
	 * 		@Override
	 * 		protected void configure(HttpSecurity http) throws Exception {
	 * 			http
	 * 				.authorizeRequests()
	 * 					.anyRequest().authenticated()
	 * 					.and()
	 * 				  .oauth2Login();
	 *		}
	 *	}
	 *
	 *	@Bean
	 *	public ClientRegistrationRepository clientRegistrationRepository() {
	 *		return new InMemoryClientRegistrationRepository(this.googleClientRegistration());
	 *	}
	 *
	 * 	private ClientRegistration googleClientRegistration() {
	 * 		return ClientRegistration.withRegistrationId("google")
	 * 			.clientId("google-client-id")
	 * 			.clientSecret("google-client-secret")
	 * 			.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
	 * 			.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
	 * 			.redirectUriTemplate("{baseUrl}/login/oauth2/code/{registrationId}")
	 * 			.scope("openid", "profile", "email", "address", "phone")
	 * 			.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth")
	 * 			.tokenUri("https://www.googleapis.com/oauth2/v4/token")
	 * 			.userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo")
	 * 			.userNameAttributeName(IdTokenClaimNames.SUB)
	 * 			.jwkSetUri("https://www.googleapis.com/oauth2/v3/certs")
	 * 			.clientName("Google")
	 * 			.build();
	 *	}
	 * }
	 *

	 *
	 *

	 * For more advanced configuration, see {@link OAuth2LoginConfigurer} for available options to customize the defaults.
	 *
	 * @since 5.0
	 * @see Section 4.1 Authorization Code Grant
	 * @see Section 3.1 Authorization Code Flow
	 * @see org.springframework.security.oauth2.client.registration.ClientRegistration
	 * @see org.springframework.security.oauth2.client.registration.ClientRegistrationRepository
	 * @return the {@link OAuth2LoginConfigurer} for further customizations
	 * @throws Exception
	 */
	public OAuth2LoginConfigurer<HttpSecurity> oauth2Login() throws Exception {
		return getOrApply(new OAuth2LoginConfigurer<>());
	}

	/**
	 * Configures OAuth 2.0 Client support.
	 *
	 * @since 5.1
	 * @see OAuth 2.0 Authorization Framework
	 * @return the {@link OAuth2ClientConfigurer} for further customizations
	 * @throws Exception
	 */
	public OAuth2ClientConfigurer<HttpSecurity> oauth2Client() throws Exception {
		OAuth2ClientConfigurer<HttpSecurity> configurer = getOrApply(new OAuth2ClientConfigurer<>());
		this.postProcess(configurer);
		return configurer;
	}

	/**
	 * Configures OAuth 2.0 Resource Server support.
	 *
	 * @since 5.1
	 * @see OAuth 2.0 Authorization Framework
	 * @return the {@link OAuth2ResourceServerConfigurer} for further customizations
	 * @throws Exception
	 */
	public OAuth2ResourceServerConfigurer<HttpSecurity> oauth2ResourceServer() throws Exception {
		OAuth2ResourceServerConfigurer<HttpSecurity> configurer = getOrApply(new OAuth2ResourceServerConfigurer<>(getContext()));
		this.postProcess(configurer);
		return configurer;
	}

	/**
	 * Configures channel security. In order for this configuration to be useful at least
	 * one mapping to a required channel must be provided.
	 *
	 *
Example Configuration

	 *
	 * The example below demonstrates how to require HTTPs for every request. Only
	 * requiring HTTPS for some requests is supported, but not recommended since an
	 * application that allows for HTTP introduces many security vulnerabilities. For one
	 * such example, read about Firesheep.
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class ChannelSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin()
	 * 				.and().requiresChannel().anyRequest().requiresSecure();
	 * 	}
	 *
	 * 	@Override
	 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 * 		auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
	 * 	}
	 * }
	 *

	 *
	 *
	 * @return the {@link ChannelSecurityConfigurer} for further customizations
	 * @throws Exception
	 */
	public ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry requiresChannel()
			throws Exception {
		ApplicationContext context = getContext();
		return getOrApply(new ChannelSecurityConfigurer<>(context))
				.getRegistry();
	}

	/**
	 * Configures HTTP Basic authentication.
	 *
	 *
Example Configuration

	 *
	 * The example below demonstrates how to configure HTTP Basic authentication for an
	 * application. The default realm is "Realm", but can be
	 * customized using {@link HttpBasicConfigurer#realmName(String)}.
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class HttpBasicSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http.authorizeRequests().antMatchers("/**").hasRole("USER").and().httpBasic();
	 * 	}
	 *
	 * 	@Override
	 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 * 		auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
	 * 	}
	 * }
	 *

	 *
	 * @return the {@link HttpBasicConfigurer} for further customizations
	 * @throws Exception
	 */
	public HttpBasicConfigurer<HttpSecurity> httpBasic() throws Exception {
		return getOrApply(new HttpBasicConfigurer<>());
	}

	public <C> void setSharedObject(Class<C> sharedType, C object) {
		super.setSharedObject(sharedType, object);
	}

	@Override
	protected void beforeConfigure() throws Exception {
		setSharedObject(AuthenticationManager.class, getAuthenticationRegistry().build());
	}

	@Override
	protected DefaultSecurityFilterChain performBuild() throws Exception {
		Collections.sort(filters, comparator);
		return new DefaultSecurityFilterChain(requestMatcher, filters);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.springframework.security.config.annotation.web.HttpSecurityBuilder#authenticationProvider
	 * (org.springframework.security.authentication.AuthenticationProvider)
	 */
	public HttpSecurity authenticationProvider(
			AuthenticationProvider authenticationProvider) {
		getAuthenticationRegistry().authenticationProvider(authenticationProvider);
		return this;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.springframework.security.config.annotation.web.HttpSecurityBuilder#userDetailsService
	 * (org.springframework.security.core.userdetails.UserDetailsService)
	 */
	public HttpSecurity userDetailsService(UserDetailsService userDetailsService)
			throws Exception {
		getAuthenticationRegistry().userDetailsService(userDetailsService);
		return this;
	}

	private AuthenticationManagerBuilder getAuthenticationRegistry() {
		return getSharedObject(AuthenticationManagerBuilder.class);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.springframework.security.config.annotation.web.HttpSecurityBuilder#addFilterAfter(javax
	 * .servlet.Filter, java.lang.Class)
	 */
	public HttpSecurity addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) {
		comparator.registerAfter(filter.getClass(), afterFilter);
		return addFilter(filter);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.springframework.security.config.annotation.web.HttpSecurityBuilder#addFilterBefore(
	 * javax.servlet.Filter, java.lang.Class)
	 */
	public HttpSecurity addFilterBefore(Filter filter,
			Class<? extends Filter> beforeFilter) {
		comparator.registerBefore(filter.getClass(), beforeFilter);
		return addFilter(filter);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.springframework.security.config.annotation.web.HttpSecurityBuilder#addFilter(javax.
	 * servlet.Filter)
	 */
	public HttpSecurity addFilter(Filter filter) {
		Class<? extends Filter> filterClass = filter.getClass();
		if (!comparator.isRegistered(filterClass)) {
			throw new IllegalArgumentException(
					"The Filter class "
							+ filterClass.getName()
							+ " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
		}
		this.filters.add(filter);
		return this;
	}

	/**
	 * Adds the Filter at the location of the specified Filter class. For example, if you
	 * want the filter CustomFilter to be registered in the same position as
	 * {@link UsernamePasswordAuthenticationFilter}, you can invoke:
	 *
	 *

	 * addFilterAt(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)
	 *

	 *
	 * Registration of multiple Filters in the same location means their ordering is not
	 * deterministic. More concretely, registering multiple Filters in the same location
	 * does not override existing Filters. Instead, do not register Filters you do not
	 * want to use.
	 *
	 * @param filter the Filter to register
	 * @param atFilter the location of another {@link Filter} that is already registered
	 * (i.e. known) with Spring Security.
	 * @return the {@link HttpSecurity} for further customizations
	 */
	public HttpSecurity addFilterAt(Filter filter, Class<? extends Filter> atFilter) {
		this.comparator.registerAt(filter.getClass(), atFilter);
		return addFilter(filter);
	}

	/**
	 * Allows specifying which {@link HttpServletRequest} instances this
	 * {@link HttpSecurity} will be invoked on. This method allows for easily invoking the
	 * {@link HttpSecurity} for multiple different {@link RequestMatcher} instances. If
	 * only a single {@link RequestMatcher} is necessary consider using {@link #mvcMatcher(String)},
	 * {@link #antMatcher(String)}, {@link #regexMatcher(String)}, or
	 * {@link #requestMatcher(RequestMatcher)}.
	 *
	 *

	 * Invoking {@link #requestMatchers()} will not override previous invocations of {@link #mvcMatcher(String)}},
	 * {@link #requestMatchers()}, {@link #antMatcher(String)},
	 * {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
	 *

	 *
	 *
Example Configurations

	 *
	 * The following configuration enables the {@link HttpSecurity} for URLs that begin
	 * with "/api/" or "/oauth/".
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class RequestMatchersSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http
	 * 			.requestMatchers()
	 * 				.antMatchers("/api/**", "/oauth/**")
	 * 				.and()
	 * 			.authorizeRequests()
	 * 				.antMatchers("/**").hasRole("USER")
	 * 				.and()
	 * 			.httpBasic();
	 * 	}
	 *
	 * 	@Override
	 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 * 		auth
	 * 			.inMemoryAuthentication()
	 * 				.withUser("user").password("password").roles("USER");
	 * 	}
	 * }
	 *

	 *
	 * The configuration below is the same as the previous configuration.
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class RequestMatchersSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http
	 * 			.requestMatchers()
	 * 				.antMatchers("/api/**")
	 * 				.antMatchers("/oauth/**")
	 * 				.and()
	 * 			.authorizeRequests()
	 * 				.antMatchers("/**").hasRole("USER")
	 * 				.and()
	 * 			.httpBasic();
	 * 	}
	 *
	 * 	@Override
	 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 * 		auth
	 * 			.inMemoryAuthentication()
	 * 				.withUser("user").password("password").roles("USER");
	 * 	}
	 * }
	 *

	 *
	 * The configuration below is also the same as the above configuration.
	 *
	 *

	 * @Configuration
	 * @EnableWebSecurity
	 * public class RequestMatchersSecurityConfig extends WebSecurityConfigurerAdapter {
	 *
	 * 	@Override
	 * 	protected void configure(HttpSecurity http) throws Exception {
	 * 		http
	 * 			.requestMatchers()
	 * 				.antMatchers("/api/**")
	 * 				.and()
	 *			 .requestMatchers()
	 * 				.antMatchers("/oauth/**")
	 * 				.and()
	 * 			.authorizeRequests()
	 * 				.antMatchers("/**").hasRole("USER")
	 * 				.and()
	 * 			.httpBasic();
	 * 	}
	 *
	 * 	@Override
	 * 	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 * 		auth
	 * 			.inMemoryAuthentication()
	 * 				.withUser("user").password("password").roles("USER");
	 * 	}
	 * }
	 *

	 *
	 * @return the {@link RequestMatcherConfigurer} for further customizations
	 */
	public RequestMatcherConfigurer requestMatchers() {
		return requestMatcherConfigurer;
	}

	/**
	 * Allows configuring the {@link HttpSecurity} to only be invoked when matching the
	 * provided {@link RequestMatcher}. If more advanced configuration is necessary,
	 * consider using {@link #requestMatchers()}.
	 *
	 *

	 * Invoking {@link #requestMatcher(RequestMatcher)} will override previous invocations
	 * of {@link #requestMatchers()}, {@link #mvcMatcher(String)}, {@link #antMatcher(String)},
	 * {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
	 *

	 *
	 * @param requestMatcher the {@link RequestMatcher} to use (i.e. new
	 * AntPathRequestMatcher("/admin/**","GET") )
	 * @return the {@link HttpSecurity} for further customizations
	 * @see #requestMatchers()
	 * @see #antMatcher(String)
	 * @see #regexMatcher(String)
	 */
	public HttpSecurity requestMatcher(RequestMatcher requestMatcher) {
		this.requestMatcher = requestMatcher;
		return this;
	}

	/**
	 * Allows configuring the {@link HttpSecurity} to only be invoked when matching the
	 * provided ant pattern. If more advanced configuration is necessary, consider using
	 * {@link #requestMatchers()} or {@link #requestMatcher(RequestMatcher)}.
	 *
	 *

	 * Invoking {@link #antMatcher(String)} will override previous invocations of {@link #mvcMatcher(String)}},
	 * {@link #requestMatchers()}, {@link #antMatcher(String)},
	 * {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
	 *

	 *
	 * @param antPattern the Ant Pattern to match on (i.e. "/admin/**")
	 * @return the {@link HttpSecurity} for further customizations
	 * @see AntPathRequestMatcher
	 */
	public HttpSecurity antMatcher(String antPattern) {
		return requestMatcher(new AntPathRequestMatcher(antPattern));
	}

	/**
	 * Allows configuring the {@link HttpSecurity} to only be invoked when matching the
	 * provided Spring MVC pattern. If more advanced configuration is necessary, consider using
	 * {@link #requestMatchers()} or {@link #requestMatcher(RequestMatcher)}.
	 *
	 *

	 * Invoking {@link #mvcMatcher(String)} will override previous invocations of {@link #mvcMatcher(String)}},
	 * {@link #requestMatchers()}, {@link #antMatcher(String)},
	 * {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
	 *

	 *
	 * @param mvcPattern the Spring MVC Pattern to match on (i.e. "/admin/**")
	 * @return the {@link HttpSecurity} for further customizations
	 * @see MvcRequestMatcher
	 */
	public HttpSecurity mvcMatcher(String mvcPattern) {
		HandlerMappingIntrospector introspector = new HandlerMappingIntrospector(getContext());
		return requestMatcher(new MvcRequestMatcher(introspector, mvcPattern));
	}

	/**
	 * Allows configuring the {@link HttpSecurity} to only be invoked when matching the
	 * provided regex pattern. If more advanced configuration is necessary, consider using
	 * {@link #requestMatchers()} or {@link #requestMatcher(RequestMatcher)}.
	 *
	 *

	 * Invoking {@link #regexMatcher(String)} will override previous invocations of {@link #mvcMatcher(String)}},
	 * {@link #requestMatchers()}, {@link #antMatcher(String)},
	 * {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
	 *

	 *
	 * @param pattern the Regular Expression to match on (i.e. "/admin/.+")
	 * @return the {@link HttpSecurity} for further customizations
	 * @see RegexRequestMatcher
	 */
	public HttpSecurity regexMatcher(String pattern) {
		return requestMatcher(new RegexRequestMatcher(pattern, null));
	}

	/**
	 * An extension to {@link RequestMatcherConfigurer} that allows optionally configuring
	 * the servlet path.
	 *
	 * @author Rob Winch
	 */
	public final class MvcMatchersRequestMatcherConfigurer extends RequestMatcherConfigurer {

		/**
		 * Creates a new instance
		 * @param context the {@link ApplicationContext} to use
		 * @param matchers the {@link MvcRequestMatcher} instances to set the servlet path
		 * on if {@link #servletPath(String)} is set.
		 */
		private MvcMatchersRequestMatcherConfigurer(ApplicationContext context,
				List<MvcRequestMatcher> matchers) {
			super(context);
			this.matchers = new ArrayList<>(matchers);
		}

		public RequestMatcherConfigurer servletPath(String servletPath) {
			for (RequestMatcher matcher : this.matchers) {
				((MvcRequestMatcher) matcher).setServletPath(servletPath);
			}
			return this;
		}

	}

	/**
	 * Allows mapping HTTP requests that this {@link HttpSecurity} will be used for
	 *
	 * @author Rob Winch
	 * @since 3.2
	 */
	public class RequestMatcherConfigurer
			extends AbstractRequestMatcherRegistry<RequestMatcherConfigurer> {

		protected List<RequestMatcher> matchers = new ArrayList<>();

		/**
		 * @param context
		 */
		private RequestMatcherConfigurer(ApplicationContext context) {
			setApplicationContext(context);
		}

		@Override
		public MvcMatchersRequestMatcherConfigurer mvcMatchers(HttpMethod method,
				String... mvcPatterns) {
			List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
			setMatchers(mvcMatchers);
			return new MvcMatchersRequestMatcherConfigurer(getContext(), mvcMatchers);
		}

		@Override
		public MvcMatchersRequestMatcherConfigurer mvcMatchers(String... patterns) {
			return mvcMatchers(null, patterns);
		}

		@Override
		protected RequestMatcherConfigurer chainRequestMatchers(
				List<RequestMatcher> requestMatchers) {
			setMatchers(requestMatchers);
			return this;
		}

		private void setMatchers(List<? extends RequestMatcher> requestMatchers) {
			this.matchers.addAll(requestMatchers);
			requestMatcher(new OrRequestMatcher(this.matchers));
		}

		/**
		 * Return the {@link HttpSecurity} for further customizations
		 *
		 * @return the {@link HttpSecurity} for further customizations
		 */
		public HttpSecurity and() {
			return HttpSecurity.this;
		}

	}

	/**
	 * If the {@link SecurityConfigurer} has already been specified get the original,
	 * otherwise apply the new {@link SecurityConfigurerAdapter}.
	 *
	 * @param configurer the {@link SecurityConfigurer} to apply if one is not found for
	 * this {@link SecurityConfigurer} class.
	 * @return the current {@link SecurityConfigurer} for the configurer passed in
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(
			C configurer) throws Exception {
		C existingConfig = (C) getConfigurer(configurer.getClass());
		if (existingConfig != null) {
			return existingConfig;
		}
		return apply(configurer);
	}
}


/*
 * Copyright 2002-2017 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.config.web.server;

import org.springframework.http.HttpMethod;
import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;

import java.util.List;

/**
 * @author Rob Winch
 * @since 5.0
 */
abstract class AbstractServerWebExchangeMatcherRegistry<T> {

	/**
	 * Maps any request.
	 *
	 * @return the object that is chained after creating the {@link ServerWebExchangeMatcher}
	 */
	public T anyExchange() {
		return matcher(ServerWebExchangeMatchers.anyExchange());
	}

	/**
	 * Maps a {@link List} of
	 * {@link PathPatternParserServerWebExchangeMatcher}
	 * instances.
	 *
	 * @param method the {@link HttpMethod} to use for any
	 * {@link HttpMethod}.
	 *
	 * @return the object that is chained after creating the {@link ServerWebExchangeMatcher}
	 */
	public T pathMatchers(HttpMethod method) {
		return pathMatchers(method, new String[] { "/**" });
	}

	/**
	 * Maps a {@link List} of
	 * {@link PathPatternParserServerWebExchangeMatcher}
	 * instances.
	 *
	 * @param method the {@link HttpMethod} to use or {@code null} for any
	 * {@link HttpMethod}.
	 * @param antPatterns the ant patterns to create. If {@code null} or empty, then matches on nothing.
	 * {@link PathPatternParserServerWebExchangeMatcher} from
	 *
	 * @return the object that is chained after creating the {@link ServerWebExchangeMatcher}
	 */
	public T pathMatchers(HttpMethod method, String... antPatterns) {
		return matcher(ServerWebExchangeMatchers.pathMatchers(method, antPatterns));
	}

	/**
	 * Maps a {@link List} of
	 * {@link PathPatternParserServerWebExchangeMatcher}
	 * instances that do not care which {@link HttpMethod} is used.
	 *
	 * @param antPatterns the ant patterns to create
	 * {@link PathPatternParserServerWebExchangeMatcher} from
	 *
	 * @return the object that is chained after creating the {@link ServerWebExchangeMatcher}
	 */
	public T pathMatchers(String... antPatterns) {
		return matcher(ServerWebExchangeMatchers.pathMatchers(antPatterns));
	}

	/**
	 * Associates a list of {@link ServerWebExchangeMatcher} instances
	 *
	 * @param matchers the {@link ServerWebExchangeMatcher} instances
	 *
	 * @return the object that is chained after creating the {@link ServerWebExchangeMatcher}
	 */
	public T matchers(ServerWebExchangeMatcher... matchers) {
		return registerMatcher(new OrServerWebExchangeMatcher(matchers));
	}

	/**
	 * Subclasses should implement this method for returning the object that is chained to
	 * the creation of the {@link ServerWebExchangeMatcher} instances.
	 *
	 * @param matcher the {@link ServerWebExchangeMatcher} instances that were created
	 * @return the chained Object for the subclass which allows association of something
	 * else to the {@link ServerWebExchangeMatcher}
	 */
	protected abstract T registerMatcher(ServerWebExchangeMatcher matcher);

	/**
	 * Associates a {@link ServerWebExchangeMatcher} instances
	 *
	 * @param matcher the {@link ServerWebExchangeMatcher} instance
	 *
	 * @return the object that is chained after creating the {@link ServerWebExchangeMatcher}
	 */
	private T matcher(ServerWebExchangeMatcher matcher) {
		return registerMatcher(matcher);
	}
}


/*
 * Copyright 2002-2013 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.ldap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.ldap.core.ContextExecutor;
import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.util.Assert;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.PartialResultException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Extension of Spring LDAP's LdapTemplate class which adds extra functionality required
 * by Spring Security.
 *
 * @author Ben Alex
 * @author Luke Taylor
 * @author Filip Hanik
 * @since 2.0
 */
public class SpringSecurityLdapTemplate extends LdapTemplate {
	// ~ Static fields/initializers
	// =====================================================================================
	private static final Log logger = LogFactory.getLog(SpringSecurityLdapTemplate.class);

	public static final String[] NO_ATTRS = new String[0];

	/**
	 * Every search results where a record is defined by a Map<String,String[]>
	 * contains at least this key - the DN of the record itself.
	 */
	public static final String DN_KEY = "spring.security.ldap.dn";

	private static final boolean RETURN_OBJECT = true;

	// ~ Instance fields
	// ================================================================================================

	/** Default search controls */
	private SearchControls searchControls = new SearchControls();

	// ~ Constructors
	// ===================================================================================================

	public SpringSecurityLdapTemplate(ContextSource contextSource) {
		Assert.notNull(contextSource, "ContextSource cannot be null");
		setContextSource(contextSource);

		searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
	}

	// ~ Methods
	// ========================================================================================================

	/**
	 * Performs an LDAP compare operation of the value of an attribute for a particular
	 * directory entry.
	 *
	 * @param dn the entry who's attribute is to be used
	 * @param attributeName the attribute who's value we want to compare
	 * @param value the value to be checked against the directory value
	 *
	 * @return true if the supplied value matches that in the directory
	 */
	public boolean compare(final String dn, final String attributeName, final Object value) {
		final String comparisonFilter = "(" + attributeName + "={0})";

		class LdapCompareCallback implements ContextExecutor {

			public Object executeWithContext(DirContext ctx) throws NamingException {
				SearchControls ctls = new SearchControls();
				ctls.setReturningAttributes(NO_ATTRS);
				ctls.setSearchScope(SearchControls.OBJECT_SCOPE);

				NamingEnumeration<SearchResult> results = ctx.search(dn,
						comparisonFilter, new Object[] { value }, ctls);

				Boolean match = Boolean.valueOf(results.hasMore());
				LdapUtils.closeEnumeration(results);

				return match;
			}
		}

		Boolean matches = (Boolean) executeReadOnly(new LdapCompareCallback());

		return matches.booleanValue();
	}

	/**
	 * Composes an object from the attributes of the given DN.
	 *
	 * @param dn the directory entry which will be read
	 * @param attributesToRetrieve the named attributes which will be retrieved from the
	 * directory entry.
	 *
	 * @return the object created by the mapper
	 */
	public DirContextOperations retrieveEntry(final String dn,
			final String[] attributesToRetrieve) {

		return (DirContextOperations) executeReadOnly(new ContextExecutor() {
			public Object executeWithContext(DirContext ctx) throws NamingException {
				Attributes attrs = ctx.getAttributes(dn, attributesToRetrieve);

				// Object object = ctx.lookup(LdapUtils.getRelativeName(dn, ctx));

				return new DirContextAdapter(attrs, new DistinguishedName(dn),
						new DistinguishedName(ctx.getNameInNamespace()));
			}
		});
	}

	/**
	 * Performs a search using the supplied filter and returns the union of the values of
	 * the named attribute found in all entries matched by the search. Note that one
	 * directory entry may have several values for the attribute. Intended for role
	 * searches and similar scenarios.
	 *
	 * @param base the DN to search in
	 * @param filter search filter to use
	 * @param params the parameters to substitute in the search filter
	 * @param attributeName the attribute who's values are to be retrieved.
	 *
	 * @return the set of String values for the attribute as a union of the values found
	 * in all the matching entries.
	 */
	public Set<String> searchForSingleAttributeValues(final String base,
			final String filter, final Object[] params, final String attributeName) {
		String[] attributeNames = new String[] { attributeName };
		Set<Map<String, List<String>>> multipleAttributeValues = searchForMultipleAttributeValues(
				base, filter, params, attributeNames);
		Set<String> result = new HashSet<>();
		for (Map<String, List<String>> map : multipleAttributeValues) {
			List<String> values = map.get(attributeName);
			if (values != null) {
				result.addAll(values);
			}
		}
		return result;
	}

	/**
	 * Performs a search using the supplied filter and returns the values of each named
	 * attribute found in all entries matched by the search. Note that one directory entry
	 * may have several values for the attribute. Intended for role searches and similar
	 * scenarios.
	 *
	 * @param base the DN to search in
	 * @param filter search filter to use
	 * @param params the parameters to substitute in the search filter
	 * @param attributeNames the attributes' values that are to be retrieved.
	 *
	 * @return the set of String values for each attribute found in all the matching
	 * entries. The attribute name is the key for each set of values. In addition each map
	 * contains the DN as a String with the key predefined key {@link #DN_KEY}.
	 */
	public Set<Map<String, List<String>>> searchForMultipleAttributeValues(
			final String base, final String filter, final Object[] params,
			final String[] attributeNames) {
		// Escape the params acording to RFC2254
		Object[] encodedParams = new String[params.length];

		for (int i = 0; i < params.length; i++) {
			encodedParams[i] = LdapEncoder.filterEncode(params[i].toString());
		}

		String formattedFilter = MessageFormat.format(filter, encodedParams);
		logger.debug("Using filter: " + formattedFilter);

		final HashSet<Map<String, List<String>>> set = new HashSet<Map<String, List<String>>>();

		ContextMapper roleMapper = new ContextMapper() {
			public Object mapFromContext(Object ctx) {
				DirContextAdapter adapter = (DirContextAdapter) ctx;
				Map<String, List<String>> record = new HashMap<String, List<String>>();
				if (attributeNames == null || attributeNames.length == 0) {
					try {
						for (NamingEnumeration ae = adapter.getAttributes().getAll(); ae
								.hasMore();) {
							Attribute attr = (Attribute) ae.next();
							extractStringAttributeValues(adapter, record, attr.getID());
						}
					}
					catch (NamingException x) {
						org.springframework.ldap.support.LdapUtils
								.convertLdapException(x);
					}
				}
				else {
					for (String attributeName : attributeNames) {
						extractStringAttributeValues(adapter, record, attributeName);
					}
				}
				record.put(DN_KEY, Arrays.asList(getAdapterDN(adapter)));
				set.add(record);
				return null;
			}
		};

		SearchControls ctls = new SearchControls();
		ctls.setSearchScope(searchControls.getSearchScope());
		ctls.setReturningAttributes(attributeNames != null && attributeNames.length > 0 ? attributeNames
				: null);

		search(base, formattedFilter, ctls, roleMapper);

		return set;
	}

	/**
	 * Returns the DN for the context representing this LDAP record. By default this is
	 * using {@link javax.naming.Context#getNameInNamespace()} instead of
	 * {@link org.springframework.ldap.core.DirContextAdapter#getDn()} since the latter
	 * returns a partial DN if a base has been specified.
	 * @param adapter - the Context to extract the DN from
	 * @return - the String representing the full DN
	 */
	private String getAdapterDN(DirContextAdapter adapter) {
		// returns the full DN rather than the sub DN if a base is specified
		return adapter.getNameInNamespace();
	}

	/**
	 * Extracts String values for a specified attribute name and places them in the map
	 * representing the ldap record If a value is not of type String, it will derive it's
	 * value from the {@link Object#toString()}
	 *
	 * @param adapter - the adapter that contains the values
	 * @param record - the map holding the attribute names and values
	 * @param attributeName - the name for which to fetch the values from
	 */
	private void extractStringAttributeValues(DirContextAdapter adapter,
			Map<String, List<String>> record, String attributeName) {
		Object[] values = adapter.getObjectAttributes(attributeName);
		if (values == null || values.length == 0) {
			if (logger.isDebugEnabled()) {
				logger.debug("No attribute value found for '" + attributeName + "'");
			}
			return;
		}
		List<String> svalues = new ArrayList<>();
		for (Object o : values) {
			if (o != null) {
				if (String.class.isAssignableFrom(o.getClass())) {
					svalues.add((String) o);
				}
				else {
					if (logger.isDebugEnabled()) {
						logger.debug("Attribute:" + attributeName
								+ " contains a non string value of type[" + o.getClass()
								+ "]");
					}
					svalues.add(o.toString());
				}
			}
		}
		record.put(attributeName, svalues);
	}

	/**
	 * Performs a search, with the requirement that the search shall return a single
	 * directory entry, and uses the supplied mapper to create the object from that entry.
	 * <p>
	 * Ignores <tt>PartialResultException</tt> if thrown, for compatibility with Active
	 * Directory (see {@link LdapTemplate#setIgnorePartialResultException(boolean)}).
	 *
	 * @param base the search base, relative to the base context supplied by the context
	 * source.
	 * @param filter the LDAP search filter
	 * @param params parameters to be substituted in the search.
	 *
	 * @return a DirContextOperations instance created from the matching entry.
	 *
	 * @throws IncorrectResultSizeDataAccessException if no results are found or the
	 * search returns more than one result.
	 */
	public DirContextOperations searchForSingleEntry(final String base,
			final String filter, final Object[] params) {

		return (DirContextOperations) executeReadOnly(new ContextExecutor() {
			public Object executeWithContext(DirContext ctx) throws NamingException {
				return searchForSingleEntryInternal(ctx, searchControls, base, filter,
						params);
			}
		});
	}

	/**
	 * Internal method extracted to avoid code duplication in AD search.
	 */
	public static DirContextOperations searchForSingleEntryInternal(DirContext ctx,
			SearchControls searchControls, String base, String filter, Object[] params)
			throws NamingException {
		final DistinguishedName ctxBaseDn = new DistinguishedName(
				ctx.getNameInNamespace());
		final DistinguishedName searchBaseDn = new DistinguishedName(base);
		final NamingEnumeration<SearchResult> resultsEnum = ctx.search(searchBaseDn,
				filter, params, buildControls(searchControls));

		if (logger.isDebugEnabled()) {
			logger.debug("Searching for entry under DN '" + ctxBaseDn + "', base = '"
					+ searchBaseDn + "', filter = '" + filter + "'");
		}

		Set<DirContextOperations> results = new HashSet<>();
		try {
			while (resultsEnum.hasMore()) {
				SearchResult searchResult = resultsEnum.next();
				DirContextAdapter dca = (DirContextAdapter) searchResult.getObject();
				Assert.notNull(dca,
						"No object returned by search, DirContext is not correctly configured");

				if (logger.isDebugEnabled()) {
					logger.debug("Found DN: " + dca.getDn());
				}
				results.add(dca);
			}
		}
		catch (PartialResultException e) {
			LdapUtils.closeEnumeration(resultsEnum);
			logger.info("Ignoring PartialResultException");
		}

		if (results.size() == 0) {
			throw new IncorrectResultSizeDataAccessException(1, 0);
		}

		if (results.size() > 1) {
			throw new IncorrectResultSizeDataAccessException(1, results.size());
		}

		return results.iterator().next();
	}

	/**
	 * We need to make sure the search controls has the return object flag set to true, in
	 * order for the search to return DirContextAdapter instances.
	 * @param originalControls
	 * @return
	 */
	private static SearchControls buildControls(SearchControls originalControls) {
		return new SearchControls(originalControls.getSearchScope(),
				originalControls.getCountLimit(), originalControls.getTimeLimit(),
				originalControls.getReturningAttributes(), RETURN_OBJECT,
				originalControls.getDerefLinkFlag());
	}

	/**
	 * Sets the search controls which will be used for search operations by the template.
	 *
	 * @param searchControls the SearchControls instance which will be cached in the
	 * template.
	 */
	public void setSearchControls(SearchControls searchControls) {
		this.searchControls = searchControls;
	}
}