add reactive/webflux/java folder
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2020 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
|
||||
*
|
||||
* https://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 example;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.BrowserVersion;
|
||||
import example.pages.IndexPage;
|
||||
import example.pages.LoginPage;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
|
||||
/**
|
||||
* Integration tests for WebFlux based form log in sample.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
public class WebfluxFormApplicationITests {
|
||||
|
||||
WebDriver driver;
|
||||
|
||||
@LocalServerPort
|
||||
int port;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.driver = new HtmlUnitDriver(BrowserVersion.CHROME);
|
||||
}
|
||||
|
||||
@Test
|
||||
void loginWhenInvalidUsernameThenError() {
|
||||
LoginPage login = IndexPage.to(this.driver, this.port, LoginPage.class);
|
||||
login.assertAt();
|
||||
|
||||
login.loginForm().username("invalid").password("password").submit(LoginPage.class).assertError();
|
||||
}
|
||||
|
||||
@Test
|
||||
void loginAndLogout() {
|
||||
LoginPage login = IndexPage.to(this.driver, this.port, LoginPage.class);
|
||||
login.assertAt();
|
||||
|
||||
IndexPage index = login.loginForm().username("user").password("password").submit(IndexPage.class);
|
||||
index.assertAt();
|
||||
|
||||
login = index.logout();
|
||||
login.assertAt().assertLogout();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2020 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
|
||||
*
|
||||
* https://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 example.pages;
|
||||
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.PageFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* The index page.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
public class IndexPage {
|
||||
|
||||
private WebDriver driver;
|
||||
|
||||
private WebElement logout;
|
||||
|
||||
public IndexPage(WebDriver webDriver) {
|
||||
this.driver = webDriver;
|
||||
}
|
||||
|
||||
public static <T> T to(WebDriver driver, int port, Class<T> page) {
|
||||
driver.get("http://localhost:" + port + "/");
|
||||
return PageFactory.initElements(driver, page);
|
||||
}
|
||||
|
||||
public IndexPage assertAt() {
|
||||
assertThat(this.driver.getTitle()).isEqualTo("Secured");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginPage logout() {
|
||||
this.logout.click();
|
||||
return LoginPage.create(this.driver);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2020 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
|
||||
*
|
||||
* https://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 example.pages;
|
||||
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
import org.openqa.selenium.support.PageFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* The custom log in page.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
public class LoginPage {
|
||||
|
||||
private WebDriver driver;
|
||||
|
||||
@FindBy(css = "div[role=alert]")
|
||||
private WebElement alert;
|
||||
|
||||
private LoginForm loginForm;
|
||||
|
||||
public LoginPage(WebDriver webDriver) {
|
||||
this.driver = webDriver;
|
||||
this.loginForm = PageFactory.initElements(webDriver, LoginForm.class);
|
||||
}
|
||||
|
||||
static LoginPage create(WebDriver driver) {
|
||||
return PageFactory.initElements(driver, LoginPage.class);
|
||||
}
|
||||
|
||||
public LoginPage assertAt() {
|
||||
assertThat(this.driver.getTitle()).isEqualTo("Please Log In");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginPage assertError() {
|
||||
assertThat(this.alert.getText()).isEqualTo("Invalid username and password.");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginPage assertLogout() {
|
||||
assertThat(this.alert.getText()).isEqualTo("You have been logged out.");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginForm loginForm() {
|
||||
return this.loginForm;
|
||||
}
|
||||
|
||||
public static class LoginForm {
|
||||
|
||||
private WebDriver driver;
|
||||
|
||||
private WebElement username;
|
||||
|
||||
private WebElement password;
|
||||
|
||||
@FindBy(css = "button[type=submit]")
|
||||
private WebElement submit;
|
||||
|
||||
public LoginForm(WebDriver driver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
public LoginForm username(String username) {
|
||||
this.username.sendKeys(username);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginForm password(String password) {
|
||||
this.password.sendKeys(password);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <T> T submit(Class<T> page) {
|
||||
this.submit.click();
|
||||
return PageFactory.initElements(this.driver, page);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2020 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
|
||||
*
|
||||
* https://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 example;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
/**
|
||||
* Sends the user to the views.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
@Controller
|
||||
public class IndexController {
|
||||
|
||||
@GetMapping("/")
|
||||
public String index() {
|
||||
return "index";
|
||||
}
|
||||
|
||||
@GetMapping("/login")
|
||||
public String login() {
|
||||
return "login";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2020 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
|
||||
*
|
||||
* https://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 example;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* Demonstrates how to make a webflux based application with a custom form log in.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class WebfluxFormApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(WebfluxFormApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2020 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
|
||||
*
|
||||
* https://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 example;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
|
||||
import static org.springframework.security.config.Customizer.withDefaults;
|
||||
|
||||
/**
|
||||
* WebFlux configuration for custom log in page.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebFluxSecurity
|
||||
public class WebfluxFormSecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
public MapReactiveUserDetailsService userDetailsService() {
|
||||
// @formatter:off
|
||||
UserDetails user = User.withDefaultPasswordEncoder()
|
||||
.username("user")
|
||||
.password("password")
|
||||
.roles("USER")
|
||||
.build();
|
||||
// @formatter:on
|
||||
return new MapReactiveUserDetailsService(user);
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeExchange((exchanges) -> exchanges
|
||||
.pathMatchers("/login").permitAll()
|
||||
.anyExchange().authenticated()
|
||||
)
|
||||
.httpBasic(withDefaults())
|
||||
.formLogin((form) -> form
|
||||
.loginPage("/login")
|
||||
);
|
||||
// @formatter:on
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="WARN">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Secured</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Secured</h1>
|
||||
|
||||
<form th:action="@{/logout}" method="post">
|
||||
<input id="logout" type="submit" value="Log Out"/>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
<title>Please Log In</title>
|
||||
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
|
||||
<link href="https://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet" crossorigin="anonymous"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<form class="form-signin" method="post" th:action="@{/login}">
|
||||
<h2 class="form-signin-heading">Please Log In</h2>
|
||||
<div th:if="${param.error}" class="alert alert-danger" role="alert">Invalid
|
||||
username and password.</div>
|
||||
<div th:if="${param.logout}" class="alert alert-success" role="alert">You
|
||||
have been logged out.</div>
|
||||
<p>
|
||||
<label for="username" class="sr-only">Username</label>
|
||||
<input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus>
|
||||
</p>
|
||||
<p>
|
||||
<label for="password" class="sr-only">Password</label>
|
||||
<input type="password" id="password" name="password" class="form-control" placeholder="Password" required>
|
||||
</p>
|
||||
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user