Added basic JWT authentication

working state
This commit is contained in:
Max W. 2024-09-09 17:15:08 +02:00
parent 656e0c0e7a
commit 3641dbcdf9
7 changed files with 95 additions and 4 deletions

View File

@ -39,10 +39,11 @@ public class SecurityConfig {
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // No session will be created by Spring Security .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // No session will be created by Spring Security
) )
.authorizeHttpRequests(authorize -> authorize .authorizeHttpRequests(authorize -> authorize
.requestMatchers("/api/v1/**").authenticated() // Secure all /api/v1/** routes .requestMatchers("/api/v1/auth/login").permitAll() // Allow access to login endpoint
.requestMatchers("/api/v1/**").authenticated() // Secure all other /api/v1/** routes
.anyRequest().permitAll() // All other requests are allowed without authentication .anyRequest().permitAll() // All other requests are allowed without authentication
) )
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) // Apply JWT filter .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) // Ensure JWT filter is applied after login is allowed
.logout(LogoutConfigurer::permitAll) .logout(LogoutConfigurer::permitAll)
.rememberMe(Customizer.withDefaults()); .rememberMe(Customizer.withDefaults());

View File

@ -0,0 +1,10 @@
package de.w665.biblenotes.rest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v1")
public abstract class ApiRestController {
}

View File

@ -0,0 +1,44 @@
package de.w665.biblenotes.rest.mappings;
import de.w665.biblenotes.rest.ro.AuthenticationRequest;
import de.w665.biblenotes.service.AuthenticationService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/v1/auth")
public class AuthenticationController {
private final AuthenticationService authenticationService;
public AuthenticationController(AuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
@PostMapping("/login")
public ResponseEntity<Object> createAuthenticationToken(@RequestBody AuthenticationRequest authenticationRequest, HttpServletRequest request) {
log.debug("Received AuthenticationRequest for username: " + authenticationRequest.getUsername());
String token = authenticationService.authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword(), request.getRemoteAddr());
Map<String, Object> response = new HashMap<>();
response.put("token", token);
response.put("success", token != null);
if(token == null) {
log.debug("Authentication failed for username: " + authenticationRequest.getUsername());
return new ResponseEntity<>(response, HttpStatus.UNAUTHORIZED);
}
return new ResponseEntity<>(response, HttpStatus.OK);
}
}

View File

@ -0,0 +1,14 @@
package de.w665.biblenotes.rest.mappings;
import de.w665.biblenotes.rest.ApiRestController;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController extends ApiRestController {
@GetMapping("/test")
public String test() {
return "Test";
}
}

View File

@ -0,0 +1,16 @@
package de.w665.biblenotes.rest.ro;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@NoArgsConstructor
@Getter
@Setter
@ToString
public class AuthenticationRequest {
private String username;
private String password;
}

View File

@ -28,13 +28,19 @@ import java.util.List;
public class JwtAuthenticationFilter extends OncePerRequestFilter { public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final AuthenticationService authenticationService; private final AuthenticationService authenticationService;
private final RequestMatcher requestMatcher = new AntPathRequestMatcher("/api/v1/secure/**"); private final RequestMatcher requestMatcher = new AntPathRequestMatcher("/api/v1/**"); // The filter will verify authentication for all requests starting with /api/v1/
@Override @Override
protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException { protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
logger.debug("Filtering request: " + request.getRequestURI()); logger.debug("Filtering request: " + request.getRequestURI());
if ("/api/v1/auth/login".equals(request.getRequestURI())) {
logger.debug("Login request detected. Skipping JWT authentication.");
filterChain.doFilter(request, response);
return;
}
if(!requestMatcher.matches(request)) { if(!requestMatcher.matches(request)) {
logger.debug("Request does not match the secure path. Skipping JWT authentication."); logger.debug("Request does not match the secure path. Skipping JWT authentication.");
filterChain.doFilter(request, response); filterChain.doFilter(request, response);

View File

@ -8,7 +8,7 @@ rethinkdb.port=28015
rethinkdb.database=biblenotes rethinkdb.database=biblenotes
# Logging # Logging
logging.level.de.w665.biblenotes=INFO logging.level.de.w665.biblenotes=DEBUG
# Static path # Static path
spring.web.resources.static-locations=classpath:/static/browser/ spring.web.resources.static-locations=classpath:/static/browser/