Added basic login and logout functionality

- update SecurityConfiguration
  - add logout
  - add login
- add AuthenticationController
- add goodbye.html
- add logout.html
- add login.html
This commit is contained in:
2025-01-15 12:41:32 +01:00
parent b812f835ab
commit 3bd6f3b5af
5 changed files with 93 additions and 0 deletions

View File

@@ -2,9 +2,11 @@ package ltd.hlaeja.configuration
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpStatus.FOUND
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity.AuthorizeExchangeSpec
import org.springframework.security.config.web.server.ServerHttpSecurity.FormLoginSpec
import org.springframework.security.web.server.SecurityWebFilterChain
@Configuration
@@ -14,8 +16,19 @@ class SecurityConfiguration {
@Bean
fun securityWebFilterChain(serverHttpSecurity: ServerHttpSecurity): SecurityWebFilterChain = serverHttpSecurity
.authorizeExchange(::authorizeExchange)
.formLogin(::formLogin)
.logout(::logout)
.build()
private fun logout(logout: ServerHttpSecurity.LogoutSpec) = logout.logoutUrl("/logout")
.logoutSuccessHandler { webFilter, _ ->
webFilter.exchange.response.headers.add("Location", "/logout")
webFilter.exchange.response.statusCode = FOUND
webFilter.exchange.response.setComplete()
}
private fun formLogin(login: FormLoginSpec) = login.loginPage("/login")
private fun authorizeExchange(authorizeExchange: AuthorizeExchangeSpec) = authorizeExchange
.publicPaths().permitAll()
.anyExchange().authenticated()
@@ -25,5 +38,7 @@ class SecurityConfiguration {
"/js/**",
"/img/**",
"/actuator/**",
"/login",
"/logout",
)
}

View File

@@ -0,0 +1,22 @@
package ltd.hlaeja.controller
import org.springframework.http.HttpStatus.UNAUTHORIZED
import org.springframework.security.core.context.ReactiveSecurityContextHolder
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ResponseStatus
import reactor.core.publisher.Mono
@Controller
class AuthenticationController {
@GetMapping("/login")
@ResponseStatus(UNAUTHORIZED)
fun login(): Mono<String> = Mono.just("authentication/login")
@GetMapping("/logout")
fun logout(): Mono<String> = ReactiveSecurityContextHolder.getContext()
.filter { it.authentication?.isAuthenticated == true }
.map { "authentication/logout" }
.defaultIfEmpty("authentication/goodbye")
}

View File

@@ -0,0 +1,16 @@
<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Goodbye</title>
<!--/*/<th:block th:insert="~{layout.html :: documentHead}"/>/*/-->
</head>
<body>
<main>
<h1>You are logged out</h1>
<hr>
<p>We hope to see you again soon!</p>
<a th:href="@{/login}">Login Again</a>
</main>
<!--/*/<th:block th:replace="~{layout.html :: script}"/>/*/-->
</body>
</html>

View File

@@ -0,0 +1,23 @@
<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login</title>
<!--/*/<th:block th:insert="~{layout.html :: documentHead}"/>/*/-->
</head>
<body>
<main>
<h1>Login</h1>
<hr>
<form id="loginForm" th:action="@{/login}" th:method="post">
<label for="username" >Username</label>
<input type="text" id="username" name="username" placeholder="Enter your username" required>
<br>
<label for="password">Password</label>
<input type="password" id="password" name="password" placeholder="Enter your password" required>
<br>
<button type="submit">Login</button>
</form>
</main>
<!--/*/<th:block th:replace="~{layout.html :: script}"/>/*/-->
</body>
</html>

View File

@@ -0,0 +1,17 @@
<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Logout</title>
<!--/*/<th:block th:insert="~{layout.html :: documentHead}"/>/*/-->
</head>
<body>
<main>
<h1>Logout</h1>
<hr>
<p>Are you sure you want to logout?</p>
<form id="logoutForm" th:action="@{/logout}" th:method="post"></form>
<button type="submit" onclick="document.getElementById('logoutForm').submit(); return false;">Logout</button>
</main>
<!--/*/<th:block th:replace="~{layout.html :: script}"/>/*/-->
</body>
</html>