/*
* 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
*
* 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.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.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherConfigurer;
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.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.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.web.DefaultSecurityFilterChain;
import org.springframework.security.web.PortMapper;
import org.springframework.security.web.PortMapperImpl;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.security.web.util.AntPathRequestMatcher;
import org.springframework.security.web.util.AnyRequestMatcher;
import org.springframework.security.web.util.RegexRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.util.Assert;
/**
* 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
* .authorizeUrls()
* .antMatchers("/**").hasRole("USER")
* .and()
* .formLogin();
* }
*
* @Override
* protected void registerAuthentication(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 AuthenticationManager authenticationManager;
private List<Filter> filters = new ArrayList<Filter>();
private RequestMatcher requestMatcher = new AnyRequestMatcher();
private FilterComparator comparitor = 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
*/
public HttpSecurity(ObjectPostProcessor<Object> objectPostProcessor, AuthenticationManagerBuilder authenticationBuilder, Map<Class<Object>,Object> sharedObjects) {
super(objectPostProcessor);
Assert.notNull(authenticationBuilder, "authenticationBuilder cannot be null");
setSharedObject(AuthenticationManagerBuilder.class, authenticationBuilder);
for(Map.Entry<Class<Object>, Object> entry : sharedObjects.entrySet()) {
setSharedObject(entry.getKey(), entry.getValue());
}
}
/**
* Allows configuring OpenID based authentication. Multiple invocations of
* {@link #openidLogin()} will override previous invocations.
*
* <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
* .authorizeUrls()
* .antMatchers("/**").hasRole("USER")
* .and()
* .openidLogin()
* .permitAll();
* }
*
* @Override
* protected void registerAuthentication(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
* .authorizeUrls()
* .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 apply(new OpenIDLoginConfigurer<HttpSecurity>());
}
/**
* Allows configuring of Session Management. Multiple invocations of
* {@link #sessionManagement()} will override previous invocations.
*
* <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
* .authorizeUrls()
* .anyRequest().hasRole("USER")
* .and()
* .formLogin()
* .permitAll()
* .and()
* .sessionManagement()
* .maximumSessions(1)
* .expiredUrl("/login?expired");
* }
*
* @Override
* protected void registerAuthentication(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 apply(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
* .authorizeUrls()
* .antMatchers("/**").hasRole("USER")
* .and()
* .formLogin()
* .permitAll()
* .and()
* // Example portMapper() configuration
* .portMapper()
* .http(9090).mapsTo(9443)
* .http(80).mapsTo(443);
* }
*
* @Override
* protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
* auth
* .inMemoryAuthentication()
* .withUser("user")
* .password("password")
* .roles("USER");
* }
* }
* </pre>
*
* @return the {@link PortMapperConfigurer} for further customizations
* @throws Exception
* @see {@link #requiresChannel()}
*/
public PortMapperConfigurer<HttpSecurity> portMapper() throws Exception {
return apply(new PortMapperConfigurer<HttpSecurity>());
}
/**
* Configures container based 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
* .authorizeUrls()
* .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 apply(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
* .authorizeUrls()
* .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 apply(new X509Configurer<HttpSecurity>());
}
/**
* Allows configuring of Remember Me authentication. Multiple invocations of
* {@link #rememberMe()} will override previous invocations.
*
* <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 registerAuthentication(AuthenticationManagerBuilder auth)
* throws Exception {
* auth
* .inMemoryAuthentication()
* .withUser("user")
* .password("password")
* .roles("USER");
* }
*
* @Override
* protected void configure(HttpSecurity http) throws Exception {
* http
* .authorizeUrls()
* .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 apply(new RememberMeConfigurer<HttpSecurity>());
}
/**
* Allows restricting access based upon the {@link HttpServletRequest} using
* {@link RequestMatcher} implementations (i.e. via URL patterns). Invoking
* {@link #authorizeUrls()} twice will override previous invocations of
* {@link #authorizeUrls()}.
*
* <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
* .authorizeUrls()
* .antMatchers("/**").hasRole("USER")
* .and()
* .formLogin();
* }
*
* @Override
* protected void registerAuthentication(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
* .authorizeUrls()
* .antMatchers("/admin/**").hasRole("ADMIN")
* .antMatchers("/**").hasRole("USER")
* .and()
* .formLogin();
* }
*
* @Override
* protected void registerAuthentication(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
* .authorizeUrls()
* .antMatchers("/**").hasRole("USER")
* .antMatchers("/admin/**").hasRole("ADMIN")
* </pre>
*
* @see #requestMatcher(RequestMatcher)
*
* @return
* @throws Exception
*/
public ExpressionUrlAuthorizationConfigurer<HttpSecurity> authorizeUrls() throws Exception {
return apply(new ExpressionUrlAuthorizationConfigurer<HttpSecurity>());
}
/**
* 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 apply(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 apply(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 apply(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 apply(new ServletApiConfigurer<HttpSecurity>());
}
/**
* 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
* .authorizeUrls()
* .antMatchers("/**").hasRole("USER")
* .and()
* .formLogin()
* .and()
* // sample logout customization
* .logout()
* .logout()
* .deleteCookies("remove")
* .invalidateHttpSession(false)
* .logoutUrl("/custom-logout")
* .logoutSuccessUrl("/logout-success");
* }
*
* @Override
* protected void registerAuthentication(AuthenticationManagerBuilder auth)
* throws Exception {
* auth
* .inMemoryAuthentication()
* .withUser("user")
* .password("password")
* .roles("USER");
* }
* }
* </pre>
*
* @return
* @throws Exception
*/
public LogoutConfigurer<HttpSecurity> logout() throws Exception {
return apply(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
* .authorizeUrls()
* .antMatchers("/**").hasRole("USER")
* .and()
* .formLogin()
* .and()
* // sample anonymous customization
* .anonymous()
* .authorities("ROLE_ANON");
* }
*
* @Override
* protected void registerAuthentication(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
* .authorizeUrls()
* .antMatchers("/**").hasRole("USER")
* .and()
* .formLogin()
* .and()
* // sample anonymous customization
* .anonymous()
* .disabled();
* }
*
* @Override
* protected void registerAuthentication(AuthenticationManagerBuilder auth)
* throws Exception {
* auth
* .inMemoryAuthentication()
* .withUser("user")
* .password("password")
* .roles("USER");
* }
* }
* </pre>
*
* @return
* @throws Exception
*/
public AnonymousConfigurer<HttpSecurity> anonymous() throws Exception {
return apply(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
* .authorizeUrls()
* .antMatchers("/**").hasRole("USER")
* .and()
* .formLogin();
* }
*
* @Override
* protected void registerAuthentication(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
* .authorizeUrls()
* .antMatchers("/**").hasRole("USER")
* .and()
* .formLogin()
* .usernameParameter("j_username") // default is username
* .passwordParameter("j_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 registerAuthentication(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 apply(new FormLoginConfigurer<HttpSecurity>());
}
/**
* Configures channel security. In order for this configuration to be useful at least
* one mapping to a required channel must be provided. Invoking this method multiple times
* will reset previous invocations of the method.
*
* <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
* .authorizeUrls()
* .antMatchers("/**").hasRole("USER")
* .and()
* .formLogin()
* .and()
* .channelSecurity()
* .anyRequest().requiresSecure();
* }
*
* @Override
* protected void registerAuthentication(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> requiresChannel() throws Exception {
return apply(new ChannelSecurityConfigurer<HttpSecurity>());
}
/**
* Configures HTTP Basic authentication. Multiple infocations of
* {@link #httpBasic()} will override previous invocations.
*
* <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
* .authorizeUrls()
* .antMatchers("/**").hasRole("USER").and()
* .httpBasic();
* }
*
* @Override
* protected void registerAuthentication(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 apply(new HttpBasicConfigurer<HttpSecurity>());
}
@Override
protected void beforeConfigure() throws Exception {
this.authenticationManager = getAuthenticationRegistry().build();
}
@Override
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(filters,comparitor);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
/* (non-Javadoc)
* @see org.springframework.security.config.annotation.web.HttpBuilder#authenticationProvider(org.springframework.security.authentication.AuthenticationProvider)
*/
@Override
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)
*/
@Override
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)
*/
@Override
public HttpSecurity addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) {
comparitor.registerAfter(filter.getClass(), afterFilter);
return addFilter(filter);
}
/* (non-Javadoc)
* @see org.springframework.security.config.annotation.web.HttpBuilder#addFilterBefore(javax.servlet.Filter, java.lang.Class)
*/
@Override
public HttpSecurity addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter) {
comparitor.registerBefore(filter.getClass(), beforeFilter);
return addFilter(filter);
}
/* (non-Javadoc)
* @see org.springframework.security.config.annotation.web.HttpBuilder#addFilter(javax.servlet.Filter)
*/
@Override
public HttpSecurity addFilter(Filter filter) {
Class<? extends Filter> filterClass = filter.getClass();
if(!comparitor.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;
}
/**
* 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 #antMatcher(String)},
* {@link #regexMatcher(String)}, or {@link #requestMatcher(RequestMatcher)}.
*
* <p>
* Invoking {@link #requestMatchers()} will override previous invocations of
* {@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()
* .authorizeUrls()
* .antMatchers("/**").hasRole("USER").and()
* .httpBasic();
* }
*
* @Override
* protected void registerAuthentication(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()
* .authorizeUrls()
* .antMatchers("/**").hasRole("USER").and()
* .httpBasic();
* }
*
* @Override
* protected void registerAuthentication(AuthenticationManagerBuilder auth)
* throws Exception {
* auth
* .inMemoryAuthentication()
* .withUser("user")
* .password("password")
* .roles("USER");
* }
* }
* </pre>
*
* The configuration differs from the previous configurations because it invokes
* {@link #requestMatchers()} twice which resets the {@link RequestMatcherConfigurer}.
* Therefore the configuration below only matches on URLs that start with "/oauth/**".
*
* <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()
* .authorizeUrls()
* .antMatchers("/**").hasRole("USER").and()
* .httpBasic();
* }
*
* @Override
* protected void registerAuthentication(AuthenticationManagerBuilder auth)
* throws Exception {
* auth
* .inMemoryAuthentication()
* .withUser("user")
* .password("password")
* .roles("USER");
* }
* }
* </pre>
*
* @return the {@link RequestMatcherConfigurer} for further customizations
*/
public RequestMatcherConfigurer requestMatchers() {
return new 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 #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 #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 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 #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));
}
/*
* (non-Javadoc)
* @see org.springframework.security.config.annotation.web.HttpBuilder#getAuthenticationManager()
*/
@Override
public AuthenticationManager getAuthenticationManager() {
return authenticationManager;
}
/**
* Allows mapping HTTP requests that this {@link HttpSecurity} will be used for
*
* @author Rob Winch
* @since 3.2
*/
public final class RequestMatcherConfigurer extends AbstractRequestMatcherConfigurer<HttpSecurity,RequestMatcherConfigurer,DefaultSecurityFilterChain> {
protected RequestMatcherConfigurer chainRequestMatchers(List<RequestMatcher> requestMatchers) {
requestMatcher(new OrRequestMatcher(requestMatchers));
return this;
}
/**
* Return the {@link HttpSecurity} for further customizations
*
* @return the {@link HttpSecurity} for further customizations
*/
public HttpSecurity and() {
return HttpSecurity.this;
}
private RequestMatcherConfigurer(){}
}
/**
* Internal {@link RequestMatcher} instance used by {@link RequestMatcher}
* that will match if any of the passed in {@link RequestMatcher} instances
* match.
*
* @author Rob Winch
* @since 3.2
*/
private static final class OrRequestMatcher implements RequestMatcher {
private final List<RequestMatcher> requestMatchers;
private OrRequestMatcher(List<RequestMatcher> requestMatchers) {
this.requestMatchers = requestMatchers;
}
@Override
public boolean matches(HttpServletRequest request) {
for(RequestMatcher matcher : requestMatchers) {
if(matcher.matches(request)) {
return true;
}
}
return false;
}
}
}