diff --git a/apigateway/src/main/java/com/springbootmicroservices/apigateway/filter/JwtAuthenticationFilter.java b/apigateway/src/main/java/com/springbootmicroservices/apigateway/filter/JwtAuthenticationFilter.java index 4344e00..085c7eb 100644 --- a/apigateway/src/main/java/com/springbootmicroservices/apigateway/filter/JwtAuthenticationFilter.java +++ b/apigateway/src/main/java/com/springbootmicroservices/apigateway/filter/JwtAuthenticationFilter.java @@ -55,11 +55,13 @@ public GatewayFilter apply(Config config) { return Mono.fromCallable(() -> { userServiceClient.validateToken(jwt); + log.debug("Token validation succeeded for path: {}", path); return true; }) .subscribeOn(Schedulers.boundedElastic()) .flatMap(valid -> chain.filter(exchange)) .onErrorResume(e -> { + log.error("Token validation failed for path: {}", path, e); if (e instanceof FeignException.Unauthorized || e instanceof FeignException.Forbidden) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); } else { @@ -68,7 +70,7 @@ public GatewayFilter apply(Config config) { return exchange.getResponse().setComplete(); }); } - + log.warn("Missing or invalid Authorization header for path: {}", path); return chain.filter(exchange); }; } diff --git a/productservice/src/main/java/com/springbootmicroservices/productservice/config/SecurityConfig.java b/productservice/src/main/java/com/springbootmicroservices/productservice/config/SecurityConfig.java index 6c2ce43..95896cf 100644 --- a/productservice/src/main/java/com/springbootmicroservices/productservice/config/SecurityConfig.java +++ b/productservice/src/main/java/com/springbootmicroservices/productservice/config/SecurityConfig.java @@ -1,9 +1,9 @@ package com.springbootmicroservices.productservice.config; -import com.springbootmicroservices.productservice.client.UserServiceClient; import com.springbootmicroservices.productservice.filter.CustomBearerTokenAuthenticationFilter; import com.springbootmicroservices.productservice.security.CustomAuthenticationEntryPoint; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; @@ -28,10 +28,9 @@ @EnableWebSecurity @RequiredArgsConstructor @EnableMethodSecurity +@Slf4j public class SecurityConfig { - private final UserServiceClient userServiceClient; - @Bean protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); @@ -44,6 +43,8 @@ public SecurityFilterChain filterChain( final CustomAuthenticationEntryPoint customAuthenticationEntryPoint ) throws Exception { + log.debug("Configuring Security Filter Chain"); + httpSecurity .exceptionHandling(customizer -> customizer.authenticationEntryPoint(customAuthenticationEntryPoint)) .cors(customizer -> customizer.configurationSource(corsConfigurationSource())) @@ -54,6 +55,8 @@ public SecurityFilterChain filterChain( .sessionManagement(customizer -> customizer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .addFilterBefore(customBearerTokenAuthenticationFilter, BearerTokenAuthenticationFilter.class); + log.debug("CustomBearerTokenAuthenticationFilter added to the filter chain"); + return httpSecurity.build(); } @@ -71,5 +74,5 @@ private CorsConfigurationSource corsConfigurationSource() { public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } -} +} \ No newline at end of file diff --git a/productservice/src/main/java/com/springbootmicroservices/productservice/filter/CustomBearerTokenAuthenticationFilter.java b/productservice/src/main/java/com/springbootmicroservices/productservice/filter/CustomBearerTokenAuthenticationFilter.java index 954c95c..6192bb8 100644 --- a/productservice/src/main/java/com/springbootmicroservices/productservice/filter/CustomBearerTokenAuthenticationFilter.java +++ b/productservice/src/main/java/com/springbootmicroservices/productservice/filter/CustomBearerTokenAuthenticationFilter.java @@ -2,6 +2,7 @@ import com.springbootmicroservices.productservice.client.UserServiceClient; import com.springbootmicroservices.productservice.model.auth.Token; +import feign.FeignException; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -10,8 +11,11 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; import java.io.IOException; @@ -27,15 +31,46 @@ protected void doFilterInternal(@NonNull final HttpServletRequest httpServletReq @NonNull final HttpServletResponse httpServletResponse, @NonNull final FilterChain filterChain) throws ServletException, IOException { - log.debug("API Request was secured with Security!"); + log.debug("CustomBearerTokenAuthenticationFilter: Request received for URI: {}", httpServletRequest.getRequestURI()); final String authorizationHeader = httpServletRequest.getHeader(HttpHeaders.AUTHORIZATION); if (Token.isBearerToken(authorizationHeader)) { final String jwt = Token.getJwt(authorizationHeader); - userServiceClient.validateToken(jwt); - } - filterChain.doFilter(httpServletRequest, httpServletResponse); + // Use Mono.fromCallable for async processing + Mono.fromCallable(() -> { + userServiceClient.validateToken(jwt); + log.debug("Token validation succeeded for request: {}", httpServletRequest.getRequestURI()); + return true; + }) + .subscribeOn(Schedulers.boundedElastic()) + .flatMap(valid -> { + try { + filterChain.doFilter(httpServletRequest, httpServletResponse); + } catch (IOException | ServletException e) { + throw new RuntimeException(e); + } + return Mono.empty(); + }) + .onErrorResume(e -> { + log.error("Token validation failed for request: {}", httpServletRequest.getRequestURI(), e); + try { + if (e instanceof FeignException.Unauthorized || e instanceof FeignException.Forbidden) { + httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value()); + } else { + httpServletResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); + } + httpServletResponse.getWriter().write(e.getMessage()); + } catch (IOException ex) { + log.error("Error writing response", ex); + } + return Mono.empty(); + }) + .block(); + } else { + log.warn("Missing or invalid Authorization header for request: {}", httpServletRequest.getRequestURI()); + filterChain.doFilter(httpServletRequest, httpServletResponse); + } } -} +} \ No newline at end of file diff --git a/userservice/src/main/java/com/springbootmicroservices/userservice/service/impl/TokenServiceImpl.java b/userservice/src/main/java/com/springbootmicroservices/userservice/service/impl/TokenServiceImpl.java index e6a78c7..08d283e 100644 --- a/userservice/src/main/java/com/springbootmicroservices/userservice/service/impl/TokenServiceImpl.java +++ b/userservice/src/main/java/com/springbootmicroservices/userservice/service/impl/TokenServiceImpl.java @@ -8,21 +8,22 @@ import com.springbootmicroservices.userservice.model.user.enums.UserType; import com.springbootmicroservices.userservice.service.InvalidTokenService; import com.springbootmicroservices.userservice.service.TokenService; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jws; -import io.jsonwebtoken.JwsHeader; -import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.*; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.time.DateUtils; +import org.springframework.http.HttpStatus; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.stereotype.Service; +import org.springframework.web.server.ResponseStatusException; import java.util.*; @Service @RequiredArgsConstructor +@Slf4j public class TokenServiceImpl implements TokenService { private final TokenConfigurationParameter tokenConfigurationParameter; @@ -141,10 +142,31 @@ public UsernamePasswordAuthenticationToken getAuthentication(final String token) } public void verifyAndValidate(final String jwt) { - Jwts.parser() + + try { + Jws claimsJws = Jwts.parser() .verifyWith(tokenConfigurationParameter.getPublicKey()) .build() .parseSignedClaims(jwt); + + // Log the claims for debugging purposes + Claims claims = claimsJws.getBody(); + log.info("Token claims: {}", claims); + + // Additional checks (e.g., expiration, issuer, etc.) + if (claims.getExpiration().before(new Date())) { + throw new JwtException("Token has expired"); + } + + log.info("Token is valid"); + + } catch (JwtException e) { + log.error("Invalid JWT token", e); + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Invalid JWT token", e); + } catch (Exception e) { + log.error("Error validating token", e); + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Error validating token", e); + } } @Override