mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-05-04 05:01:06 +01:00
Allow @EnableGlobalMethodSecurity in a non webapp
Fixes gh-202
This commit is contained in:
@@ -476,6 +476,26 @@ default configuration you should find a `BeanNameViewResolver` in your
|
||||
way of doing that. Look at `ErrorMvcAutoConfiguration` for more
|
||||
options.
|
||||
|
||||
## Secure an Application
|
||||
|
||||
Web applications will be secure by default (with Basic authentication
|
||||
on all endpoints) if Spring Security is on the classpath. To add
|
||||
method-level security to a web application you can simply
|
||||
`@EnableGlobalMethodSecurity` with your desired settings.
|
||||
|
||||
The default `AuthenticationManager` has a single user (username "user"
|
||||
and password random, printed at INFO when the application starts
|
||||
up). You can change the password by providing a
|
||||
`security.user.password`. This and other useful properties are
|
||||
externalized via `SecurityProperties`.
|
||||
|
||||
## Change the AuthenticationManager and add User Accounts
|
||||
|
||||
If you provide a `@Bean` of type `AuthenticationManager` the default
|
||||
one will not be created, so you have the full feature set of Spring
|
||||
Security available
|
||||
(e.g. [various authentication options](http://docs.spring.io/spring-security/site/docs/3.2.1.CI-SNAPSHOT/reference/htmlsingle/#jc-authentication)).
|
||||
|
||||
## Use 'Short' Command Line Arguments
|
||||
|
||||
Some people like to use (for example) `--port=9000` instead of
|
||||
|
||||
@@ -102,6 +102,11 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
|
||||
+3
-2
@@ -39,6 +39,7 @@ import org.springframework.boot.autoconfigure.security.AuthenticationManagerConf
|
||||
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityPrequisite;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||
import org.springframework.boot.autoconfigure.security.SpringBootWebSecurityConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -127,7 +128,7 @@ public class ManagementSecurityAutoConfiguration {
|
||||
IgnoredRequestConfigurer ignoring = builder.ignoring();
|
||||
// The ignores are not cumulative, so to prevent overwriting the defaults we
|
||||
// add them back.
|
||||
List<String> ignored = SecurityAutoConfiguration.getIgnored(this.security);
|
||||
List<String> ignored = SpringBootWebSecurityConfiguration.getIgnored(this.security);
|
||||
ignored.addAll(Arrays.asList(getEndpointPaths(this.endpointHandlerMapping,
|
||||
false)));
|
||||
if (!this.management.getSecurity().isEnabled()) {
|
||||
@@ -185,7 +186,7 @@ public class ManagementSecurityAutoConfiguration {
|
||||
http.sessionManagement().sessionCreationPolicy(
|
||||
this.management.getSecurity().getSessions());
|
||||
|
||||
SecurityAutoConfiguration.configureHeaders(http.headers(),
|
||||
SpringBootWebSecurityConfiguration.configureHeaders(http.headers(),
|
||||
this.security.getHeaders());
|
||||
|
||||
}
|
||||
|
||||
+3
-3
@@ -128,11 +128,11 @@ public class ManagementSecurityAutoConfigurationTests {
|
||||
public void testDisableBasicAuthOnApplicationPaths() throws Exception {
|
||||
this.context = new AnnotationConfigWebApplicationContext();
|
||||
this.context.setServletContext(new MockServletContext());
|
||||
this.context.register(SecurityAutoConfiguration.class,
|
||||
ManagementSecurityAutoConfiguration.class,
|
||||
HttpMessageConvertersAutoConfiguration.class,
|
||||
this.context.register(HttpMessageConvertersAutoConfiguration.class,
|
||||
EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class,
|
||||
ManagementServerPropertiesAutoConfiguration.class,
|
||||
SecurityAutoConfiguration.class,
|
||||
ManagementSecurityAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
EnvironmentTestUtils.addEnvironment(this.context, "security.basic.enabled:false");
|
||||
this.context.refresh();
|
||||
|
||||
+3
-1
@@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -61,7 +62,8 @@ public class SpringApplicationHierarchyTests {
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration(exclude = { ServerPropertiesAutoConfiguration.class,
|
||||
JolokiaAutoConfiguration.class, EndpointMBeanExportAutoConfiguration.class })
|
||||
JolokiaAutoConfiguration.class, EndpointMBeanExportAutoConfiguration.class,
|
||||
SecurityAutoConfiguration.class })
|
||||
public static class Parent {
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -23,6 +23,8 @@ 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.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties.User;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -32,6 +34,8 @@ import org.springframework.security.config.annotation.authentication.builders.Au
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnBean(ObjectPostProcessor.class)
|
||||
@ConditionalOnMissingBean(AuthenticationManager.class)
|
||||
public class AuthenticationManagerConfiguration {
|
||||
|
||||
private static Log logger = LogFactory
|
||||
|
||||
+4
-234
@@ -16,254 +16,24 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.security;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties.Headers;
|
||||
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.context.annotation.Import;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
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.WebSecurityConfiguration;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
||||
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.header.writers.HstsHeaderWriter;
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||
import org.springframework.web.servlet.support.RequestDataValueProcessor;
|
||||
|
||||
/**
|
||||
* {@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>
|
||||
* 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(AuthenticationManager.class)
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnClass({ EnableWebSecurity.class })
|
||||
@ConditionalOnMissingBean(WebSecurityConfiguration.class)
|
||||
// @ConditionalOnMissingBean(annotation = EnableWebSecurity.class)
|
||||
@Import({ SpringBootWebSecurityConfiguration.class, AuthenticationManagerConfiguration.class })
|
||||
public class SecurityAutoConfiguration {
|
||||
|
||||
private static List<String> DEFAULT_IGNORED = Arrays.asList("/css/**", "/js/**",
|
||||
"/images/**", "/**/favicon.ico");
|
||||
|
||||
@Bean(name = "org.springframework.autoconfigure.security.SecurityProperties")
|
||||
@ConditionalOnMissingBean
|
||||
public SecurityProperties securityProperties() {
|
||||
return new SecurityProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public AuthenticationEventPublisher authenticationEventPublisher() {
|
||||
return new DefaultAuthenticationEventPublisher();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean({ IgnoredPathsWebSecurityConfigurerAdapter.class })
|
||||
// @ConditionalOnBean(annotation = EnableWebSecurity.class)
|
||||
@ConditionalOnBean(WebSecurityConfiguration.class)
|
||||
public SecurityConfigurer<Filter, WebSecurity> ignoredPathsWebSecurityConfigurerAdapter() {
|
||||
return new IgnoredPathsWebSecurityConfigurerAdapter();
|
||||
}
|
||||
|
||||
// Get the ignored paths in early
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
private static class IgnoredPathsWebSecurityConfigurerAdapter implements
|
||||
SecurityConfigurer<Filter, WebSecurity> {
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties security;
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity builder) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(WebSecurity builder) throws Exception {
|
||||
IgnoredRequestConfigurer ignoring = builder.ignoring();
|
||||
List<String> ignored = getIgnored(this.security);
|
||||
ignoring.antMatchers(ignored.toArray(new String[0]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Pull in @EnableWebMvcSecurity if Spring MVC is available and no-one defined a
|
||||
// RequestDataValueProcessor
|
||||
@ConditionalOnClass(RequestDataValueProcessor.class)
|
||||
@ConditionalOnMissingBean(RequestDataValueProcessor.class)
|
||||
@ConditionalOnExpression("${security.basic.enabled:true}")
|
||||
@Configuration
|
||||
protected static class WebMvcSecurityConfigurationConditions {
|
||||
@Configuration
|
||||
@EnableWebMvcSecurity
|
||||
protected static class DefaultWebMvcSecurityConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
// Pull in a plain @EnableWebSecurity if Spring MVC is not available
|
||||
@ConditionalOnMissingBean(WebMvcSecurityConfigurationConditions.class)
|
||||
@ConditionalOnMissingClass(name = "org.springframework.web.servlet.support.RequestDataValueProcessor")
|
||||
@ConditionalOnExpression("${security.basic.enabled:true}")
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
protected static class DefaultWebSecurityConfiguration {
|
||||
}
|
||||
|
||||
@ConditionalOnExpression("${security.basic.enabled:true}")
|
||||
@Configuration
|
||||
@Order(Ordered.LOWEST_PRECEDENCE - 5)
|
||||
protected static class ApplicationWebSecurityConfigurerAdapter extends
|
||||
WebSecurityConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties security;
|
||||
|
||||
@Autowired
|
||||
private AuthenticationEventPublisher authenticationEventPublisher;
|
||||
|
||||
@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()
|
||||
.hasAnyRole(
|
||||
this.security.getUser().getRole().toArray(new String[0])) //
|
||||
.and().httpBasic() //
|
||||
.and().anonymous().disable();
|
||||
}
|
||||
if (!this.security.isEnableCsrf()) {
|
||||
http.csrf().disable();
|
||||
}
|
||||
// No cookies for application endpoints by default
|
||||
http.sessionManagement().sessionCreationPolicy(this.security.getSessions());
|
||||
|
||||
SecurityAutoConfiguration.configureHeaders(http.headers(),
|
||||
this.security.getHeaders());
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
protected AuthenticationManager authenticationManager() throws Exception {
|
||||
AuthenticationManager manager = super.authenticationManager();
|
||||
if (manager instanceof ProviderManager) {
|
||||
((ProviderManager) manager)
|
||||
.setAuthenticationEventPublisher(this.authenticationEventPublisher);
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(AuthenticationManager.class)
|
||||
protected static class ApplicationAuthenticationManagerConfiguration extends
|
||||
AuthenticationManagerConfiguration {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void configureHeaders(HeadersConfigurer<?> configurer,
|
||||
SecurityProperties.Headers headers) throws Exception {
|
||||
if (headers.getHsts() != Headers.HSTS.none) {
|
||||
boolean includeSubdomains = headers.getHsts() == Headers.HSTS.all;
|
||||
HstsHeaderWriter writer = new HstsHeaderWriter(includeSubdomains);
|
||||
writer.setRequestMatcher(AnyRequestMatcher.INSTANCE);
|
||||
configurer.addHeaderWriter(writer);
|
||||
}
|
||||
if (headers.isContentType()) {
|
||||
configurer.contentTypeOptions();
|
||||
}
|
||||
if (headers.isXss()) {
|
||||
configurer.xssProtection();
|
||||
}
|
||||
if (headers.isCache()) {
|
||||
configurer.cacheControl();
|
||||
}
|
||||
if (headers.isFrame()) {
|
||||
configurer.frameOptions();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> getIgnored(SecurityProperties security) {
|
||||
List<String> ignored = new ArrayList<String>(security.getIgnored());
|
||||
if (ignored.isEmpty()) {
|
||||
ignored.addAll(DEFAULT_IGNORED);
|
||||
}
|
||||
else if (ignored.contains("none")) {
|
||||
ignored.remove("none");
|
||||
}
|
||||
return ignored;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* 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.autoconfigure.security;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.security.SecurityProperties.Headers;
|
||||
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.SecurityConfigurer;
|
||||
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.WebSecurityConfiguration;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
||||
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.header.writers.HstsHeaderWriter;
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||
import org.springframework.web.servlet.support.RequestDataValueProcessor;
|
||||
|
||||
/**
|
||||
* {@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>
|
||||
* 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
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnClass({ EnableWebSecurity.class })
|
||||
@ConditionalOnMissingBean(WebSecurityConfiguration.class)
|
||||
@ConditionalOnWebApplication
|
||||
// @ConditionalOnMissingBean(annotation = EnableWebSecurity.class)
|
||||
public class SpringBootWebSecurityConfiguration {
|
||||
|
||||
private static List<String> DEFAULT_IGNORED = Arrays.asList("/css/**", "/js/**",
|
||||
"/images/**", "/**/favicon.ico");
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public AuthenticationEventPublisher authenticationEventPublisher() {
|
||||
return new DefaultAuthenticationEventPublisher();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean({ IgnoredPathsWebSecurityConfigurerAdapter.class })
|
||||
// @ConditionalOnBean(annotation = EnableWebSecurity.class)
|
||||
@ConditionalOnBean(SpringBootWebSecurityConfiguration.class)
|
||||
public SecurityConfigurer<Filter, WebSecurity> ignoredPathsWebSecurityConfigurerAdapter() {
|
||||
return new IgnoredPathsWebSecurityConfigurerAdapter();
|
||||
}
|
||||
|
||||
// Get the ignored paths in early
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
private static class IgnoredPathsWebSecurityConfigurerAdapter implements
|
||||
SecurityConfigurer<Filter, WebSecurity> {
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties security;
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity builder) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(WebSecurity builder) throws Exception {
|
||||
IgnoredRequestConfigurer ignoring = builder.ignoring();
|
||||
List<String> ignored = getIgnored(this.security);
|
||||
ignoring.antMatchers(ignored.toArray(new String[0]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Pull in @EnableWebMvcSecurity if Spring MVC is available and no-one defined a
|
||||
// RequestDataValueProcessor
|
||||
@ConditionalOnClass(RequestDataValueProcessor.class)
|
||||
@ConditionalOnMissingBean(RequestDataValueProcessor.class)
|
||||
@ConditionalOnExpression("${security.basic.enabled:true}")
|
||||
@Configuration
|
||||
protected static class WebMvcSecurityConfigurationConditions {
|
||||
@Configuration
|
||||
@EnableWebMvcSecurity
|
||||
protected static class DefaultWebMvcSecurityConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
// Pull in a plain @EnableWebSecurity if Spring MVC is not available
|
||||
@ConditionalOnMissingBean(WebMvcSecurityConfigurationConditions.class)
|
||||
@ConditionalOnMissingClass(name = "org.springframework.web.servlet.support.RequestDataValueProcessor")
|
||||
@ConditionalOnExpression("${security.basic.enabled:true}")
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
protected static class DefaultWebSecurityConfiguration {
|
||||
}
|
||||
|
||||
@ConditionalOnExpression("${security.basic.enabled:true}")
|
||||
@Configuration
|
||||
@Order(Ordered.LOWEST_PRECEDENCE - 5)
|
||||
protected static class ApplicationWebSecurityConfigurerAdapter extends
|
||||
WebSecurityConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private SecurityProperties security;
|
||||
|
||||
@Autowired
|
||||
private AuthenticationEventPublisher authenticationEventPublisher;
|
||||
|
||||
@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()
|
||||
.hasAnyRole(
|
||||
this.security.getUser().getRole().toArray(new String[0])) //
|
||||
.and().httpBasic() //
|
||||
.and().anonymous().disable();
|
||||
}
|
||||
if (!this.security.isEnableCsrf()) {
|
||||
http.csrf().disable();
|
||||
}
|
||||
// No cookies for application endpoints by default
|
||||
http.sessionManagement().sessionCreationPolicy(this.security.getSessions());
|
||||
|
||||
SpringBootWebSecurityConfiguration.configureHeaders(http.headers(),
|
||||
this.security.getHeaders());
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
protected AuthenticationManager authenticationManager() throws Exception {
|
||||
AuthenticationManager manager = super.authenticationManager();
|
||||
if (manager instanceof ProviderManager) {
|
||||
((ProviderManager) manager)
|
||||
.setAuthenticationEventPublisher(this.authenticationEventPublisher);
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void configureHeaders(HeadersConfigurer<?> configurer,
|
||||
SecurityProperties.Headers headers) throws Exception {
|
||||
if (headers.getHsts() != Headers.HSTS.none) {
|
||||
boolean includeSubdomains = headers.getHsts() == Headers.HSTS.all;
|
||||
HstsHeaderWriter writer = new HstsHeaderWriter(includeSubdomains);
|
||||
writer.setRequestMatcher(AnyRequestMatcher.INSTANCE);
|
||||
configurer.addHeaderWriter(writer);
|
||||
}
|
||||
if (headers.isContentType()) {
|
||||
configurer.contentTypeOptions();
|
||||
}
|
||||
if (headers.isXss()) {
|
||||
configurer.xssProtection();
|
||||
}
|
||||
if (headers.isCache()) {
|
||||
configurer.cacheControl();
|
||||
}
|
||||
if (headers.isFrame()) {
|
||||
configurer.frameOptions();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> getIgnored(SecurityProperties security) {
|
||||
List<String> ignored = new ArrayList<String>(security.getIgnored());
|
||||
if (ignored.isEmpty()) {
|
||||
ignored.addAll(DEFAULT_IGNORED);
|
||||
}
|
||||
else if (ignored.contains("none")) {
|
||||
ignored.remove("none");
|
||||
}
|
||||
return ignored;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -106,12 +106,12 @@ public class SecurityAutoConfigurationTests {
|
||||
public void testJpaCoexistsHappily() throws Exception {
|
||||
this.context = new AnnotationConfigWebApplicationContext();
|
||||
this.context.setServletContext(new MockServletContext());
|
||||
this.context.register(EntityConfiguration.class, TestConfiguration.class,
|
||||
this.context.register(EntityConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class,
|
||||
DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
|
||||
SecurityAutoConfiguration.class);
|
||||
// This can fail if security @Conditionals force early instantiation of the
|
||||
// HibernateJpaAutoConfiguration
|
||||
// HibernateJpaAutoConfiguration (e.g. the EntityManagerFactory is not found)
|
||||
this.context.refresh();
|
||||
assertNotNull(this.context.getBean(JpaTransactionManager.class));
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<module>spring-boot-sample-simple</module>
|
||||
<module>spring-boot-sample-tomcat</module>
|
||||
<module>spring-boot-sample-traditional</module>
|
||||
<module>spring-boot-sample-web-secure</module>
|
||||
<module>spring-boot-sample-web-static</module>
|
||||
<module>spring-boot-sample-web-jsp</module>
|
||||
<module>spring-boot-sample-web-ui</module>
|
||||
|
||||
@@ -19,15 +19,6 @@
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf-spring4</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
security.user.password=password
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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 sample.secure;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan
|
||||
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
|
||||
public class SampleSecureApplication implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
private SampleService service;
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new UsernamePasswordAuthenticationToken("user", "N/A", AuthorityUtils
|
||||
.commaSeparatedStringToAuthorityList("ROLE_USER")));
|
||||
try {
|
||||
System.out.println(service.secure());
|
||||
} finally {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication.run(SampleSecureApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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 sample.secure;
|
||||
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
public class SampleService {
|
||||
|
||||
@Secured("ROLE_USER")
|
||||
public String secure() {
|
||||
return "Hello Security";
|
||||
}
|
||||
|
||||
@PreAuthorize("true")
|
||||
public String authorized() {
|
||||
return "Hello World";
|
||||
}
|
||||
|
||||
@PreAuthorize("false")
|
||||
public String denied() {
|
||||
return "Goodbye World";
|
||||
}
|
||||
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 sample.secure;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import sample.secure.SampleSecureApplicationTests.TestConfiguration;
|
||||
|
||||
/**
|
||||
* Basic integration tests for demo application.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = { SampleSecureApplication.class,
|
||||
TestConfiguration.class })
|
||||
public class SampleSecureApplicationTests {
|
||||
|
||||
@Autowired
|
||||
private SampleService service;
|
||||
|
||||
@Autowired
|
||||
private Authentication authentication;
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test(expected = AuthenticationException.class)
|
||||
public void secure() throws Exception {
|
||||
assertEquals(service.secure(), "Hello Security");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticated() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
assertEquals(service.secure(), "Hello Security");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preauth() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
assertEquals(service.authorized(), "Hello World");
|
||||
}
|
||||
|
||||
@Test(expected = AccessDeniedException.class)
|
||||
public void denied() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
assertEquals(service.denied(), "Goodbye World");
|
||||
}
|
||||
|
||||
@PropertySource("classpath:test.properties")
|
||||
@Configuration
|
||||
protected static class TestConfiguration {
|
||||
|
||||
@Autowired
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
@Bean
|
||||
public Authentication user() {
|
||||
return authenticationManager
|
||||
.authenticate(new UsernamePasswordAuthenticationToken("user",
|
||||
"password"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<!-- Your own application should inherit from spring-boot-starter-parent -->
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-samples</artifactId>
|
||||
<version>0.5.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-boot-sample-web-secure</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf-spring4</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
spring.thymeleaf.cache: false
|
||||
debug: true
|
||||
Reference in New Issue
Block a user