/*
* Copyright 2002-2016 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
*
* http://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 java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.http.HttpServletRequest;
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.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.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer;
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;
/**
* 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.
*
* <h2>Example Usage</h2>
*
* 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}.
*
* <pre>
* @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");
* }
* }
* </pre>
*
* @author Rob Winch
* @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<Filter>();
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.
*
* <h2>Example Configurations</h2>
*
* A basic example accepting the defaults and not using attribute exchange:
*
* <pre>
* @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");
* }
* }
* </pre>
*
* A more advanced example demonstrating using attribute exchange and providing a
* custom AuthenticationUserDetailsService that will make any user that authenticates
* a valid user.
*
* <pre>
* @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("http://axschema.org/contact/email").required(true).and()
* .attribute("firstname").type("http://axschema.org/namePerson/first")
* .required(true).and().attribute("lastname")
* .type("http://axschema.org/namePerson/last").required(true).and().and()
* .attributeExchange(".*yahoo.com.*").attribute("email")
* .type("http://schema.openid.net/contact/email").required(true).and()
* .attribute("fullname").type("http://axschema.org/namePerson")
* .required(true).and().and().attributeExchange(".*myopenid.com.*")
* .attribute("email").type("http://schema.openid.net/contact/email")
* .required(true).and().attribute("fullname")
* .type("http://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"));
* }
* }
* </pre>
*
* @return the {@link OpenIDLoginConfigurer} for further customizations.
*
* @throws Exception
* @see OpenIDLoginConfigurer
*/
public OpenIDLoginConfigurer<HttpSecurity> openidLogin() throws Exception {
return getOrApply(new OpenIDLoginConfigurer<HttpSecurity>());
}
/**
* 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:
*
* <pre>
* @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()
* ...;
* }
* }
* </pre>
*
* You can disable the headers using the following:
*
* <pre>
* @Configuration
* @EnableWebSecurity
* public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
*
* @Override
* protected void configure(HttpSecurity http) throws Exception {
* http
* .headers().disable()
* ...;
* }
* }
* </pre>
*
* 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.
*
* <pre>
* @Configuration
* @EnableWebSecurity
* public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
*
* @Override
* protected void configure(HttpSecurity http) throws Exception {
* http
* .headers()
* .defaultsDisabled()
* .cacheControl()
* .and()
* .frameOptions()
* .and()
* ...;
* }
* }
* </pre>
*
* 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()}.
*
* <pre>
* @Configuration
* @EnableWebSecurity
* public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
*
* @Override
* protected void configure(HttpSecurity http) throws Exception {
* http
* .headers()
* .frameOptions()
* .disable()
* .and()
* ...;
* }
* }
* </pre>
*
* @return
* @throws Exception
* @see HeadersConfigurer
*/
public HeadersConfigurer<HttpSecurity> headers() throws Exception {
return getOrApply(new HeadersConfigurer<HttpSecurity>());
}
/**
* 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<HttpSecurity>());
}
/**
* Allows configuring of Session Management.
*
* <h2>Example Configuration</h2>
*
* 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.
*
* <pre>
* @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");
* }
* }
* </pre>
*
* 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:
*
* <pre>
* <listener>
* <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
* </listener>
* </pre>
*
* 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<HttpSecurity>());
}
/**
* 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.
*
* <h2>Example Configuration</h2>
*
* 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.
*
* <pre>
* @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");
* }
* }
* </pre>
*
* @return the {@link PortMapperConfigurer} for further customizations
* @throws Exception
* @see #requiresChannel()
*/
public PortMapperConfigurer<HttpSecurity> portMapper() throws Exception {
return getOrApply(new PortMapperConfigurer<HttpSecurity>());
}
/**
* Configures container based pre authentication. In this case, authentication
* is managed by the Servlet Container.
*
* <h2>Example Configuration</h2>
*
* 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}.
*
* <pre>
* @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");
* }
* }
* </pre>
*
* 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:
*
* <pre>
* <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>
* </pre>
*
* 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<HttpSecurity>());
}
/**
* Configures X509 based pre authentication.
*
* <h2>Example Configuration</h2>
*
* 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.
*
* <pre>
* @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();
* }
* }
* </pre>
*
* @return the {@link X509Configurer} for further customizations
* @throws Exception
*/
public X509Configurer<HttpSecurity> x509() throws Exception {
return getOrApply(new X509Configurer<HttpSecurity>());
}
/**
* Allows configuring of Remember Me authentication.
*
* <h2>Example Configuration</h2>
*
* 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.
*
* <pre>
* @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();
* }
* }
* </pre>
*
* @return the {@link RememberMeConfigurer} for further customizations
* @throws Exception
*/
public RememberMeConfigurer<HttpSecurity> rememberMe() throws Exception {
return getOrApply(new RememberMeConfigurer<HttpSecurity>());
}
/**
* Allows restricting access based upon the {@link HttpServletRequest} using
*
* <h2>Example Configurations</h2>
*
* 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".
*
* <pre>
* @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("adminr").password("password").roles("ADMIN", "USER");
* }
* }
* </pre>
*
* 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.
*
* <pre>
* @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("adminr").password("password").roles("ADMIN", "USER");
* }
* }
* </pre>
*
* 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:
*
* <pre>
* http.authorizeRequests().antMatchers("/**").hasRole("USER").antMatchers("/admin/**")
* .hasRole("ADMIN")
* </pre>
*
* @see #requestMatcher(RequestMatcher)
*
* @return
* @throws Exception
*/
public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests()
throws Exception {
ApplicationContext context = getContext();
return getOrApply(new ExpressionUrlAuthorizationConfigurer<HttpSecurity>(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<HttpSecurity>());
}
/**
* 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<HttpSecurity>());
}
/**
* 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<HttpSecurity>());
}
/**
* 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<HttpSecurity>());
}
/**
* Adds CSRF support. This is activated by default when using
* {@link WebSecurityConfigurerAdapter}'s default constructor. You can disable it
* using:
*
* <pre>
* @Configuration
* @EnableWebSecurity
* public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
*
* @Override
* protected void configure(HttpSecurity http) throws Exception {
* http
* .csrf().disable()
* ...;
* }
* }
* </pre>
*
* @return the {@link ServletApiConfigurer} for further customizations
* @throws Exception
*/
public CsrfConfigurer<HttpSecurity> csrf() throws Exception {
ApplicationContext context = getContext();
return getOrApply(new CsrfConfigurer<HttpSecurity>(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".
*
* <h2>Example Custom Configuration</h2>
*
* 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".
*
* <pre>
* @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");
* }
* }
* </pre>
*
* @return
* @throws Exception
*/
public LogoutConfigurer<HttpSecurity> logout() throws Exception {
return getOrApply(new LogoutConfigurer<HttpSecurity>());
}
/**
* 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".
*
* <h2>Example Configuration</h2>
*
* The following configuration demonstrates how to specify that anonymous users should
* contain the role "ROLE_ANON" instead.
*
* <pre>
* @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");
* }
* }
* </pre>
*
* 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.
*
* <pre>
* @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");
* }
* }
* </pre>
*
* @return
* @throws Exception
*/
public AnonymousConfigurer<HttpSecurity> anonymous() throws Exception {
return getOrApply(new AnonymousConfigurer<HttpSecurity>());
}
/**
* Specifies to support form based authentication. If
* {@link FormLoginConfigurer#loginPage(String)} is not specified a default login page
* will be generated.
*
* <h2>Example Configurations</h2>
*
* 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)}
*
* <pre>
* @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");
* }
* }
* </pre>
*
* The configuration below demonstrates customizing the defaults.
*
* <pre>
* @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");
* }
* }
* </pre>
*
* @see FormLoginConfigurer#loginPage(String)
*
* @return
* @throws Exception
*/
public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
return getOrApply(new FormLoginConfigurer<HttpSecurity>());
}
/**
* Configures authentication against an external <i>OAuth 2.0</i> or <i>OpenID Connect 1.0</i> Provider.
* <br>
* <br>
*
* The <i>"authentication flow"</i> is realized using the <b>Authorization Code Grant</b>,
* as specified in the <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">OAuth 2.0 Authorization Framework</a>.
* <br>
* <br>
*
* As a prerequisite to using this feature, the developer must register a <i>Client</i> with an <i>Authorization Server</i>.
* The output of the <i>Client Registration</i> process results in a number of properties that are then used for configuring
* an instance of a {@link org.springframework.security.oauth2.client.registration.ClientRegistration}.
* Properties specific to a <i>Client</i> include: <i>client_id</i>, <i>client_secret</i>, <i>scope</i>, <i>redirect_uri</i>, etc.
* There are also properties specific to the <i>Provider</i>, for example,
* <i>Authorization Endpoint URI</i>, <i>Token Endpoint URI</i>, <i>UserInfo Endpoint URI</i>, etc.
* <br>
* <br>
*
* Multiple client support is provided for use cases where the application provides the user the option
* for <i>"Logging in"</i> against one or more Providers, for example, <i>Google</i>, <i>GitHub</i>, <i>Facebook</i>, etc.
* <br>
* <br>
*
* {@link org.springframework.security.oauth2.client.registration.ClientRegistration}(s) are composed within a
* {@link org.springframework.security.oauth2.client.registration.ClientRegistrationRepository}.
* An instance of {@link org.springframework.security.oauth2.client.registration.ClientRegistrationRepository} is <b>required</b>
* and may be supplied via the {@link ApplicationContext} or configured using
* {@link OAuth2LoginConfigurer#clients(org.springframework.security.oauth2.client.registration.ClientRegistrationRepository)}.
* <br>
* <br>
*
* The default configuration provides an auto-generated login page at <code>"/login"</code> and
* redirects to <code>"/login?error"</code> when an authentication error occurs.
* The login page will display each of the clients (composed within the
* {@link org.springframework.security.oauth2.client.registration.ClientRegistrationRepository})
* with an anchor link to <code>"/oauth2/authorization/code/{clientAlias}"</code>.
* Clicking through the link will initiate the <i>"Authorization Request"</i> flow
* redirecting the end-user's user-agent to the <i>Authorization Endpoint</i> of the <i>Provider</i>.
* Assuming the <i>Resource Owner</i> (end-user) grants the <i>Client</i> access, the <i>Authorization Server</i>
* will redirect the end-user's user-agent to the <i>Redirection Endpoint</i> containing the <i>Authorization Code</i>
* - the <i>Redirection Endpoint</i> is automatically configured for the application and
* defaults to <code>"/oauth2/authorize/code/{clientAlias}"</code>.
*
* <p>
* At this point in the <i>"authentication flow"</i>, the configured
* {@link org.springframework.security.oauth2.client.authentication.AuthorizationGrantTokenExchanger}
* will exchange the <i>Authorization Code</i> for an <i>Access Token</i> and then use it to access the protected resource
* at the <i>UserInfo Endpoint</i> (via {@link org.springframework.security.oauth2.client.user.OAuth2UserService})
* in order to retrieve the details of the <i>Resource Owner</i> (end-user) and establish the <i>"authenticated"</i> session.
*
* <h2>Example Configurations</h2>
*
* The minimal configuration defaults to automatically generating a login page at <code>"/login"</code>
* and redirecting to <code>"/login?error"</code> when an authentication error occurs or redirecting to
* <code>"/"</code> when an authenticated session is established.
*
* <pre>
* @EnableWebSecurity
* public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
*
* @Override
* protected void configure(HttpSecurity http) throws Exception {
* http
* .authorizeRequests()
* .anyRequest().authenticated()
* .and()
* .oauth2Login();
* }
*
* @Bean
* public ClientRegistrationRepository clientRegistrationRepository() {
* // ClientRegistrationRepositoryImpl must be composed of at least one ClientRegistration instance
* return new ClientRegistrationRepositoryImpl();
* }
* }
* </pre>
*
* The following shows the configuration options available for customizing the defaults.
*
* <pre>
* @EnableWebSecurity
* public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
*
* @Override
* protected void configure(HttpSecurity http) throws Exception {
* http
* .authorizeRequests()
* .anyRequest().authenticated()
* .and()
* .oauth2Login()
* .clients(this.clientRegistrationRepository())
* .authorizationRequestBuilder(this.authorizationRequestBuilder())
* .authorizationCodeTokenExchanger(this.authorizationCodeTokenExchanger())
* .userInfoEndpoint()
* .userInfoService(this.userInfoService())
* .userInfoEndpoint()
* // Provide a mapping between a Converter implementation and a UserInfo Endpoint URI
* .userInfoTypeConverter(this.userInfoConverter(),
* new URI("https://www.googleapis.com/oauth2/v3/userinfo"));
* }
*
* @Bean
* public ClientRegistrationRepository clientRegistrationRepository() {
* // ClientRegistrationRepositoryImpl must be composed of at least one ClientRegistration instance
* return new ClientRegistrationRepositoryImpl();
* }
*
* @Bean
* public AuthorizationRequestUriBuilder authorizationRequestBuilder() {
* // Custom URI builder for the "Authorization Request"
* return new AuthorizationRequestUriBuilderImpl();
* }
*
* @Bean
* public AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger() {
* // Custom implementation that exchanges an "Authorization Code Grant" for an "Access Token"
* return new AuthorizationCodeTokenExchangerImpl();
* }
*
* @Bean
* public OAuth2UserService userInfoService() {
* // Custom implementation that retrieves the details of the authenticated user at the "UserInfo Endpoint"
* return new OAuth2UserServiceImpl();
* }
*
* @Bean
* public Converter<ClientHttpResponse, UserInfo> userInfoConverter() {
* // Default converter implementation for UserInfo
* return new org.springframework.security.oauth2.client.user.converter.UserInfoConverter();
* }
* }
* </pre>
*
* @author Joe Grandja
* @since 5.0
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">Section 4.1 Authorization Code Grant Flow</a>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.1">Section 4.1.1 Authorization Request</a>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.2">Section 4.1.2 Authorization Response</a>
* @see org.springframework.security.oauth2.client.registration.ClientRegistration
* @see org.springframework.security.oauth2.client.registration.ClientRegistrationRepository
* @see org.springframework.security.oauth2.client.authentication.AuthorizationRequestUriBuilder
* @see org.springframework.security.oauth2.client.authentication.AuthorizationGrantTokenExchanger
* @see org.springframework.security.oauth2.client.user.OAuth2UserService
*
* @return the {@link OAuth2LoginConfigurer} for further customizations
* @throws Exception
*/
public OAuth2LoginConfigurer<HttpSecurity> oauth2Login() throws Exception {
return getOrApply(new OAuth2LoginConfigurer<HttpSecurity>());
}
/**
* Configures channel security. In order for this configuration to be useful at least
* one mapping to a required channel must be provided.
*
* <h2>Example Configuration</h2>
*
* 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 <a
* href="http://en.wikipedia.org/wiki/Firesheep">Firesheep</a>.
*
* <pre>
* @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");
* }
* }
* </pre>
*
*
* @return the {@link ChannelSecurityConfigurer} for further customizations
* @throws Exception
*/
public ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry requiresChannel()
throws Exception {
ApplicationContext context = getContext();
return getOrApply(new ChannelSecurityConfigurer<HttpSecurity>(context))
.getRegistry();
}
/**
* Configures HTTP Basic authentication.
*
* <h2>Example Configuration</h2>
*
* The example below demonstrates how to configure HTTP Basic authentication for an
* application. The default realm is "Spring Security Application", but can be
* customized using {@link HttpBasicConfigurer#realmName(String)}.
*
* <pre>
* @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");
* }
* }
* </pre>
*
* @return the {@link HttpBasicConfigurer} for further customizations
* @throws Exception
*/
public HttpBasicConfigurer<HttpSecurity> httpBasic() throws Exception {
return getOrApply(new HttpBasicConfigurer<HttpSecurity>());
}
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.HttpBuilder#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.HttpBuilder#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.HttpBuilder#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.HttpBuilder#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.HttpBuilder#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:
*
* <pre>
* addFilterAt(new CustomFilter(), UsernamePasswordAuthenticationFilter.class)
* </pre>
*
* @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)}.
*
* <p>
* Invoking {@link #requestMatchers()} will not override previous invocations of {@link #mvcMatcher(String)}},
* {@link #requestMatchers()}, {@link #antMatcher(String)},
* {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
* </p>
*
* <h3>Example Configurations</h3>
*
* The following configuration enables the {@link HttpSecurity} for URLs that begin
* with "/api/" or "/oauth/".
*
* <pre>
* @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");
* }
* }
* </pre>
*
* The configuration below is the same as the previous configuration.
*
* <pre>
* @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");
* }
* }
* </pre>
*
* The configuration below is also the same as the above configuration.
*
* <pre>
* @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");
* }
* }
* </pre>
*
* @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()}.
*
* <p>
* Invoking {@link #requestMatcher(RequestMatcher)} will override previous invocations
* of {@link #requestMatchers()}, {@link #mvcMatcher(String)}, {@link #antMatcher(String)},
* {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
* </p>
*
* @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)}.
*
* <p>
* Invoking {@link #antMatcher(String)} will override previous invocations of {@link #mvcMatcher(String)}},
* {@link #requestMatchers()}, {@link #antMatcher(String)},
* {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
* </p>
*
* @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)}.
*
* <p>
* Invoking {@link #mvcMatcher(String)} will override previous invocations of {@link #mvcMatcher(String)}},
* {@link #requestMatchers()}, {@link #antMatcher(String)},
* {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
* </p>
*
* @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)}.
*
* <p>
* Invoking {@link #regexMatcher(String)} will override previous invocations of {@link #mvcMatcher(String)}},
* {@link #requestMatchers()}, {@link #antMatcher(String)},
* {@link #regexMatcher(String)}, and {@link #requestMatcher(RequestMatcher)}.
* </p>
*
* @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<RequestMatcher>(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<RequestMatcher>();
/**
* @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);
}
}