org.springframework.boot.actuate.autoconfigure.SecurityAutoConfiguration.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.boot.actuate.autoconfigure.SecurityAutoConfiguration.java

Source

/*
 * 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>&#47;css&#47;**, &#47;js&#47;**, &#47;images&#47;**, &#47;**&#47;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();

        }

    }

}