Browse Source

Allow @EnableGlobalMethodSecurity in a non webapp

Fixes gh-202
pull/208/head
Dave Syer 12 years ago
parent
commit
b1db714c23
  1. 20
      docs/howto.md
  2. 5
      spring-boot-actuator/pom.xml
  3. 5
      spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java
  4. 6
      spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfigurationTests.java
  5. 4
      spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/SpringApplicationHierarchyTests.java
  6. 4
      spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/AuthenticationManagerConfiguration.java
  7. 238
      spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfiguration.java
  8. 259
      spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SpringBootWebSecurityConfiguration.java
  9. 4
      spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java
  10. 1
      spring-boot-samples/pom.xml
  11. 9
      spring-boot-samples/spring-boot-sample-secure/pom.xml
  12. 1
      spring-boot-samples/spring-boot-sample-secure/resources/test.properties
  13. 53
      spring-boot-samples/spring-boot-sample-secure/src/main/java/sample/secure/SampleSecureApplication.java
  14. 45
      spring-boot-samples/spring-boot-sample-secure/src/main/java/sample/secure/SampleService.java
  15. 99
      spring-boot-samples/spring-boot-sample-secure/src/test/java/sample/secure/SampleSecureApplicationTests.java
  16. 40
      spring-boot-samples/spring-boot-sample-web-secure/pom.xml
  17. 0
      spring-boot-samples/spring-boot-sample-web-secure/src/main/java/sample/ops/ui/SampleSecureApplication.java
  18. 2
      spring-boot-samples/spring-boot-sample-web-secure/src/main/resources/application.properties
  19. 0
      spring-boot-samples/spring-boot-sample-web-secure/src/main/resources/logback.xml
  20. 0
      spring-boot-samples/spring-boot-sample-web-secure/src/main/resources/static/css/bootstrap.min.css
  21. 0
      spring-boot-samples/spring-boot-sample-web-secure/src/main/resources/templates/error.html
  22. 0
      spring-boot-samples/spring-boot-sample-web-secure/src/main/resources/templates/home.html
  23. 0
      spring-boot-samples/spring-boot-sample-web-secure/src/main/resources/templates/login.html
  24. 0
      spring-boot-samples/spring-boot-sample-web-secure/src/test/java/sample/ops/ui/SampleSecureApplicationTests.java

20
docs/howto.md

@ -476,6 +476,26 @@ default configuration you should find a `BeanNameViewResolver` in your @@ -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

5
spring-boot-actuator/pom.xml

@ -102,6 +102,11 @@ @@ -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>

5
spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java

@ -39,6 +39,7 @@ import org.springframework.boot.autoconfigure.security.AuthenticationManagerConf @@ -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 { @@ -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 { @@ -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());
}

6
spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfigurationTests.java

@ -128,11 +128,11 @@ public class ManagementSecurityAutoConfigurationTests { @@ -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();

4
spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/SpringApplicationHierarchyTests.java

@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure; @@ -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 { @@ -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
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/AuthenticationManagerConfiguration.java

@ -23,6 +23,8 @@ import java.util.Set; @@ -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 @@ -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

238
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfiguration.java

@ -16,254 +16,24 @@ @@ -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>&#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>
* 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
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SpringBootWebSecurityConfiguration.java

@ -0,0 +1,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>&#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>
* 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;
}
}

4
spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java

@ -106,12 +106,12 @@ public class SecurityAutoConfigurationTests { @@ -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));
}

1
spring-boot-samples/pom.xml

@ -32,6 +32,7 @@ @@ -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>

9
spring-boot-samples/spring-boot-sample-secure/pom.xml

@ -19,15 +19,6 @@ @@ -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>

1
spring-boot-samples/spring-boot-sample-secure/resources/test.properties

@ -0,0 +1 @@ @@ -0,0 +1 @@
security.user.password=password

53
spring-boot-samples/spring-boot-sample-secure/src/main/java/sample/secure/SampleSecureApplication.java

@ -0,0 +1,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
spring-boot-samples/spring-boot-sample-secure/src/main/java/sample/secure/SampleService.java

@ -0,0 +1,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
spring-boot-samples/spring-boot-sample-secure/src/test/java/sample/secure/SampleSecureApplicationTests.java

@ -0,0 +1,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"));
}
}
}

40
spring-boot-samples/spring-boot-sample-web-secure/pom.xml

@ -0,0 +1,40 @@ @@ -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>

0
spring-boot-samples/spring-boot-sample-secure/src/main/java/sample/ops/ui/SampleSecureApplication.java → spring-boot-samples/spring-boot-sample-web-secure/src/main/java/sample/ops/ui/SampleSecureApplication.java

2
spring-boot-samples/spring-boot-sample-web-secure/src/main/resources/application.properties

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
spring.thymeleaf.cache: false
debug: true

0
spring-boot-samples/spring-boot-sample-secure/src/main/resources/logback.xml → spring-boot-samples/spring-boot-sample-web-secure/src/main/resources/logback.xml

0
spring-boot-samples/spring-boot-sample-secure/src/main/resources/static/css/bootstrap.min.css → spring-boot-samples/spring-boot-sample-web-secure/src/main/resources/static/css/bootstrap.min.css vendored

0
spring-boot-samples/spring-boot-sample-secure/src/main/resources/templates/error.html → spring-boot-samples/spring-boot-sample-web-secure/src/main/resources/templates/error.html

0
spring-boot-samples/spring-boot-sample-secure/src/main/resources/templates/home.html → spring-boot-samples/spring-boot-sample-web-secure/src/main/resources/templates/home.html

0
spring-boot-samples/spring-boot-sample-secure/src/main/resources/templates/login.html → spring-boot-samples/spring-boot-sample-web-secure/src/main/resources/templates/login.html

0
spring-boot-samples/spring-boot-sample-secure/src/test/java/sample/ops/ui/SampleSecureApplicationTests.java → spring-boot-samples/spring-boot-sample-web-secure/src/test/java/sample/ops/ui/SampleSecureApplicationTests.java

Loading…
Cancel
Save