- Added JwtAuthenticationFilter
- Added UploadHistory - Added secure endpoints to SecurityConfig
This commit is contained in:
parent
59e0fb1b48
commit
5f681a7a1b
@ -1,32 +1,45 @@
|
||||
package de.w665.sharepulse.config;
|
||||
|
||||
import de.w665.sharepulse.rest.security.JwtAuthenticationFilter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.Customizer;
|
||||
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.configurers.LogoutConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
private final JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||
|
||||
public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
|
||||
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf(csrf -> csrf.ignoringRequestMatchers("/api/v1/**")) // Disable CSRF for API routes
|
||||
.sessionManagement(sessionManagement -> sessionManagement
|
||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // No session will be created by Spring Security
|
||||
)
|
||||
.authorizeHttpRequests(authorize -> authorize
|
||||
.requestMatchers("/api/v1/secure/**").authenticated()
|
||||
.anyRequest().permitAll()
|
||||
)
|
||||
.formLogin(formLogin -> formLogin
|
||||
.loginPage("/management/login")
|
||||
.permitAll()
|
||||
.requestMatchers("/api/v1/secure/**").authenticated() // Secure these endpoints
|
||||
.anyRequest().permitAll() // All other requests are allowed without authentication
|
||||
)
|
||||
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) // Apply JWT filter
|
||||
.logout(LogoutConfigurer::permitAll)
|
||||
.csrf(csrf -> csrf
|
||||
.ignoringRequestMatchers("/api/v1/**") // Disable CSRF for /api/**
|
||||
)
|
||||
.rememberMe(Customizer.withDefaults());
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// TODO: Fix the security configuration to allow public access to unsecured endpoints
|
||||
|
@ -0,0 +1,20 @@
|
||||
package de.w665.sharepulse.rest.mappings;
|
||||
|
||||
import de.w665.sharepulse.rest.SecureApiRestController;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class UploadHistory extends SecureApiRestController {
|
||||
|
||||
@GetMapping("/test")
|
||||
public ResponseEntity<Object> test(HttpServletRequest request) {
|
||||
log.debug("Received test request");
|
||||
return ResponseEntity.ok("Test successful");
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package de.w665.sharepulse.rest.security;
|
||||
|
||||
import de.w665.sharepulse.service.AuthenticationService;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
private final AuthenticationService authenticationService;
|
||||
private final RequestMatcher requestMatcher = new AntPathRequestMatcher("/api/v1/secure/**");
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
|
||||
|
||||
logger.debug("Filtering request: " + request.getRequestURI());
|
||||
|
||||
if(!requestMatcher.matches(request)) {
|
||||
logger.debug("Request does not match the secure path. Skipping JWT authentication.");
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String jwt = getJwtFromRequest(request);
|
||||
if (jwt != null && authenticationService.validateToken(jwt)) {
|
||||
String username = authenticationService.extractSubject(jwt);
|
||||
// Extract the role from the JWT and set it to Spring AuthenticationContext for access control
|
||||
String role = authenticationService.getClaimValue(jwt, "role", String.class);
|
||||
List<GrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role));
|
||||
|
||||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, null, authorities);
|
||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
// SUCCESSFUL AUTHENTICATION
|
||||
filterChain.doFilter(request, response);
|
||||
} else {
|
||||
logger.warn("Unauthorized: Authentication token is missing or invalid.");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
logger.warn("Could not set user authentication in security context. An error occurred during JWT processing.", ex);
|
||||
}
|
||||
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
private String getJwtFromRequest(HttpServletRequest request) {
|
||||
String bearerToken = request.getHeader("Authorization");
|
||||
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
|
||||
return bearerToken.substring(7);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -57,9 +57,10 @@ public class AuthenticationService {
|
||||
Date expiryDate = new Date(nowMillis + expirationTime);
|
||||
|
||||
return Jwts.builder()
|
||||
.subject(username.getUsername())
|
||||
.subject("SharePulse Authentication Token")
|
||||
.issuedAt(now)
|
||||
.claim("role", username.getRole())
|
||||
.claim("username", username.getUsername())
|
||||
.expiration(expiryDate)
|
||||
.signWith(secretKey)
|
||||
.compact();
|
||||
@ -83,8 +84,7 @@ public class AuthenticationService {
|
||||
* Retrieves a typed claim from the JWT.
|
||||
* @param token the JWT from which to extract the claim
|
||||
* @param claimName the name of the claim to retrieve
|
||||
* @param claimType the Class object of the type T of the claim
|
||||
* @param <T> the expected type of the claim value
|
||||
* @param claimType the Class object of <T> the expected type of the claim value
|
||||
* @return the value of the specified claim as type T, or null if not found or in case of an error
|
||||
* Usage example: getClaimValue(token, "role", String.class)
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user