diff --git a/auth-migration/src/main/java/com/example/auth/security/JwtAuthenticationFilter.java b/auth-migration/src/main/java/com/example/auth/security/JwtAuthenticationFilter.java new file mode 100644 index 0000000..7ad051d --- /dev/null +++ b/auth-migration/src/main/java/com/example/auth/security/JwtAuthenticationFilter.java @@ -0,0 +1,69 @@ +package com.example.auth.security; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.List; + +/** + * JWT Authentication Filter - preserves AUTH-010, AUTH-012 invariants. + * CRITICAL: Tokens only from Authorization header (never URL query). + */ +@Component +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private static final String AUTHORIZATION_HEADER = "Authorization"; + private static final String BEARER_PREFIX = "Bearer "; + + private final JwtTokenProvider jwtTokenProvider; + + public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) { + this.jwtTokenProvider = jwtTokenProvider; + } + + @Override + protected void doFilterInternal( + HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + + String token = extractToken(request); + + if (token != null && jwtTokenProvider.validateToken(token)) { + JwtTokenProvider.JwtClaims claims = jwtTokenProvider.getClaims(token); + + List authorities = claims.roles().stream() + .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) + .toList(); + + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken(claims.subject(), null, authorities); + + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + filterChain.doFilter(request, response); + } + + private String extractToken(HttpServletRequest request) { + String bearerToken = request.getHeader(AUTHORIZATION_HEADER); + if (bearerToken != null && bearerToken.startsWith(BEARER_PREFIX)) { + return bearerToken.substring(BEARER_PREFIX.length()); + } + return null; + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) { + String path = request.getServletPath(); + return path.startsWith("/auth/login") || path.startsWith("/auth/refresh"); + } +}