12 changed files with 387 additions and 8 deletions
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
<?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-secure</artifactId> |
||||
<packaging>jar</packaging> |
||||
<properties> |
||||
<main.basedir>${basedir}/../..</main.basedir> |
||||
</properties> |
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>${project.groupId}</groupId> |
||||
<artifactId>spring-boot-starter-actuator</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.springframework.security</groupId> |
||||
<artifactId>spring-security-config</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.security</groupId> |
||||
<artifactId>spring-security-web</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.thymeleaf</groupId> |
||||
<artifactId>thymeleaf-spring3</artifactId> |
||||
</dependency> |
||||
</dependencies> |
||||
<build> |
||||
<plugins> |
||||
<plugin> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-maven-plugin</artifactId> |
||||
</plugin> |
||||
</plugins> |
||||
</build> |
||||
</project> |
||||
@ -0,0 +1,78 @@
@@ -0,0 +1,78 @@
|
||||
/* |
||||
* 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.sample.ops.ui; |
||||
|
||||
import java.util.Date; |
||||
import java.util.Map; |
||||
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; |
||||
import org.springframework.boot.builder.SpringApplicationBuilder; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.ComponentScan; |
||||
import org.springframework.core.Ordered; |
||||
import org.springframework.core.annotation.Order; |
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; |
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; |
||||
|
||||
@EnableAutoConfiguration |
||||
@ComponentScan |
||||
@Controller |
||||
public class SampleSecureApplication extends WebMvcConfigurerAdapter { |
||||
|
||||
@RequestMapping("/") |
||||
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"); |
||||
} |
||||
|
||||
public static void main(String[] args) throws Exception { |
||||
// Set user password to "password" for demo purposes only
|
||||
new SpringApplicationBuilder(SampleSecureApplication.class).properties( |
||||
"security.basic.enabled=false", "security.user.password=password").run(args); |
||||
} |
||||
|
||||
@Override |
||||
public void addViewControllers(ViewControllerRegistry registry) { |
||||
registry.addViewController("/login").setViewName("login"); |
||||
} |
||||
|
||||
@Bean |
||||
public ApplicationSecurity applicationSecurity() { |
||||
return new ApplicationSecurity(); |
||||
} |
||||
|
||||
@Order(Ordered.LOWEST_PRECEDENCE - 8) |
||||
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter { |
||||
@Override |
||||
protected void configure(HttpSecurity http) throws Exception { |
||||
http.authorizeRequests().anyRequest().fullyAuthenticated().and().formLogin() |
||||
.loginPage("/login").failureUrl("/login?error").permitAll(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
spring.thymeleaf.cache: false |
||||
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<configuration> |
||||
<include resource="org/springframework/boot/logging/logback/base.xml"/> |
||||
<!-- logger name="org.springframework.boot" level="DEBUG"/--> |
||||
<logger name="org.springframework.security" level="DEBUG"/> |
||||
</configuration> |
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html> |
||||
<html xmlns:th="http://www.thymeleaf.org"> |
||||
<head> |
||||
<title>Error</title> |
||||
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" |
||||
href="../../css/bootstrap.min.css" /> |
||||
</head> |
||||
<body> |
||||
<div class="container"> |
||||
<div class="navbar"> |
||||
<div class="navbar-inner"> |
||||
<a class="brand" href="http://www.thymeleaf.org"> Thymeleaf - |
||||
Plain </a> |
||||
<ul class="nav"> |
||||
<li><a th:href="@{/}" href="home.html"> Home </a></li> |
||||
<li><a th:href="@{/logout}" href="logout"> Logout </a></li> |
||||
</ul> |
||||
</div> |
||||
</div> |
||||
<h1 th:text="${title}">Title</h1> |
||||
<div id="created" th:text="${#dates.format(timestamp)}">July 11, |
||||
2012 2:17:16 PM CDT</div> |
||||
<div> |
||||
There was an unexpected error (type=<span th:text="${error}">Bad</span>, status=<span th:text="${status}">500</span>). |
||||
</div> |
||||
<div th:text="${message}">Fake content</div> |
||||
<div> |
||||
Please contact the operator with the above information. |
||||
</div> |
||||
</div> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html> |
||||
<html xmlns:th="http://www.thymeleaf.org"> |
||||
<head> |
||||
<title th:text="${title}">Title</title> |
||||
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" |
||||
href="../../css/bootstrap.min.css" /> |
||||
</head> |
||||
<body> |
||||
<div class="container"> |
||||
<div class="navbar"> |
||||
<div class="navbar-inner"> |
||||
<a class="brand" href="http://www.thymeleaf.org"> Thymeleaf - |
||||
Plain </a> |
||||
<ul class="nav"> |
||||
<li><a th:href="@{/}" href="home.html"> Home </a></li> |
||||
<li><a th:href="@{/logout}" href="logout"> Logout </a></li> |
||||
</ul> |
||||
</div> |
||||
</div> |
||||
<h1 th:text="${title}">Title</h1> |
||||
<div th:text="${message}">Fake content</div> |
||||
<div id="created" th:text="${#dates.format(date)}">July 11, |
||||
2012 2:17:16 PM CDT</div> |
||||
</div> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html> |
||||
<html xmlns:th="http://www.thymeleaf.org"> |
||||
<head> |
||||
<title>Login</title> |
||||
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}" |
||||
href="../../css/bootstrap.min.css" /> |
||||
</head> |
||||
<body onload="document.f.username.focus();"> |
||||
<div class="container"> |
||||
<div class="navbar"> |
||||
<div class="navbar-inner"> |
||||
<a class="brand" href="http://www.thymeleaf.org"> Thymeleaf - |
||||
Plain </a> |
||||
<ul class="nav"> |
||||
<li><a th:href="@{/}" href="home.html"> Home </a></li> |
||||
</ul> |
||||
</div> |
||||
</div> |
||||
<div class="content"> |
||||
<p th:if="${param.logout}" class="alert">You have been logged out</p> |
||||
<p th:if="${param.error}" class="alert alert-error">There was an error, please try again</p> |
||||
<h2>Login with Username and Password</h2> |
||||
<form name="form" action="/login" method="POST"> |
||||
<fieldset> |
||||
<input type="text" name="username" value="" placeholder="Username" /> |
||||
<input type="password" name="password" placeholder="Password" /> |
||||
</fieldset> |
||||
<input type="submit" id="login" value="Login" |
||||
class="btn btn-primary" /> <input type="hidden" |
||||
th:name="${_csrf.parameterName}" th:value="${_csrf.token}" /> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,130 @@
@@ -0,0 +1,130 @@
|
||||
/* |
||||
* 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.sample.ops.ui; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.Arrays; |
||||
import java.util.Map; |
||||
import java.util.concurrent.Callable; |
||||
import java.util.concurrent.Executors; |
||||
import java.util.concurrent.Future; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import org.junit.AfterClass; |
||||
import org.junit.BeforeClass; |
||||
import org.junit.Test; |
||||
import org.springframework.boot.SpringApplication; |
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
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.http.client.ClientHttpResponse; |
||||
import org.springframework.web.client.DefaultResponseErrorHandler; |
||||
import org.springframework.web.client.RestTemplate; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertTrue; |
||||
|
||||
/** |
||||
* Basic integration tests for demo application. |
||||
* |
||||
* @author Dave Syer |
||||
*/ |
||||
public class SampleSecureApplicationTests { |
||||
|
||||
private static ConfigurableApplicationContext context; |
||||
|
||||
@BeforeClass |
||||
public static void start() throws Exception { |
||||
Future<ConfigurableApplicationContext> future = Executors |
||||
.newSingleThreadExecutor().submit( |
||||
new Callable<ConfigurableApplicationContext>() { |
||||
@Override |
||||
public ConfigurableApplicationContext call() throws Exception { |
||||
return (ConfigurableApplicationContext) SpringApplication |
||||
.run(SampleSecureApplication.class); |
||||
} |
||||
}); |
||||
context = future.get(60, TimeUnit.SECONDS); |
||||
} |
||||
|
||||
@AfterClass |
||||
public static void stop() { |
||||
if (context != null) { |
||||
context.close(); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void testHome() throws Exception { |
||||
HttpHeaders headers = new HttpHeaders(); |
||||
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML)); |
||||
ResponseEntity<String> entity = getRestTemplate().exchange( |
||||
"http://localhost:8080", HttpMethod.GET, new HttpEntity<Void>(headers), |
||||
String.class); |
||||
assertEquals(HttpStatus.OK, entity.getStatusCode()); |
||||
assertTrue("Wrong body (title doesn't match):\n" + entity.getBody(), entity |
||||
.getBody().contains("<title>Login")); |
||||
} |
||||
|
||||
@Test |
||||
public void testCss() throws Exception { |
||||
ResponseEntity<String> entity = getRestTemplate().getForEntity( |
||||
"http://localhost:8080/css/bootstrap.min.css", String.class); |
||||
assertEquals(HttpStatus.OK, entity.getStatusCode()); |
||||
assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody().contains("body")); |
||||
} |
||||
|
||||
@Test |
||||
public void testMetrics() throws Exception { |
||||
@SuppressWarnings("rawtypes") |
||||
ResponseEntity<Map> entity = getRestTemplate().getForEntity( |
||||
"http://localhost:8080/metrics", Map.class); |
||||
assertEquals(HttpStatus.UNAUTHORIZED, entity.getStatusCode()); |
||||
} |
||||
|
||||
@Test |
||||
public void testError() throws Exception { |
||||
HttpHeaders headers = new HttpHeaders(); |
||||
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML)); |
||||
ResponseEntity<String> entity = getRestTemplate().exchange( |
||||
"http://localhost:8080/error", HttpMethod.GET, |
||||
new HttpEntity<Void>(headers), String.class); |
||||
assertEquals(HttpStatus.OK, entity.getStatusCode()); |
||||
assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody() |
||||
.contains("<html>")); |
||||
assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody() |
||||
.contains("<body>")); |
||||
assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody() |
||||
.contains("Please contact the operator with the above information")); |
||||
} |
||||
|
||||
private RestTemplate getRestTemplate() { |
||||
RestTemplate restTemplate = new RestTemplate(); |
||||
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() { |
||||
@Override |
||||
public void handleError(ClientHttpResponse response) throws IOException { |
||||
} |
||||
}); |
||||
return restTemplate; |
||||
|
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue