ตัวอย่างการเขียน Spring-boot Reactive Custom Login
pom.xml
...
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.1</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>build-info</id>
<goals>
<goal>build-info</goal>
</goals>
<configuration>
<additionalProperties>
<java.version>${java.version}</java.version>
</additionalProperties>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...- ในที่นี้เราจะใช้ Thymleaf ทำ View (Server Side) Rendering น่ะครับ
@SpringBootApplication
public class AppStarter {
public static void main(String[] args) {
SpringApplication.run(AppStarter.class, args);
}
}@Slf4j
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain securityWebFilterChain(final ServerHttpSecurity http) {
return http
.csrf(ServerHttpSecurity.CsrfSpec::disable)
.authorizeExchange(authorizeExchangeSpec -> authorizeExchangeSpec
.pathMatchers("/login").permitAll()
.anyExchange().authenticated()
)
.formLogin(formLoginSpec -> formLoginSpec
.loginPage("/login")
)
.logout(logoutSpec -> logoutSpec
.logoutUrl("/logout")
.requiresLogout(ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, "/logout"))
)
.build();
}
@Bean
public ReactiveUserDetailsService reactiveUserDetailsService(final PasswordEncoder passwordEncoder) {
return username -> {
log.debug("login with username => {}", username);
return Mono.just(
User.withUsername(username)
.password(passwordEncoder.encode("password"))
.authorities(Collections.emptyList())
.build()
);
};
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}- จะเหมือนหัวข้อ spring-boot-reactive-security เพียงแต่มีการเพิ่ม configuration
securityWebFilterChain()เข้ามา - สังเกตว่ามีการกำหนด login entry point หรือ login page เอง
- ทุก ๆ entry point
.anyExchange().authenticated()จะ require login ยกเว้น.pathMatchers("/login").permitAll()ที่อนุญาตให้ทุกคนเข้าถึงได้ .csrf(ServerHttpSecurity.CsrfSpec::disable)มีการ disabled csrf token
@RestController
public class HomeController {
@GetMapping({"", "/"})
public Mono<String> hello(final Authentication authentication) {
return Mono.just("Hello => " + authentication.getName());
}
}@Controller
public class LoginController {
@GetMapping("/login")
public Mono<String> login(){
return Mono.just("custom-login");
}
}<!DOCTYPE html>
<html>
<head>
<title>Custom Login</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h1>Custom Login Page</h1>
<form method="post" action="/login">
<label>Username</label>
<input name="username" type="text"/>
<br/>
<label>Password</label>
<input name="password" type="password" />
<br/>
<button type="submit">Login</button>
</form>
</body>
</html>classpath:application.properties
#--------------------------------- Thymleaf ------------------------------------
spring.thymeleaf.cache=false
spring.thymeleaf.check-template=true
spring.thymeleaf.check-template-location=true
spring.thymeleaf.content-type=text/html
spring.thymeleaf.enabled=true
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML5
spring.thymeleaf.prefix=classpath:/static/
spring.thymeleaf.suffix=.htmlcd ไปที่ root ของ project จากนั้น
$ mvn clean package $ mvn spring-boot:runเปิด browser แล้วเข้า http://localhost:8080
หลังจากนั้นมันจะเด้งเข้าหน้า login
- username = test
- password = password

