Remove use of Thymeleaf from smoke tests

Closes gh-28788
This commit is contained in:
Scott Frederick 2021-11-23 12:11:55 -06:00
parent 03e283a2e6
commit 12244a8edd
67 changed files with 237 additions and 791 deletions

View File

@ -76,9 +76,7 @@ dependencies {
testImplementation("org.springframework:spring-websocket")
testImplementation("org.springframework.hateoas:spring-hateoas")
testImplementation("org.springframework.security:spring-security-test")
testImplementation("org.thymeleaf:thymeleaf")
testImplementation("org.thymeleaf:thymeleaf-spring5")
testImplementation("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect")
testImplementation("org.freemarker:freemarker")
testRuntimeOnly("org.aspectj:aspectjweaver")
testRuntimeOnly("org.yaml:snakeyaml")

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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.
@ -64,7 +64,6 @@ class RemoteUrlPropertyExtractorTests {
void validUrl() {
ApplicationContext context = doTest("http://localhost:8080");
assertThat(context.getEnvironment().getProperty("remoteUrl")).isEqualTo("http://localhost:8080");
assertThat(context.getEnvironment().getProperty("spring.thymeleaf.cache")).isNull();
}
@Test

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@ -29,12 +29,11 @@ import org.apache.jasper.EmbeddedServletOptions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration;
import org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.autoconfigure.web.WebProperties.Resources;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
@ -55,6 +54,7 @@ import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.servlet.view.AbstractTemplateViewResolver;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@ -83,18 +83,11 @@ class LocalDevToolsAutoConfigurationTests {
}
}
@Test
void thymeleafCacheIsFalse() throws Exception {
this.context = getContext(() -> initializeAndRun(Config.class));
SpringResourceTemplateResolver resolver = this.context.getBean(SpringResourceTemplateResolver.class);
assertThat(resolver.isCacheable()).isFalse();
}
@Test
void defaultPropertyCanBeOverriddenFromCommandLine() throws Exception {
this.context = getContext(() -> initializeAndRun(Config.class, "--spring.thymeleaf.cache=true"));
SpringResourceTemplateResolver resolver = this.context.getBean(SpringResourceTemplateResolver.class);
assertThat(resolver.isCacheable()).isTrue();
this.context = getContext(() -> initializeAndRun(Config.class, "--spring.freemarker.cache=true"));
AbstractTemplateViewResolver resolver = this.context.getBean(AbstractTemplateViewResolver.class);
assertThat(resolver.isCache()).isTrue();
}
@Test
@ -103,8 +96,8 @@ class LocalDevToolsAutoConfigurationTests {
System.setProperty("user.home", new File("src/test/resources/user-home").getAbsolutePath());
try {
this.context = getContext(() -> initializeAndRun(Config.class));
SpringResourceTemplateResolver resolver = this.context.getBean(SpringResourceTemplateResolver.class);
assertThat(resolver.isCacheable()).isTrue();
AbstractTemplateViewResolver resolver = this.context.getBean(AbstractTemplateViewResolver.class);
assertThat(resolver.isCache()).isTrue();
}
finally {
System.setProperty("user.home", userHome);
@ -267,7 +260,6 @@ class LocalDevToolsAutoConfigurationTests {
private Map<String, Object> getDefaultProperties(Map<String, Object> specifiedProperties) {
Map<String, Object> properties = new HashMap<>();
properties.put("spring.thymeleaf.check-template-location", false);
properties.put("spring.devtools.livereload.port", 0);
properties.put("server.port", 0);
properties.putAll(specifiedProperties);
@ -276,14 +268,14 @@ class LocalDevToolsAutoConfigurationTests {
@Configuration(proxyBeanMethods = false)
@Import({ ServletWebServerFactoryAutoConfiguration.class, LocalDevToolsAutoConfiguration.class,
ThymeleafAutoConfiguration.class })
FreeMarkerAutoConfiguration.class })
static class Config {
}
@Configuration(proxyBeanMethods = false)
@ImportAutoConfiguration({ ServletWebServerFactoryAutoConfiguration.class, LocalDevToolsAutoConfiguration.class,
ThymeleafAutoConfiguration.class })
FreeMarkerAutoConfiguration.class })
static class ConfigWithMockLiveReload {
@Bean

View File

@ -1 +1 @@
spring.thymeleaf.cache=true
spring.freemarker.cache=true

View File

@ -13,7 +13,6 @@ configurations {
dependencies {
developmentOnly project(":spring-boot-project:spring-boot-devtools")
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-thymeleaf"))
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web"))
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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.
@ -16,15 +16,9 @@
package smoketest.devtools;
import java.util.Date;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class MyController {
@ -34,15 +28,4 @@ public class MyController {
Thread.sleep(5000);
}
@GetMapping("/")
public ModelAndView get(HttpSession session) {
Object sessionVar = session.getAttribute("var");
if (sessionVar == null) {
sessionVar = new Date();
session.setAttribute("var", sessionVar);
}
ModelMap model = new ModelMap("message", Message.MESSAGE).addAttribute("sessionVar", sessionVar);
return new ModelAndView("hello", model);
}
}

View File

@ -1,23 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">
<head>
<title>Hello</title>
</head>
<body>
<h1 th:text="${message}">Header</h1>
<div class="content">
<h2 th:text="${sessionVar}">Session Var</h2>
Lorem ipsum dolor sit amet,
consectetur adipiscing elit. Cras ut fringilla augue, quis dictum
turpis. Sed tincidunt mi vel euismod viverra. Nulla facilisi.
Suspendisse mauris dolor, egestas ac leo at, porttitor ullamcorper
leo. Suspendisse consequat, justo ut rutrum interdum, nibh massa
semper dui, id sagittis tellus lectus at nibh. Etiam at scelerisque
nisi. Quisque vel eros tempor, fermentum sapien sed, gravida neque.
Fusce interdum sed dolor a semper. Morbi porta mauris a velit laoreet
viverra. Praesent et tellus vehicula, sagittis mi quis, faucibus urna.
Ut diam tortor, vehicula eget aliquam eget, elementum a odio. Fusce at
nisl sapien. Suspendisse potenti.
</div>
</body>
</html>

View File

@ -8,7 +8,6 @@ description = "Spring Boot web method security smoke test"
dependencies {
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-actuator"))
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-security"))
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-thymeleaf"))
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web"))
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@ -16,9 +16,6 @@
package smoketest.security.method;
import java.util.Date;
import java.util.Map;
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
@ -32,7 +29,6 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
@ -71,17 +67,11 @@ public class SampleMethodSecurityApplication implements WebMvcConfigurer {
protected static class ApplicationSecurity {
@Bean
SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
http.authorizeRequests((requests) -> {
requests.antMatchers("/login").permitAll();
requests.anyRequest().fullyAuthenticated();
});
http.formLogin((form) -> {
form.loginPage("/login");
form.failureUrl("/login?error");
});
http.logout((logout) -> logout.logoutRequestMatcher(new AntPathRequestMatcher("/logout")));
http.exceptionHandling((exceptions) -> exceptions.accessDeniedPage("/access?error"));
SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests((requests) -> requests.anyRequest().fullyAuthenticated());
http.formLogin((form) -> form.loginPage("/login").permitAll());
http.exceptionHandling((exceptions) -> exceptions.accessDeniedPage("/access"));
return http.build();
}
@ -93,6 +83,7 @@ public class SampleMethodSecurityApplication implements WebMvcConfigurer {
@Bean
SecurityFilterChain actuatorSecurity(HttpSecurity http) throws Exception {
http.csrf().disable();
http.requestMatcher(EndpointRequest.toAnyEndpoint());
http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
http.httpBasic();
@ -106,10 +97,7 @@ public class SampleMethodSecurityApplication implements WebMvcConfigurer {
@GetMapping("/")
@Secured("ROLE_ADMIN")
public String home(Map<String, Object> model) {
model.put("message", "Hello World");
model.put("title", "Hello Home");
model.put("date", new Date());
public String home() {
return "home";
}

View File

@ -1,3 +1,4 @@
spring.thymeleaf.cache: false
logging.level.org.springframework.security: INFO
logging.level.org.springframework.security=INFO
management.endpoints.web.exposure.include=*
spring.mvc.view.prefix=/templates/
spring.mvc.view.suffix=.html

View File

@ -1,27 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="https://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="https://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>
<p class="alert alert-error">Access denied:
you do not have permission for that resource</p>
<div th:text="${message}">Fake content</div>
<div>Please contact the operator with the above information.</div>
</div>
</body>
</html>

View File

@ -1,32 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="https://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="https://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>

View File

@ -1,26 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="https://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="https://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>

View File

@ -1,34 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="https://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="https://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" th:action="@{/login}" 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" />
</form>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
</head>
<body>
<div class="container">
<p>Access denied: you do not have permission for that resource</p>
</div>
</body>
</html>

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
</head>
<body>
<div class="container">
<p>Home</p>
</div>
</body>
</html>

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body">
<div class="container">
<div class="content">
<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" />
</form>
</div>
</div>
</body>
</html>

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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.
@ -16,9 +16,7 @@
package smoketest.security.method;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Collections;
import org.junit.jupiter.api.Test;
@ -43,6 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Basic integration tests for demo application.
*
* @author Dave Syer
* @author Scott Frederick
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class SampleMethodSecurityApplicationTests {
@ -56,35 +55,32 @@ class SampleMethodSecurityApplicationTests {
@Test
void testHome() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = this.restTemplate.exchange("/", HttpMethod.GET, new HttpEntity<Void>(headers),
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("<title>Login");
}
@Test
void testLogin() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
form.set("username", "admin");
form.set("password", "admin");
getCsrf(form, headers);
ResponseEntity<String> entity = this.restTemplate.exchange("/login", HttpMethod.POST,
new HttpEntity<>(form, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
assertThat(entity.getHeaders().getLocation().toString()).isEqualTo("http://localhost:" + this.port + "/");
assertThat(entity.getHeaders().getLocation().toString()).endsWith(this.port + "/");
}
@Test
void testDenied() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
form.set("username", "user");
form.set("password", "user");
getCsrf(form, headers);
ResponseEntity<String> entity = this.restTemplate.exchange("/login", HttpMethod.POST,
new HttpEntity<>(form, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
@ -99,7 +95,7 @@ class SampleMethodSecurityApplicationTests {
@Test
void testManagementProtected() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
ResponseEntity<String> entity = this.restTemplate.exchange("/actuator/beans", HttpMethod.GET,
new HttpEntity<Void>(headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
@ -118,14 +114,4 @@ class SampleMethodSecurityApplicationTests {
}
}
private void getCsrf(MultiValueMap<String, String> form, HttpHeaders headers) {
ResponseEntity<String> page = this.restTemplate.getForEntity("/login", String.class);
String cookie = page.getHeaders().getFirst("Set-Cookie");
headers.set("Cookie", cookie);
String body = page.getBody();
Matcher matcher = Pattern.compile("(?s).*name=\"_csrf\".*?value=\"([^\"]+).*").matcher(body);
matcher.find();
form.set("_csrf", matcher.group(1));
}
}

View File

@ -7,7 +7,6 @@ description = "Spring Boot web secure custom smoke test"
dependencies {
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-security"))
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-thymeleaf"))
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web"))
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@ -16,41 +16,21 @@
package smoketest.web.secure.custom;
import java.util.Date;
import java.util.Map;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
@Controller
public class SampleWebSecureCustomApplication implements WebMvcConfigurer {
@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");
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
registry.addViewController("/login").setViewName("login");
}
@ -63,15 +43,9 @@ public class SampleWebSecureCustomApplication implements WebMvcConfigurer {
@Bean
SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.authorizeRequests((requests) -> {
requests.antMatchers("/css/**").permitAll();
requests.anyRequest().fullyAuthenticated();
});
http.formLogin((form) -> {
form.loginPage("/login");
form.failureUrl("/login?error").permitAll();
});
http.logout(LogoutConfigurer::permitAll);
http.csrf().disable();
http.authorizeRequests((requests) -> requests.anyRequest().fullyAuthenticated());
http.formLogin((form) -> form.loginPage("/login").permitAll());
return http.build();
}

View File

@ -1,4 +1,5 @@
spring.thymeleaf.cache: false
logging.level.org.springframework.security: INFO
logging.level.org.springframework.security=INFO
spring.security.user.name=user
spring.security.user.password=password
spring.mvc.view.prefix=/templates/
spring.mvc.view.suffix=.html

View File

@ -1,32 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="https://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="https://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>

View File

@ -1,28 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="https://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="https://www.thymeleaf.org"> Thymeleaf -
Plain </a>
<ul class="nav">
<li><a th:href="@{/}" href="home.html"> Home </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>
<form th:action="@{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>
</div>
</body>
</html>

View File

@ -1,34 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="https://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="https://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" th:action="@{/login}" 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" />
</form>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
</head>
<body>
<div class="container">
<p>Home</p>
</div>
</body>
</html>

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<div class="container">
<div class="content">
<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" />
</form>
</div>
</div>
</body>
</html>

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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.
@ -16,9 +16,7 @@
package smoketest.web.secure.custom;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Collections;
import org.junit.jupiter.api.Test;
@ -42,6 +40,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Basic integration tests for demo application.
*
* @author Dave Syer
* @author Scott Frederick
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class SampleWebSecureCustomApplicationTests {
@ -55,7 +54,7 @@ class SampleWebSecureCustomApplicationTests {
@Test
void testHome() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = this.restTemplate.exchange("/", HttpMethod.GET, new HttpEntity<Void>(headers),
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
@ -65,17 +64,17 @@ class SampleWebSecureCustomApplicationTests {
@Test
void testLoginPage() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = this.restTemplate.exchange("/login", HttpMethod.GET,
new HttpEntity<Void>(headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("_csrf");
assertThat(entity.getBody()).contains("<title>Login</title>");
}
@Test
void testLogin() {
HttpHeaders headers = getHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
form.set("username", "user");
@ -84,27 +83,6 @@ class SampleWebSecureCustomApplicationTests {
new HttpEntity<>(form, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
assertThat(entity.getHeaders().getLocation().toString()).endsWith(this.port + "/");
assertThat(entity.getHeaders().get("Set-Cookie")).isNotNull();
}
private HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
ResponseEntity<String> page = this.restTemplate.getForEntity("/login", String.class);
assertThat(page.getStatusCode()).isEqualTo(HttpStatus.OK);
String cookie = page.getHeaders().getFirst("Set-Cookie");
headers.set("Cookie", cookie);
Pattern pattern = Pattern.compile("(?s).*name=\"_csrf\".*?value=\"([^\"]+).*");
Matcher matcher = pattern.matcher(page.getBody());
assertThat(matcher.matches()).as(page.getBody()).isTrue();
headers.set("X-CSRF-TOKEN", matcher.group(1));
return headers;
}
@Test
void testCss() {
ResponseEntity<String> entity = this.restTemplate.getForEntity("/css/bootstrap.min.css", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("body");
}
}

View File

@ -8,7 +8,6 @@ description = "Spring Boot web secure JDBC smoke test"
dependencies {
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-jdbc"))
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-security"))
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-thymeleaf"))
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web"))
runtimeOnly("com.h2database:h2")

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@ -16,9 +16,6 @@
package smoketest.web.secure.jdbc;
import java.util.Date;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@ -26,34 +23,17 @@ import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
@Controller
public class SampleWebSecureJdbcApplication implements WebMvcConfigurer {
@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");
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
registry.addViewController("/login").setViewName("login");
}
@ -66,15 +46,9 @@ public class SampleWebSecureJdbcApplication implements WebMvcConfigurer {
@Bean
SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.authorizeRequests((requests) -> {
requests.antMatchers("/css/**").permitAll();
requests.anyRequest().fullyAuthenticated();
});
http.formLogin((form) -> {
form.loginPage("/login");
form.failureUrl("/login?error").permitAll();
});
http.logout(LogoutConfigurer::permitAll);
http.csrf().disable();
http.authorizeRequests((requests) -> requests.anyRequest().fullyAuthenticated());
http.formLogin((form) -> form.loginPage("/login").permitAll());
return http.build();
}

View File

@ -1,3 +1,3 @@
debug: true
spring.thymeleaf.cache: false
logging.level.org.springframework.security: INFO
logging.level.org.springframework.security=INFO
spring.mvc.view.prefix=/templates/
spring.mvc.view.suffix=.html

View File

@ -1,32 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="https://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="https://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>

View File

@ -1,28 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="https://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="https://www.thymeleaf.org"> Thymeleaf -
Plain </a>
<ul class="nav">
<li><a th:href="@{/}" href="home.html"> Home </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>
<form th:action="@{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>
</div>
</body>
</html>

View File

@ -1,34 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="https://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="https://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" th:action="@{/login}" 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" />
</form>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
</head>
<body>
<div class="container">
<p>Home</p>
</div>
</body>
</html>

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body">
<div class="container">
<div class="content">
<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" />
</form>
</div>
</div>
</body>
</html>

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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.
@ -16,9 +16,7 @@
package smoketest.web.secure.jdbc;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Collections;
import org.junit.jupiter.api.Test;
@ -42,6 +40,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Basic integration tests for demo application.
*
* @author Dave Syer
* @author Scott Frederick
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class SampleWebSecureJdbcApplicationTests {
@ -55,7 +54,7 @@ class SampleWebSecureJdbcApplicationTests {
@Test
void testHome() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = this.restTemplate.exchange("/", HttpMethod.GET, new HttpEntity<Void>(headers),
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
@ -65,17 +64,17 @@ class SampleWebSecureJdbcApplicationTests {
@Test
void testLoginPage() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = this.restTemplate.exchange("/login", HttpMethod.GET,
new HttpEntity<Void>(headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("_csrf");
assertThat(entity.getBody()).contains("<title>Login</title>");
}
@Test
void testLogin() {
HttpHeaders headers = getHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
form.set("username", "user");
@ -84,27 +83,6 @@ class SampleWebSecureJdbcApplicationTests {
new HttpEntity<>(form, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
assertThat(entity.getHeaders().getLocation().toString()).endsWith(this.port + "/");
assertThat(entity.getHeaders().get("Set-Cookie")).isNotNull();
}
private HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
ResponseEntity<String> page = this.restTemplate.getForEntity("/login", String.class);
assertThat(page.getStatusCode()).isEqualTo(HttpStatus.OK);
String cookie = page.getHeaders().getFirst("Set-Cookie");
headers.set("Cookie", cookie);
Pattern pattern = Pattern.compile("(?s).*name=\"_csrf\".*?value=\"([^\"]+).*");
Matcher matcher = pattern.matcher(page.getBody());
assertThat(matcher.matches()).as(page.getBody()).isTrue();
headers.set("X-CSRF-TOKEN", matcher.group(1));
return headers;
}
@Test
void testCss() {
ResponseEntity<String> entity = this.restTemplate.getForEntity("/css/bootstrap.min.css", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("body");
}
}

View File

@ -6,9 +6,7 @@ plugins {
description = "Spring Boot web secure smoke test"
dependencies {
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-actuator"))
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-security"))
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-thymeleaf"))
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web"))
testImplementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-test"))

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@ -16,42 +16,21 @@
package smoketest.web.secure;
import java.util.Date;
import java.util.Map;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
@Controller
public class SampleWebSecureApplication implements WebMvcConfigurer {
@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");
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
registry.addViewController("/login").setViewName("login");
}
@ -64,15 +43,9 @@ public class SampleWebSecureApplication implements WebMvcConfigurer {
@Bean
SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.authorizeRequests((requests) -> {
requests.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll();
requests.anyRequest().fullyAuthenticated();
});
http.formLogin((form) -> {
form.loginPage("/login");
form.failureUrl("/login?error").permitAll();
});
http.logout(LogoutConfigurer::permitAll);
http.csrf().disable();
http.authorizeRequests((requests) -> requests.anyRequest().fullyAuthenticated());
http.formLogin((form) -> form.loginPage("/login").permitAll());
return http.build();
}

View File

@ -1,6 +1,6 @@
spring.thymeleaf.cache: false
# demo only:
logging.level.org.springframework.security: INFO
logging.level.org.springframework.boot.actuate.audit.listener.AuditListener: DEBUG
logging.level.org.springframework.security=INFO
logging.level.org.springframework.boot.actuate.audit.listener.AuditListener=DEBUG
spring.security.user.name=user
spring.security.user.password=password
spring.mvc.view.prefix=/templates/
spring.mvc.view.suffix=.html

View File

@ -1,10 +0,0 @@
create table users (
username varchar(256),
password varchar(256),
enabled boolean
);
create table authorities (
username varchar(256),
authority varchar(256)
);

View File

@ -1,32 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="https://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="https://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>

View File

@ -1,28 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="https://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="https://www.thymeleaf.org"> Thymeleaf -
Plain </a>
<ul class="nav">
<li><a th:href="@{/}" href="home.html"> Home </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>
<form th:action="@{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>
</div>
</body>
</html>

View File

@ -1,34 +0,0 @@
<!DOCTYPE html>
<html xmlns:th="https://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="https://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" th:action="@{/login}" 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" />
</form>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
</head>
<body>
<div class="container">
<p>Home</p>
</div>
</body>
</html>

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<div class="container">
<div class="content">
<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" />
</form>
</div>
</div>
</body>
</html>

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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.
@ -16,9 +16,7 @@
package smoketest.web.secure;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Collections;
import org.junit.jupiter.api.Test;
@ -42,9 +40,10 @@ import static org.assertj.core.api.Assertions.assertThat;
* Basic integration tests for demo application.
*
* @author Dave Syer
* @author Scott Frederick
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class SampleSecureApplicationTests {
class SampleWebSecureApplicationTests {
@Autowired
private TestRestTemplate restTemplate;
@ -55,9 +54,9 @@ class SampleSecureApplicationTests {
@Test
void testHome() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = this.restTemplate.exchange("/", HttpMethod.GET, new HttpEntity<Void>(headers),
String.class);
headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = this.restTemplate.exchange("/home", HttpMethod.GET,
new HttpEntity<Void>(headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
assertThat(entity.getHeaders().getLocation().toString()).endsWith(this.port + "/login");
}
@ -65,17 +64,17 @@ class SampleSecureApplicationTests {
@Test
void testLoginPage() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
ResponseEntity<String> entity = this.restTemplate.exchange("/login", HttpMethod.GET,
new HttpEntity<Void>(headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("_csrf");
assertThat(entity.getBody()).contains("<title>Login</title>");
}
@Test
void testLogin() {
HttpHeaders headers = getHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
form.set("username", "user");
@ -84,27 +83,6 @@ class SampleSecureApplicationTests {
new HttpEntity<>(form, headers), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
assertThat(entity.getHeaders().getLocation().toString()).endsWith(this.port + "/");
assertThat(entity.getHeaders().get("Set-Cookie")).isNotNull();
}
private HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
ResponseEntity<String> page = this.restTemplate.getForEntity("/login", String.class);
assertThat(page.getStatusCode()).isEqualTo(HttpStatus.OK);
String cookie = page.getHeaders().getFirst("Set-Cookie");
headers.set("Cookie", cookie);
Pattern pattern = Pattern.compile("(?s).*name=\"_csrf\".*?value=\"([^\"]+).*");
Matcher matcher = pattern.matcher(page.getBody());
assertThat(matcher.matches()).as(page.getBody()).isTrue();
headers.set("X-CSRF-TOKEN", matcher.group(1));
return headers;
}
@Test
void testCss() {
ResponseEntity<String> entity = this.restTemplate.getForEntity("/css/bootstrap.min.css", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("body");
}
}

View File

@ -3,7 +3,7 @@ plugins {
id "org.springframework.boot.conventions"
}
description = "Spring Boot web UI smoke test"
description = "Spring Boot web Thymeleaf smoke test"
dependencies {
implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-thymeleaf"))

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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.
@ -14,7 +14,7 @@
* limitations under the License.
*/
package smoketest.web.ui;
package smoketest.web.thymeleaf;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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.
@ -14,7 +14,7 @@
* limitations under the License.
*/
package smoketest.web.ui;
package smoketest.web.thymeleaf;
import java.util.Calendar;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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.
@ -14,7 +14,7 @@
* limitations under the License.
*/
package smoketest.web.ui;
package smoketest.web.thymeleaf;
public interface MessageRepository {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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.
@ -14,7 +14,7 @@
* limitations under the License.
*/
package smoketest.web.ui;
package smoketest.web.thymeleaf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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.
@ -14,12 +14,12 @@
* limitations under the License.
*/
package smoketest.web.ui.mvc;
package smoketest.web.thymeleaf.mvc;
import javax.validation.Valid;
import smoketest.web.ui.Message;
import smoketest.web.ui.MessageRepository;
import smoketest.web.thymeleaf.Message;
import smoketest.web.thymeleaf.MessageRepository;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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.
@ -14,7 +14,7 @@
* limitations under the License.
*/
package smoketest.web.ui;
package smoketest.web.thymeleaf;
import java.util.regex.Pattern;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 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.
@ -14,7 +14,7 @@
* limitations under the License.
*/
package smoketest.web.ui;
package smoketest.web.thymeleaf;
import java.net.URI;
@ -63,12 +63,4 @@ class SampleWebUiApplicationTests {
assertThat(location.toString()).contains("localhost:" + this.port);
}
@Test
void testCss() {
ResponseEntity<String> entity = this.restTemplate
.getForEntity("http://localhost:" + this.port + "/css/bootstrap.min.css", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("body");
}
}