diff --git a/build.gradle b/build.gradle index 34c1538..fbccb3b 100644 --- a/build.gradle +++ b/build.gradle @@ -41,8 +41,6 @@ dependencies { annotationProcessor 'com.querydsl:querydsl-apt:5.1.0:jakarta' annotationProcessor 'jakarta.persistence:jakarta.persistence-api' - implementation 'org.springframework.boot:spring-boot-starter-security' - implementation 'io.jsonwebtoken:jjwt-api:0.12.6' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6' diff --git a/src/main/java/com/retrip/trip/infra/adapter/in/presentation/filter/AuthenticationFilter.java b/src/main/java/com/retrip/trip/infra/adapter/in/presentation/filter/AuthenticationFilter.java index f0a9b2e..97e0cb3 100644 --- a/src/main/java/com/retrip/trip/infra/adapter/in/presentation/filter/AuthenticationFilter.java +++ b/src/main/java/com/retrip/trip/infra/adapter/in/presentation/filter/AuthenticationFilter.java @@ -1,41 +1,105 @@ -//package com.retrip.trip.infra.adapter.in.presentation.filter; -// -//import com.retrip.trip.application.in.request.context.UserContext; -//import jakarta.servlet.FilterChain; -//import jakarta.servlet.ServletException; -//import jakarta.servlet.http.HttpServletRequest; -//import jakarta.servlet.http.HttpServletResponse; -//import org.springframework.stereotype.Component; -//import org.springframework.web.filter.OncePerRequestFilter; -// -//import java.io.IOException; -// -//@Component -//public class AuthenticationFilter extends OncePerRequestFilter { -// -// @Override -// protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, -// FilterChain filterChain) throws ServletException, IOException { -// String path = request.getRequestURI(); -// String pathLowercase = path.toLowerCase(); -// -// // 제외할 URL 체크 -// if (path.equals("/") || -// pathLowercase.contains("swagger") || -// pathLowercase.contains("api-docs") || -// pathLowercase.contains("actuator") || -// pathLowercase.contains("robots.txt") || -// pathLowercase.contains("status-check")) { -// filterChain.doFilter(request, response); -// return; // 필터 종료 -// } -// -// String token = request.getHeader("Authorization"); -// if (token == null || token.isEmpty()) { -// response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); -// return; -// } -// request.setAttribute("userContext", UserContext.mockOf()); -// filterChain.doFilter(request, response); -// } -//} +package com.retrip.trip.infra.adapter.in.presentation.filter; + +import com.retrip.trip.application.in.request.context.UserContext; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; +import java.util.UUID; + +@Slf4j +@Component +public class AuthenticationFilter extends OncePerRequestFilter { + + @Value("${jwt.public-key}") + private String publicKeyString; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + String path = request.getRequestURI(); + String pathLowercase = path.toLowerCase(); + + if (path.equals("/") || + pathLowercase.contains("swagger") || + pathLowercase.contains("api-docs") || + pathLowercase.contains("actuator") || + pathLowercase.contains("robots.txt") || + pathLowercase.contains("status-check") || + pathLowercase.contains("/h2-console")) { + filterChain.doFilter(request, response); + return; + } + + String token = resolveToken(request); + if (token == null || token.isEmpty()) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + return; + } + + try { + Claims claims = getClaims(token); + + String subject = claims.getSubject(); + UUID memberId = UUID.fromString(subject); + + UserContext userContext = new UserContext( + memberId, + claims.get("nickname", String.class), + claims.get("email", String.class), + claims.get("name", String.class), + null, + 0 + ); + + request.setAttribute("userContext", userContext); + + filterChain.doFilter(request, response); + + } catch (Exception e) { + log.error("Token validation failed: {}", e.getMessage()); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + + } + } + + private String resolveToken(HttpServletRequest request) { + String bearerToken = request.getHeader("Authorization"); + if (bearerToken != null && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring(7); + } + return null; + } + + private Claims getClaims(String token) throws Exception { + String sanitizedKey = publicKeyString + .replace("-----BEGIN PUBLIC KEY-----", "") + .replace("-----END PUBLIC KEY-----", "") + .replaceAll("\\s", ""); + + byte[] publicBytes = Base64.getDecoder().decode(sanitizedKey); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PublicKey publicKey = keyFactory.generatePublic(keySpec); + + Jws jws = Jwts.parser() + .verifyWith(publicKey) + .build() + .parseSignedClaims(token); + + return jws.getPayload(); + } +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c268d6d..6348ec8 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -42,5 +42,4 @@ jwt: KucWwwIDAQAB -----END PUBLIC KEY----- -server: - port: 8081 +