Java tutorial
/* * Copyright 2012-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.boot.actuate.autoconfigure; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; import org.springframework.boot.actuate.properties.ManagementServerProperties; import org.springframework.boot.actuate.properties.SecurityProperties; import org.springframework.boot.actuate.properties.SecurityProperties.User; import org.springframework.boot.actuate.web.ErrorController; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.security.authentication.AuthenticationEventPublisher; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.DefaultAuthenticationEventPublisher; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity.IgnoredRequestConfigurer; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; /** * {@link EnableAutoConfiguration Auto-configuration} for security of a web application or * service. By default everything is secured with HTTP Basic authentication except the * {@link SecurityProperties#getIgnored() explicitly ignored} paths (defaults to * <code>/css/**, /js/**, /images/**, /**/favicon.ico</code> * ). Many aspects of the behavior can be controller with {@link SecurityProperties} via * externalized application properties (or via an bean definition of that type to set the * defaults). The user details for authentication are just placeholders * <code>(username=user, * password=password)</code> but can easily be customized by providing a bean definition * of type {@link AuthenticationManager}. Also provides audit logging of authentication * events. * * <p> * The framework {@link Endpoint}s (used to expose application information to operations) * include a {@link Endpoint#isSensitive() sensitive} configuration option which will be * used as a security hint by the filter created here. * * <p> * Some common simple customizations: * <ul> * <li>Switch off security completely and permanently: remove Spring Security from the * classpath or {@link EnableAutoConfiguration#exclude() exclude} this configuration.</li> * <li>Switch off security temporarily (e.g. for a dev environment): set * <code>security.basic.enabled: false</code></li> * <li>Customize the user details: add an AuthenticationManager bean</li> * <li>Add form login for user facing resources: add a * {@link WebSecurityConfigurerAdapter} and use {@link HttpSecurity#formLogin()}</li> * </ul> * * @author Dave Syer */ @Configuration @ConditionalOnClass({ EnableWebSecurity.class }) @EnableWebSecurity // (debug = true) @EnableConfigurationProperties public class SecurityAutoConfiguration { @Bean(name = "org.springframework.actuate.properties.SecurityProperties") @ConditionalOnMissingBean public SecurityProperties securityProperties() { return new SecurityProperties(); } @Bean @ConditionalOnMissingBean public AuthenticationEventPublisher authenticationEventPublisher() { return new DefaultAuthenticationEventPublisher(); } @Bean @ConditionalOnMissingBean({ ApplicationWebSecurityConfigurerAdapter.class }) public WebSecurityConfigurerAdapter applicationWebSecurityConfigurerAdapter() { return new ApplicationWebSecurityConfigurerAdapter(); } @Bean @ConditionalOnMissingBean({ ManagementWebSecurityConfigurerAdapter.class }) public WebSecurityConfigurerAdapter managementWebSecurityConfigurerAdapter() { return new ManagementWebSecurityConfigurerAdapter(); } // Give user-supplied filters a chance to be last in line @Order(Ordered.LOWEST_PRECEDENCE - 5) private static class ApplicationWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { @Autowired private SecurityProperties security; @Autowired private AuthenticationEventPublisher authenticationEventPublisher; @Autowired(required = false) private ErrorController errorController; @Override protected void configure(HttpSecurity http) throws Exception { if (this.security.isRequireSsl()) { http.requiresChannel().anyRequest().requiresSecure(); } String[] paths = getSecureApplicationPaths(); if (this.security.getBasic().isEnabled() && paths.length > 0) { http.exceptionHandling().authenticationEntryPoint(entryPoint()); http.requestMatchers().antMatchers(paths); http.authorizeRequests().anyRequest().hasRole(this.security.getUser().getRole()) // .and().httpBasic() // .and().anonymous().disable(); } // Remove this when session creation is disabled by default http.csrf().disable(); // No cookies for application endpoints by default http.sessionManagement().sessionCreationPolicy(this.security.getSessions()); } private String[] getSecureApplicationPaths() { List<String> list = new ArrayList<String>(); for (String path : this.security.getBasic().getPath()) { path = (path == null ? "" : path.trim()); if (path.equals("/**")) { return new String[] { path }; } if (!path.equals("")) { list.add(path); } } return list.toArray(new String[list.size()]); } private AuthenticationEntryPoint entryPoint() { BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint(); entryPoint.setRealmName(this.security.getBasic().getRealm()); return entryPoint; } @Override public void configure(WebSecurity builder) throws Exception { IgnoredRequestConfigurer ignoring = builder.ignoring(); ignoring.antMatchers(this.security.getIgnored()); if (this.errorController != null) { ignoring.antMatchers(this.errorController.getErrorPath()); } } @Override protected AuthenticationManager authenticationManager() throws Exception { AuthenticationManager manager = super.authenticationManager(); if (manager instanceof ProviderManager) { ((ProviderManager) manager).setAuthenticationEventPublisher(this.authenticationEventPublisher); } return manager; } } // Give user-supplied filters a chance to be last in line @Order(Ordered.LOWEST_PRECEDENCE - 10) private static class ManagementWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { private static final String[] NO_PATHS = new String[0]; @Autowired private SecurityProperties security; @Autowired private ManagementServerProperties management; @Autowired(required = false) private EndpointHandlerMapping endpointHandlerMapping; @Override protected void configure(HttpSecurity http) throws Exception { if (this.security.isRequireSsl()) { http.requiresChannel().anyRequest().requiresSecure(); } String[] paths = getEndpointPaths(true); if (this.security.getBasic().isEnabled() && paths.length > 0) { http.exceptionHandling().authenticationEntryPoint(entryPoint()); http.requestMatchers().antMatchers(paths); http.authorizeRequests().anyRequest().hasRole(this.security.getManagement().getRole()) // .and().httpBasic() // .and().anonymous().disable(); } // No cookies for management endpoints by default http.csrf().disable(); http.sessionManagement().sessionCreationPolicy(this.security.getManagement().getSessions()); } @Override public void configure(WebSecurity builder) throws Exception { IgnoredRequestConfigurer ignoring = builder.ignoring(); ignoring.antMatchers(getEndpointPaths(false)); } private AuthenticationEntryPoint entryPoint() { BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint(); entryPoint.setRealmName(this.security.getBasic().getRealm()); return entryPoint; } private String[] getEndpointPaths(boolean secure) { if (this.endpointHandlerMapping == null) { return NO_PATHS; } List<Endpoint<?>> endpoints = this.endpointHandlerMapping.getEndpoints(); List<String> paths = new ArrayList<String>(endpoints.size()); for (Endpoint<?> endpoint : endpoints) { if (endpoint.isSensitive() == secure) { paths.add(endpoint.getPath()); } } return paths.toArray(new String[paths.size()]); } } @ConditionalOnMissingBean(AuthenticationManager.class) @Configuration public static class AuthenticationManagerConfiguration { private static Log logger = LogFactory.getLog(AuthenticationManagerConfiguration.class); @Autowired private SecurityProperties security; @Bean public AuthenticationManager authenticationManager() throws Exception { InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> builder = new AuthenticationManagerBuilder( ObjectPostProcessor.QUIESCENT_POSTPROCESSOR).inMemoryAuthentication(); User user = this.security.getUser(); if (user.isDefaultPassword()) { logger.info("Using default password for application endpoints: " + user.getPassword()); } Set<String> roles = new LinkedHashSet<String>( Arrays.asList(this.security.getManagement().getRole(), user.getRole())); builder.withUser(user.getName()).password(user.getPassword()) .roles(roles.toArray(new String[roles.size()])); return builder.and().build(); } } }