cleanup finalize jwt auth
- Add sample login page
This commit is contained in:
parent
b3762373d4
commit
e7961576ca
@ -6,12 +6,14 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
import org.springframework.security.config.Customizer;
|
import org.springframework.security.config.Customizer;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
|
||||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@ -36,7 +38,9 @@ public class SecurityConfig {
|
|||||||
// TODO: Fix security config for this project (currently old state from sharepulse)
|
// TODO: Fix security config for this project (currently old state from sharepulse)
|
||||||
|
|
||||||
http
|
http
|
||||||
.csrf(csrf -> csrf.ignoringRequestMatchers("/api/v1/**")) // Disable CSRF for API routes
|
.csrf(csrf -> csrf
|
||||||
|
.ignoringRequestMatchers("/api/v1/**")
|
||||||
|
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())) // Disable CSRF for API routes
|
||||||
.sessionManagement(sessionManagement -> sessionManagement
|
.sessionManagement(sessionManagement -> sessionManagement
|
||||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // No session will be created by Spring Security
|
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // No session will be created by Spring Security
|
||||||
)
|
)
|
||||||
@ -44,10 +48,19 @@ public class SecurityConfig {
|
|||||||
.requestMatchers("/api/v1/secure/**").authenticated() // Secure these endpoints
|
.requestMatchers("/api/v1/secure/**").authenticated() // Secure these endpoints
|
||||||
.anyRequest().permitAll() // All other requests are allowed without authentication
|
.anyRequest().permitAll() // All other requests are allowed without authentication
|
||||||
)
|
)
|
||||||
|
.headers(headers -> headers
|
||||||
|
.frameOptions(HeadersConfigurer.FrameOptionsConfig::deny) // Prevent clickjacking
|
||||||
|
//.contentSecurityPolicy(Customizer.withDefaults()) // Blocks loading of resources from other domains
|
||||||
|
.xssProtection(Customizer.withDefaults())
|
||||||
|
)
|
||||||
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) // Apply JWT filter
|
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) // Apply JWT filter
|
||||||
.logout(LogoutConfigurer::permitAll)
|
.logout(LogoutConfigurer::permitAll);
|
||||||
.rememberMe(Customizer.withDefaults());
|
|
||||||
|
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thoughts:
|
||||||
|
* - Instead of disabling the contentSecurityPolicy we should simply provide our own libraries so that no external cdns are needed
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
@ -29,14 +29,16 @@ public class AuthenticationController {
|
|||||||
log.debug("Received AuthenticationRequest for username: " + authenticationRequest.getUsername());
|
log.debug("Received AuthenticationRequest for username: " + authenticationRequest.getUsername());
|
||||||
String token = authenticationService.authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword(), request.getRemoteAddr());
|
String token = authenticationService.authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword(), request.getRemoteAddr());
|
||||||
|
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
|
||||||
if(token == null) {
|
if(token == null) {
|
||||||
log.debug("Authentication failed for username: " + authenticationRequest.getUsername());
|
log.debug("Authentication failed for username: " + authenticationRequest.getUsername());
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
response.put("error", "Authentication failed. Username or password incorrect.");
|
||||||
|
return new ResponseEntity<>(response, HttpStatus.UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Object> response = new HashMap<>();
|
|
||||||
response.put("token", token);
|
|
||||||
|
|
||||||
|
response.put("token", token);
|
||||||
if(token == null) {
|
if(token == null) {
|
||||||
log.debug("Authentication failed for username: " + authenticationRequest.getUsername());
|
log.debug("Authentication failed for username: " + authenticationRequest.getUsername());
|
||||||
return new ResponseEntity<>(response, HttpStatus.UNAUTHORIZED);
|
return new ResponseEntity<>(response, HttpStatus.UNAUTHORIZED);
|
||||||
|
@ -12,4 +12,7 @@ spring.jpa.hibernate.ddl-auto=update
|
|||||||
spring.jpa.show-sql=true
|
spring.jpa.show-sql=true
|
||||||
spring.jpa.database=postgresql
|
spring.jpa.database=postgresql
|
||||||
|
|
||||||
logging.level.de.w665.biblenotes=DEBUG
|
logging.level.de.w665.biblenotes=DEBUG
|
||||||
|
|
||||||
|
# Static path
|
||||||
|
spring.web.resources.static-locations=classpath:/static/
|
86
src/main/resources/static/index.html
Normal file
86
src/main/resources/static/index.html
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>Log in</title>
|
||||||
|
<!-- Bootstrap CSS -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<!-- jQuery -->
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="container d-flex justify-content-center align-items-center min-vh-100">
|
||||||
|
<div class="card shadow p-4" style="width: 100%; max-width: 400px;">
|
||||||
|
<h3 class="text-center mb-4">Log In</h3>
|
||||||
|
<form id="loginForm">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">Username</label>
|
||||||
|
<input type="text" class="form-control" id="username" name="username" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Password</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<button type="submit" class="btn btn-primary w-100">Log In</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="mt-3 text-center" id="loader" style="display: none;">
|
||||||
|
<div class="spinner-border" role="status">
|
||||||
|
<span class="visually-hidden">Loading...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-3 text-center" id="response" style="display: none;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('#loginForm').on('submit', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
// Hide previous messages and show loader
|
||||||
|
$('#response').hide().empty();
|
||||||
|
$('#loader').show();
|
||||||
|
|
||||||
|
// Get form data
|
||||||
|
const username = $('#username').val();
|
||||||
|
const password = $('#password').val();
|
||||||
|
|
||||||
|
// Send POST request
|
||||||
|
$.ajax({
|
||||||
|
url: 'http://localhost:665/api/v1/auth/login',
|
||||||
|
method: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
data: JSON.stringify({ username, password }),
|
||||||
|
success: function (response) {
|
||||||
|
// Hide loader and display token
|
||||||
|
$('#loader').hide();
|
||||||
|
$('#response').html(`<div class="alert alert-success">Token: ${response.token}</div>`).show();
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
// Hide loader and show error message
|
||||||
|
$('#loader').hide();
|
||||||
|
$('#response').html('<div class="alert alert-danger">Failed to log in. Please try again.</div>').show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Allow pressing Enter to submit the form
|
||||||
|
$('#loginForm').on('keypress', function (event) {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault();
|
||||||
|
$('#loginForm').submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Bootstrap Bundle with Popper -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
x
Reference in New Issue
Block a user