diff --git a/api-test.http b/api-test.http index 3c0ddab..409d410 100644 --- a/api-test.http +++ b/api-test.http @@ -14,6 +14,9 @@ Content-Type: application/json "name": "테스터" } +### 2. DB에 저장된 토큰 조회 (디버깅) +GET http://localhost:8080/debug/tokens + ### 2. [Auth] 로그인 및 토큰 발급 # 주의: LoginAuthenticationFilter 구현에 따라 id, password를 헤더로 전송합니다. POST {{auth_host}}/login @@ -24,6 +27,7 @@ password: {{password}} > {% // 응답에서 accessToken을 추출하여 전역 변수 'auth_token'에 저장 client.global.set("auth_token", response.body.data.accessToken); + client.global.set("refresh_token", response.body.data.refreshToken); client.log("Acquired Token: " + response.body.data.accessToken); %} @@ -47,4 +51,15 @@ Authorization: Bearer {{auth_token}} ### 4. [Trip] 생성된 여행 목록 조회 (검증) GET {{trip_host}}/trips Content-Type: application/json -Authorization: Bearer {{auth_token}} \ No newline at end of file +Authorization: Bearer {{auth_token}} + +### 5. [Auth] 토큰 재발급 (Reissue) +POST {{auth_host}}/auth/reissue +Content-Type: application/json +Cookie: refreshToken={{refresh_token}} + +> {% + client.global.set("auth_token", response.body.data.accessToken); + client.global.set("refresh_token", response.body.data.refreshToken); + client.log("Reissued Token: " + response.body.data.accessToken); +%} \ No newline at end of file diff --git a/src/main/java/com/retrip/auth/application/config/SecurityConfig.java b/src/main/java/com/retrip/auth/application/config/SecurityConfig.java index d96fea1..3037104 100644 --- a/src/main/java/com/retrip/auth/application/config/SecurityConfig.java +++ b/src/main/java/com/retrip/auth/application/config/SecurityConfig.java @@ -2,6 +2,7 @@ import com.retrip.auth.application.in.CustomOAuth2UserService; import com.retrip.auth.application.in.MemberQueryService; +import com.retrip.auth.application.out.repository.RefreshTokenRepository; import com.retrip.auth.infra.adapter.in.rest.filter.JwtAuthenticationFilter; import com.retrip.auth.infra.adapter.in.rest.filter.LoginAuthenticationFilter; import lombok.RequiredArgsConstructor; @@ -32,7 +33,7 @@ public class SecurityConfig { private final CustomOAuth2UserService customOAuth2UserService; private final OAuth2LoginSuccessHandler oAuth2LoginSuccessHandler; - + private final RefreshTokenRepository refreshTokenRepository; private final JwtAuthenticationFilter jwtAuthenticationFilter; @Bean @@ -63,7 +64,7 @@ public LoginAuthenticationFilter loginAuthenticationFilter( JwtConfig jwtConfig, AuthenticationManager authenticationManager, JwtProvider jwtProvider) { - LoginAuthenticationFilter filter = new LoginAuthenticationFilter(jwtConfig, authenticationManager,jwtProvider); + LoginAuthenticationFilter filter = new LoginAuthenticationFilter(jwtConfig, authenticationManager,jwtProvider,refreshTokenRepository); return filter; } @@ -104,6 +105,7 @@ public SecurityFilterChain securityFilterChain( "/swagger-resources/**", "/webjars/**" ).permitAll() + .requestMatchers("/debug/**").permitAll() .anyRequest().authenticated() ); diff --git a/src/main/java/com/retrip/auth/domain/entity/RefreshToken.java b/src/main/java/com/retrip/auth/domain/entity/RefreshToken.java index b0b888c..01b3f31 100644 --- a/src/main/java/com/retrip/auth/domain/entity/RefreshToken.java +++ b/src/main/java/com/retrip/auth/domain/entity/RefreshToken.java @@ -1,5 +1,6 @@ package com.retrip.auth.domain.entity; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Table; @@ -16,11 +17,12 @@ public class RefreshToken { @Id + @Column(length = 1024) private String tokenValue; private String memberId; + @Column(length = 1024) private String authorities; - } \ No newline at end of file diff --git a/src/main/java/com/retrip/auth/infra/adapter/in/rest/filter/LoginAuthenticationFilter.java b/src/main/java/com/retrip/auth/infra/adapter/in/rest/filter/LoginAuthenticationFilter.java index 4b9b232..b2b5fdf 100644 --- a/src/main/java/com/retrip/auth/infra/adapter/in/rest/filter/LoginAuthenticationFilter.java +++ b/src/main/java/com/retrip/auth/infra/adapter/in/rest/filter/LoginAuthenticationFilter.java @@ -2,9 +2,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.retrip.auth.application.config.JwtConfig; -import com.retrip.auth.application.config.JwtProvider; // [추가] +import com.retrip.auth.application.config.JwtProvider; import com.retrip.auth.application.config.UsernamePasswordAuthentication; import com.retrip.auth.application.in.response.LoginResponse; +import com.retrip.auth.application.out.repository.RefreshTokenRepository; +import com.retrip.auth.domain.entity.RefreshToken; import com.retrip.auth.infra.adapter.in.rest.common.ApiResponse; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -15,10 +17,12 @@ import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; +import java.util.stream.Collectors; @Slf4j @RequiredArgsConstructor @@ -26,7 +30,8 @@ public class LoginAuthenticationFilter extends OncePerRequestFilter { private final JwtConfig jwtConfig; private final AuthenticationManager manager; - private final JwtProvider jwtProvider; // [추가] 토큰 생성기 주입 + private final JwtProvider jwtProvider; + private final RefreshTokenRepository refreshTokenRepository; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, @@ -37,7 +42,6 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse String password = request.getHeader("password"); if (!StringUtils.hasText(id) || !StringUtils.hasText(password)) { - // 값이 없으면 400 Bad Request가 더 적절하지만, 기존 로직 유지 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } @@ -47,10 +51,21 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse Authentication authentication = new UsernamePasswordAuthentication(id, password); Authentication auth = manager.authenticate(authentication); - // 3. [핵심 변경] JwtProvider를 통해 RSA 서명된 토큰 생성 - // 기존의 복잡한 generateToken 메서드 삭제 -> 위임 + // 3. 토큰 생성 LoginResponse.TokenResponse tokenResponse = jwtProvider.generateTokens(auth); + // 3-1. Refresh Token DB에 저장 + String authorities = auth.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.joining(",")); + + RefreshToken refreshToken = new RefreshToken( + tokenResponse.refreshToken(), + auth.getName(), // memberId + authorities + ); + refreshTokenRepository.save(refreshToken); + // 4. 응답 작성 ApiResponse result = ApiResponse.ok(tokenResponse); @@ -63,7 +78,6 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response.getWriter().flush(); } catch (AuthenticationException e) { - // 인증 실패 시 처리 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); } } @@ -71,7 +85,6 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse @Override protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { - // /login 경로만 필터 적용 (나머지는 통과 -> true 반환) return !request.getRequestURI().equals("/login"); } } \ No newline at end of file