Rework security autoconfiguration

This commit combines security autoconfigurations for
management endpoints and the rest of the application. By default,
if Spring Security is on the classpath, it turns on @EnableWebSecurity.
In the presence of another WebSecurityConfigurerAdapter this backs off
completely. A default AuthenticationManager is also provided with a user
and generated password. This can be turned off by specifying a bean of
type AuthenticationManager, AuthenticationProvider or UserDetailsService.

Closes gh-7958
This commit is contained in:
Madhura Bhave
2017-08-27 21:19:34 -07:00
parent f60ad0df74
commit e08ddbf838
83 changed files with 1146 additions and 2212 deletions

View File

@@ -27,6 +27,7 @@
<module>spring-boot-sample-actuator-log4j2</module>
<module>spring-boot-sample-actuator-noweb</module>
<module>spring-boot-sample-actuator-ui</module>
<module>spring-boot-sample-actuator-custom-security</module>
<module>spring-boot-sample-amqp</module>
<module>spring-boot-sample-aop</module>
<module>spring-boot-sample-atmosphere</module>

View File

@@ -0,0 +1,60 @@
<?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>2.0.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-actuator-custom-security</artifactId>
<name>Spring Boot Actuator Custom Security Sample</name>
<description>Spring Boot Actuator Custom Security Sample</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.jolokia</groupId>
<artifactId>jolokia-core</artifactId>
</dependency>
<!-- Runtime -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,26 @@
package sample.actuator.customsecurity;
import java.util.Date;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ExampleController {
@GetMapping("/")
public String home(Map<String, Object> model) {
model.put("message", "Hello World");
model.put("title", "Hello Home");
model.put("date", new Date());
return "home";
}
@RequestMapping("/foo")
public String foo() {
throw new RuntimeException("Expected exception in controller");
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2012-2017 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.actuator.customsecurity;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SampleActuatorCustomSecurityApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(SampleActuatorCustomSecurityApplication.class, args);
}
}

View File

@@ -0,0 +1,40 @@
package sample.actuator.customsecurity;
import org.springframework.boot.autoconfigure.security.SpringBootSecurity;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private SpringBootSecurity bootSecurity;
public SecurityConfiguration(SpringBootSecurity bootSecurity) {
this.bootSecurity = bootSecurity;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").authorities("ROLE_USER").and()
.withUser("admin").password("admin").authorities("ROLE_ACTUATOR", "ROLE_USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.requestMatchers(this.bootSecurity.endpointIds("status", "info")).permitAll()
.requestMatchers(this.bootSecurity.endpointIds(SpringBootSecurity.ALL_ENDPOINTS)).hasRole("ACTUATOR")
.requestMatchers(this.bootSecurity.staticResources()).permitAll()
.antMatchers("/foo").permitAll()
.antMatchers("/**").hasRole("USER")
.and()
.cors()
.and()
.httpBasic();
}
}

View File

@@ -0,0 +1 @@
endpoints.all.web.enabled=true

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,32 @@
<#import "/spring.ftl" as spring />
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
<#assign home><@spring.url relativeUrl="/"/></#assign>
<#assign bootstrap><@spring.url relativeUrl="/css/bootstrap.min.css"/></#assign>
<link rel="stylesheet" href="${bootstrap}" />
</head>
<body>
<div class="container">
<div class="navbar">
<div class="navbar-inner">
<a class="brand" href="http://freemarker.org/"> FreeMarker -
Plain </a>
<ul class="nav">
<li><a href="${home}"> Home </a></li>
</ul>
</div>
</div>
<h1>Error Page</h1>
<div id="created">${timestamp?datetime}</div>
<div>
There was an unexpected error (type=${error}, status=${status}).
</div>
<div>${message}</div>
<div>
Please contact the operator with the above information.
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,26 @@
<#import "/spring.ftl" as spring />
<!DOCTYPE html>
<html>
<head>
<title>${title}</title>
<#assign home><@spring.url relativeUrl="/"/></#assign>
<#assign bootstrap><@spring.url relativeUrl="/css/bootstrap.min.css"/></#assign>
<link rel="stylesheet" href="${bootstrap}" />
</head>
<body>
<div class="container">
<div class="navbar">
<div class="navbar-inner">
<a class="brand" href="http://freemarker.org/"> FreeMarker -
Plain </a>
<ul class="nav">
<li><a href="${home}"> Home </a></li>
</ul>
</div>
</div>
<h1>${title}</h1>
<div>${message}</div>
<div id="created">${date?datetime}</div>
</div>
</body>
</html>

View File

@@ -1,4 +1,4 @@
package sample.actuator;
package sample.actuator.customsecurity;
import java.net.URI;
import java.util.Map;
@@ -12,10 +12,14 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.LocalHostUriTemplateHandler;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
@@ -59,7 +63,7 @@ public class CorsSampleActuatorApplicationTests {
@Test
public void preflightRequestToEndpointShouldReturnOk() throws Exception {
RequestEntity<?> healthRequest = RequestEntity
.options(new URI("/application/health"))
.options(new URI("/application/env"))
.header("Origin", "http://localhost:8080")
.header("Access-Control-Request-Method", "GET").build();
ResponseEntity<?> exchange = this.testRestTemplate.exchange(healthRequest,
@@ -70,7 +74,7 @@ public class CorsSampleActuatorApplicationTests {
@Test
public void preflightRequestWhenCorsConfigInvalidShouldReturnForbidden()
throws Exception {
RequestEntity<?> entity = RequestEntity.options(new URI("/application/health"))
RequestEntity<?> entity = RequestEntity.options(new URI("/application/env"))
.header("Origin", "http://localhost:9095")
.header("Access-Control-Request-Method", "GET").build();
ResponseEntity<byte[]> exchange = this.testRestTemplate.exchange(entity,

View File

@@ -14,16 +14,12 @@
* limitations under the License.
*/
package sample.actuator;
import java.util.Map;
package sample.actuator.customsecurity;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.web.server.LocalManagementPort;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
@@ -39,17 +35,14 @@ import static org.assertj.core.api.Assertions.assertThat;
* Integration tests for separate management and main service ports.
*
* @author Dave Syer
* @author Madhura Bhave
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
"management.port=0", "management.context-path=/admin",
"management.security.enabled=false" })
"management.port=0", "management.context-path=/admin"})
@DirtiesContext
public class InsecureManagementPortAndPathSampleActuatorApplicationTests {
@Autowired
private SecurityProperties security;
@LocalServerPort
private int port = 9010;
@@ -59,43 +52,36 @@ public class InsecureManagementPortAndPathSampleActuatorApplicationTests {
@Test
public void testHome() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = new TestRestTemplate("user", getPassword())
.getForEntity("http://localhost:" + this.port, Map.class);
ResponseEntity<String> entity = new TestRestTemplate("user", "password")
.getForEntity("http://localhost:" + this.port, String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
@SuppressWarnings("unchecked")
Map<String, Object> body = entity.getBody();
assertThat(body.get("message")).isEqualTo("Hello Phil");
assertThat(entity.getBody()).contains("Hello World");
}
@Test
public void testMetrics() throws Exception {
testHome(); // makes sure some requests have been made
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + this.managementPort + "/admin/metrics", Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@Test
public void testHealth() throws Exception {
public void testSecureActuator() throws Exception {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + this.managementPort + "/admin/health",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
public void testInsecureActuator() throws Exception {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + this.managementPort + "/admin/status",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"status\":\"UP\"");
}
@Test
public void testMissing() throws Exception {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
ResponseEntity<String> entity = new TestRestTemplate("admin", "admin").getForEntity(
"http://localhost:" + this.managementPort + "/admin/missing",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
assertThat(entity.getBody()).contains("\"status\":404");
}
private String getPassword() {
return this.security.getUser().getPassword();
}
}

View File

@@ -0,0 +1,75 @@
package sample.actuator.customsecurity;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Madhura Bhave
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DirtiesContext
public class SampleActuatorCustomSecurityApplicationTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void homeIsSecure() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = this.restTemplate.getForEntity("/", Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
@SuppressWarnings("unchecked")
Map<String, Object> body = entity.getBody();
assertThat(body.get("error")).isEqualTo("Unauthorized");
assertThat(entity.getHeaders()).doesNotContainKey("Set-Cookie");
}
@Test
public void testInsecureApplicationPath() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = this.restTemplate.getForEntity("/foo", Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
@SuppressWarnings("unchecked")
Map<String, Object> body = entity.getBody();
assertThat((String)body.get("message")).contains("Expected exception in controller");
}
@Test
public void testInsecureStaticResources() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<String> entity = this.restTemplate.getForEntity("/css/bootstrap.min.css", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("body");
}
@Test
public void insecureActuator() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<String> entity = this.restTemplate.getForEntity("/application/status",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"status\":\"UP\"");
}
@Test
public void secureActuator() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = this.restTemplate.getForEntity("/application/env",
Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
}

View File

@@ -18,10 +18,21 @@ package sample.actuator.log4j2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@SpringBootApplication
public class SampleActuatorLog4J2Application {
@Bean
public UserDetailsService userDetailsService() throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("password").roles("USER").build());
return manager;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SampleActuatorLog4J2Application.class, args);
}

View File

@@ -1,3 +1,2 @@
endpoints.shutdown.enabled=true
management.security.enabled=false
endpoints.all.web.enabled=true

View File

@@ -16,6 +16,8 @@
package sample.actuator.log4j2;
import java.util.Base64;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Rule;
@@ -63,10 +65,17 @@ public class SampleActuatorLog4J2ApplicationTests {
@Test
public void validateLoggersEndpoint() throws Exception {
this.mvc.perform(get("/application/loggers/org.apache.coyote.http11.Http11NioProtocol"))
this.mvc.perform(get("/application/loggers/org.apache.coyote.http11.Http11NioProtocol")
.header("Authorization", "Basic " + getBasicAuth()))
.andExpect(status().isOk())
.andExpect(content().string(equalTo("{\"configuredLevel\":\"WARN\","
+ "\"effectiveLevel\":\"WARN\"}")));
}
private String getBasicAuth() {
return new String(Base64.getEncoder()
.encode(("user:password").getBytes()));
}
}

View File

@@ -21,6 +21,10 @@ import java.util.Map;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -29,6 +33,13 @@ import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class SampleActuatorUiApplication {
@Bean
public UserDetailsService userDetailsService() throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("password").roles("USER").build());
return manager;
}
@GetMapping("/")
public String home(Map<String, Object> model) {
model.put("message", "Hello World");

View File

@@ -1,4 +1,2 @@
health.diskspace.enabled=false
# empty so home page is unsecured
security.basic.path=
endpoints.all.web.enabled=true

View File

@@ -21,9 +21,7 @@ import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.web.server.LocalManagementPort;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
@@ -46,9 +44,6 @@ import static org.assertj.core.api.Assertions.assertThat;
@DirtiesContext
public class SampleActuatorUiApplicationPortTests {
@Autowired
private SecurityProperties security;
@LocalServerPort
private int port = 9010;
@@ -82,7 +77,7 @@ public class SampleActuatorUiApplicationPortTests {
}
private String getPassword() {
return this.security.getUser().getPassword();
return "password";
}
}

View File

@@ -54,7 +54,8 @@ public class SampleActuatorUiApplicationTests {
public void testHome() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = this.restTemplate.exchange("/", HttpMethod.GET,
ResponseEntity<String> entity = this.restTemplate.withBasicAuth("user", getPassword())
.exchange("/", HttpMethod.GET,
new HttpEntity<Void>(headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("<title>Hello");
@@ -80,11 +81,16 @@ public class SampleActuatorUiApplicationTests {
public void testError() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = this.restTemplate.exchange("/error",
ResponseEntity<String> entity = this.restTemplate.withBasicAuth("user", getPassword())
.exchange("/error",
HttpMethod.GET, new HttpEntity<Void>(headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
assertThat(entity.getBody()).contains("<html>").contains("<body>")
.contains("Please contact the operator with the above information");
}
private String getPassword() {
return "password";
}
}

View File

@@ -22,6 +22,10 @@ import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@SpringBootApplication
@EnableConfigurationProperties(ServiceProperties.class)
@@ -31,6 +35,13 @@ public class SampleActuatorApplication {
SpringApplication.run(SampleActuatorApplication.class, args);
}
@Bean
public UserDetailsService userDetailsService() throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("password").roles("USER").build());
return manager;
}
@Bean
public HealthIndicator helloHealthIndicator() {
return new HealthIndicator() {

View File

@@ -11,6 +11,7 @@ server.tomcat.accesslog.pattern=%h %t "%r" %s %b
security.require-ssl=false
#spring.jackson.serialization.INDENT_OUTPUT=true
spring.jmx.enabled=true
endpoints.all.web.enabled=true
spring.jackson.serialization.write_dates_as_timestamps=false

View File

@@ -22,7 +22,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
@@ -45,9 +44,6 @@ import static org.assertj.core.api.Assertions.assertThat;
@ActiveProfiles("endpoints")
public class EndpointsPropertiesSampleActuatorApplicationTests {
@Autowired
private SecurityProperties security;
@Autowired
private TestRestTemplate restTemplate;
@@ -74,7 +70,7 @@ public class EndpointsPropertiesSampleActuatorApplicationTests {
}
private String getPassword() {
return this.security.getUser().getPassword();
return "password";
}
}

View File

@@ -1,80 +0,0 @@
/*
* Copyright 2012-2017 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.actuator;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for insecured service endpoints (even with Spring Security on
* classpath).
*
* @author Dave Syer
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
"management.security.enabled:false" })
@DirtiesContext
@ActiveProfiles("unsecure-management")
public class InsecureManagementSampleActuatorApplicationTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testHomeIsSecure() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = this.restTemplate.getForEntity("/", Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
@SuppressWarnings("unchecked")
Map<String, Object> body = entity.getBody();
assertThat(body.get("error")).isEqualTo("Unauthorized");
assertThat(entity.getHeaders()).doesNotContainKey("Set-Cookie");
}
@Test
public void testMetrics() throws Exception {
try {
testHomeIsSecure(); // makes sure some requests have been made
}
catch (AssertionError ex) {
// ignore;
}
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = this.restTemplate.getForEntity("/application/metrics",
Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
@SuppressWarnings("unchecked")
Map<String, Object> body = entity.getBody();
assertThat(body).containsKey("counter.status.401.unmapped");
}
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2012-2016 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.actuator;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for insecured service endpoints (even with Spring Security on
* classpath).
*
* @author Dave Syer
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
"security.basic.enabled:false" })
@DirtiesContext
public class InsecureSampleActuatorApplicationTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testHome() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = this.restTemplate.getForEntity("/", Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
@SuppressWarnings("unchecked")
Map<String, Object> body = entity.getBody();
assertThat(body.get("message")).isEqualTo("Hello Phil");
assertThat(entity.getHeaders()).doesNotContainKey("Set-Cookie");
}
}

View File

@@ -21,9 +21,7 @@ import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.web.server.LocalManagementPort;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
@@ -47,9 +45,6 @@ import static org.assertj.core.api.Assertions.assertThat;
@DirtiesContext
public class ManagementAddressActuatorApplicationTests {
@Autowired
private SecurityProperties security;
@LocalServerPort
private int port = 9010;
@@ -75,7 +70,7 @@ public class ManagementAddressActuatorApplicationTests {
}
private String getPassword() {
return this.security.getUser().getPassword();
return "password";
}
}

View File

@@ -22,7 +22,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
@@ -47,9 +46,6 @@ public class ManagementPathSampleActuatorApplicationTests {
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private SecurityProperties securityProperties;
@Test
public void testHealth() throws Exception {
ResponseEntity<String> entity = this.restTemplate
@@ -71,7 +67,7 @@ public class ManagementPathSampleActuatorApplicationTests {
}
private String getPassword() {
return this.securityProperties.getUser().getPassword();
return "password";
}
}

View File

@@ -21,9 +21,7 @@ import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.web.server.LocalManagementPort;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
@@ -46,9 +44,6 @@ import static org.assertj.core.api.Assertions.assertThat;
@DirtiesContext
public class ManagementPortAndPathSampleActuatorApplicationTests {
@Autowired
private SecurityProperties security;
@LocalServerPort
private int port = 9010;
@@ -98,7 +93,7 @@ public class ManagementPortAndPathSampleActuatorApplicationTests {
@Test
public void testErrorPage() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = new TestRestTemplate()
ResponseEntity<Map> entity = new TestRestTemplate("user", getPassword())
.getForEntity("http://localhost:" + this.port + "/error", Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
@SuppressWarnings("unchecked")
@@ -109,7 +104,7 @@ public class ManagementPortAndPathSampleActuatorApplicationTests {
@Test
public void testManagementErrorPage() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(
ResponseEntity<Map> entity = new TestRestTemplate("user", getPassword()).getForEntity(
"http://localhost:" + this.managementPort + "/error", Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
@SuppressWarnings("unchecked")
@@ -118,7 +113,7 @@ public class ManagementPortAndPathSampleActuatorApplicationTests {
}
private String getPassword() {
return this.security.getUser().getPassword();
return "password";
}
}

View File

@@ -18,14 +18,14 @@ package sample.actuator;
import java.util.Map;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.web.server.LocalManagementPort;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
@@ -46,9 +46,6 @@ import static org.assertj.core.api.Assertions.assertThat;
@DirtiesContext
public class ManagementPortSampleActuatorApplicationTests {
@Autowired
private SecurityProperties security;
@LocalServerPort
private int port = 9010;
@@ -89,7 +86,7 @@ public class ManagementPortSampleActuatorApplicationTests {
@Test
public void testErrorPage() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = new TestRestTemplate().getForEntity(
ResponseEntity<Map> entity = new TestRestTemplate("user", getPassword()).getForEntity(
"http://localhost:" + this.managementPort + "/error", Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
@SuppressWarnings("unchecked")
@@ -98,7 +95,7 @@ public class ManagementPortSampleActuatorApplicationTests {
}
private String getPassword() {
return this.security.getUser().getPassword();
return "password";
}
}

View File

@@ -22,7 +22,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
@@ -44,9 +43,6 @@ import static org.assertj.core.api.Assertions.assertThat;
@DirtiesContext
public class NoManagementSampleActuatorApplicationTests {
@Autowired
private SecurityProperties security;
@Autowired
private TestRestTemplate restTemplate;
@@ -71,7 +67,7 @@ public class NoManagementSampleActuatorApplicationTests {
}
private String getPassword() {
return this.security.getUser().getPassword();
return "password";
}
}

View File

@@ -25,7 +25,6 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
@@ -51,9 +50,6 @@ import static org.assertj.core.api.Assertions.assertThat;
@DirtiesContext
public class SampleActuatorApplicationTests {
@Autowired
private SecurityProperties security;
@Autowired
private TestRestTemplate restTemplate;
@@ -204,7 +200,8 @@ public class SampleActuatorApplicationTests {
@Test
public void testErrorPageDirectAccess() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = this.restTemplate.getForEntity("/error", Map.class);
ResponseEntity<Map> entity = this.restTemplate.withBasicAuth("user", getPassword())
.getForEntity("/error", Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
@SuppressWarnings("unchecked")
Map<String, Object> body = entity.getBody();
@@ -239,7 +236,7 @@ public class SampleActuatorApplicationTests {
}
private String getPassword() {
return this.security.getUser().getPassword();
return "password";
}
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2012-2017 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.actuator;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for insecured service endpoints (even with Spring Security on
* classpath).
*
* @author Dave Syer
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
"security.basic.enabled:false", "server.servlet.path:/spring" })
@DirtiesContext
public class ServletPathInsecureSampleActuatorApplicationTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testHome() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = this.restTemplate.getForEntity("/spring/",
Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
@SuppressWarnings("unchecked")
Map<String, Object> body = entity.getBody();
assertThat(body.get("message")).isEqualTo("Hello Phil");
assertThat(entity.getHeaders()).doesNotContainKey("Set-Cookie");
}
@Test
public void testMetricsIsSecure() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = this.restTemplate
.getForEntity("/spring/application/metrics", Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
}

View File

@@ -22,7 +22,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
@@ -47,13 +46,11 @@ public class ServletPathSampleActuatorApplicationTests {
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private SecurityProperties security;
@Test
public void testErrorPath() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = this.restTemplate.getForEntity("/spring/error",
ResponseEntity<Map> entity = this.restTemplate.withBasicAuth("user", getPassword())
.getForEntity("/spring/error",
Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
@SuppressWarnings("unchecked")
@@ -84,7 +81,7 @@ public class ServletPathSampleActuatorApplicationTests {
}
private String getPassword() {
return this.security.getUser().getPassword();
return "password";
}
}

View File

@@ -22,7 +22,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
@@ -43,9 +42,6 @@ import static org.assertj.core.api.Assertions.assertThat;
@DirtiesContext
public class ShutdownSampleActuatorApplicationTests {
@Autowired
private SecurityProperties security;
@Autowired
private TestRestTemplate restTemplate;
@@ -73,7 +69,7 @@ public class ShutdownSampleActuatorApplicationTests {
}
private String getPassword() {
return this.security.getUser().getPassword();
return "password";
}
}

View File

@@ -1,3 +1,2 @@
server.error.path: /oops
management.context-path: /admin
endpoints.health.sensitive: false
management.context-path: /admin

View File

@@ -0,0 +1,29 @@
package sample.secure.oauth2.actuator;
import org.springframework.boot.autoconfigure.security.SpringBootSecurity;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* Basic auth security for actuator endpoints.
*
* @author Madhura Bhave
*/
@Configuration
public class ActuatorSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final SpringBootSecurity springBootSecurity;
public ActuatorSecurityConfiguration(SpringBootSecurity springBootSecurity) {
this.springBootSecurity = springBootSecurity;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(this.springBootSecurity.endpointIds(SpringBootSecurity.ALL_ENDPOINTS))
.authorizeRequests().antMatchers("/**").authenticated()
.and()
.httpBasic();
}
}

View File

@@ -20,7 +20,11 @@ import java.util.UUID;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -34,6 +38,13 @@ public class SampleSecureOAuth2ActuatorApplication {
return new Message("Hello World");
}
@Bean
public UserDetailsService userDetailsService() throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("password").roles("USER").build());
return manager;
}
public static void main(String[] args) {
SpringApplication.run(SampleSecureOAuth2ActuatorApplication.class, args);
}

View File

@@ -1,6 +1,5 @@
server.port=8081
security.basic.enabled=true
security.user.password=password
endpoints.all.web.enabled=true
security.oauth2.resource.id=service
security.oauth2.resource.userInfoUri=http://localhost:8080/user
logging.level.org.springframework.security=DEBUG

View File

@@ -0,0 +1,17 @@
package sample.secure.oauth2;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
/**
* @author Madhura Bhave
*/
@Configuration
public class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("greg").password("turnquist").roles("read");
}
}

View File

@@ -1,7 +1,5 @@
spring.datasource.platform=h2
security.user.name=greg
security.user.password=turnquist
security.oauth2.client.client-id=foo
security.oauth2.client.client-secret=bar
security.oauth2.authorization.checkTokenAccess=isAuthenticated()

View File

@@ -20,11 +20,15 @@ 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.Bean;
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;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@EnableAutoConfiguration
@ComponentScan
@@ -34,6 +38,13 @@ public class SampleSecureApplication implements CommandLineRunner {
@Autowired
private SampleService service;
@Bean
public UserDetailsService userDetailsService() throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("password").roles("USER").build());
return manager;
}
@Override
public void run(String... args) throws Exception {
SecurityContextHolder.getContext()

View File

@@ -20,15 +20,10 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import sample.secure.SampleSecureApplicationTests.TestConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
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;
@@ -43,23 +38,17 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Dave Syer
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { SampleSecureApplication.class, TestConfiguration.class })
@SpringBootTest(classes = {SampleSecureApplication.class})
public class SampleSecureApplicationTests {
@Autowired
private SampleService service;
@Autowired
private ApplicationContext context;
private Authentication authentication;
@Before
public void init() {
AuthenticationManager authenticationManager = this.context
.getBean(AuthenticationManager.class);
this.authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken("user", "password"));
this.authentication = new UsernamePasswordAuthenticationToken("user", "password");
}
@After
@@ -90,9 +79,4 @@ public class SampleSecureApplicationTests {
assertThat("Goodbye World").isEqualTo(this.service.denied());
}
@PropertySource("classpath:test.properties")
@Configuration
protected static class TestConfiguration {
}
}

View File

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

View File

@@ -30,11 +30,21 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@SpringBootConfiguration
@EnableAutoConfiguration
public class SampleServletApplication extends SpringBootServletInitializer {
@Bean
public UserDetailsService userDetailsService() throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("password").roles("USER").build());
return manager;
}
@SuppressWarnings("serial")
@Bean
public Servlet dispatcherServlet() {

View File

@@ -16,15 +16,20 @@
package sample.servlet;
import java.util.Collections;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
@@ -44,12 +49,11 @@ public class SampleServletApplicationTests {
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private SecurityProperties security;
@Test
public void testHomeIsSecure() throws Exception {
ResponseEntity<String> entity = this.restTemplate.getForEntity("/", String.class);
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
ResponseEntity<String> entity = this.restTemplate.exchange("/", HttpMethod.GET, new HttpEntity<Void>(headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@@ -62,6 +66,6 @@ public class SampleServletApplicationTests {
}
private String getPassword() {
return this.security.getUser().getPassword();
return "password";
}
}

View File

@@ -20,7 +20,7 @@ import java.util.Date;
import java.util.Map;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.autoconfigure.security.SpringBootSecurity;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
@@ -80,7 +80,6 @@ public class SampleMethodSecurityApplication implements WebMvcConfigurer {
}
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
@Override
@@ -94,4 +93,25 @@ public class SampleMethodSecurityApplication implements WebMvcConfigurer {
}
@Configuration
@Order(1)
protected static class ActuatorSecurity extends WebSecurityConfigurerAdapter {
private final SpringBootSecurity springBootSecurity;
public ActuatorSecurity(SpringBootSecurity springBootSecurity) {
this.springBootSecurity = springBootSecurity;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(this.springBootSecurity.endpointIds(SpringBootSecurity.ALL_ENDPOINTS))
.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic();
}
}
}

View File

@@ -1,2 +1,3 @@
spring.thymeleaf.cache: false
logging.level.org.springframework.security: INFO
logging.level.org.springframework.security: INFO
endpoints.all.web.enabled=true

View File

@@ -105,8 +105,10 @@ public class SampleMethodSecurityApplicationTests {
@Test
public void testManagementProtected() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
ResponseEntity<String> entity = this.restTemplate
.getForEntity("/application/beans", String.class);
.exchange("/application/beans", HttpMethod.GET, new HttpEntity<Void>(headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}

View File

@@ -20,10 +20,8 @@ import java.util.Date;
import java.util.Map;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@@ -60,7 +58,6 @@ public class SampleWebSecureCustomApplication implements WebMvcConfigurer {
}
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
@Override

View File

@@ -23,10 +23,8 @@ import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@@ -63,7 +61,6 @@ public class SampleWebSecureJdbcApplication implements WebMvcConfigurer {
}
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
@Autowired

View File

@@ -20,10 +20,9 @@ import java.util.Date;
import java.util.Map;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.autoconfigure.security.SpringBootSecurity;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@@ -60,12 +59,19 @@ public class SampleWebSecureApplication implements WebMvcConfigurer {
}
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
private final SpringBootSecurity springBootSecurity;
public ApplicationSecurity(SpringBootSecurity springBootSecurity) {
this.springBootSecurity = springBootSecurity;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated().and().formLogin()
http.authorizeRequests()
.requestMatchers(this.springBootSecurity.staticResources()).permitAll()
.anyRequest().fullyAuthenticated().and().formLogin()
.loginPage("/login").failureUrl("/login?error").permitAll().and()
.logout().permitAll();
}

View File

@@ -1,6 +1,5 @@
spring.thymeleaf.cache: false
security.basic.enabled: false
# demo only:
security.user.password: password
logging.level.org.springframework.security: INFO
logging.level.org.springframework.boot.actuate.audit.listener.AuditListener: DEBUG