11package com .postdm .backend .domain .auth .application ;
22
3+ import com .postdm .backend .domain .auth .domain .RefreshToken ;
34import com .postdm .backend .domain .auth .dto .SignInRequestDto ;
45import com .postdm .backend .domain .auth .dto .SignUpRequestDto ;
6+ import com .postdm .backend .domain .auth .repository .RefreshTokenRepository ;
57import com .postdm .backend .domain .email .domain .entity .CertificationEntity ;
68import com .postdm .backend .domain .email .domain .repository .CertificationRepository ;
79import com .postdm .backend .domain .member .domain .entity .Member ;
1416import jakarta .servlet .http .Cookie ;
1517import jakarta .servlet .http .HttpServletResponse ;
1618import org .springframework .beans .factory .annotation .Value ;
19+ import org .springframework .security .core .Authentication ;
20+ import org .springframework .security .core .GrantedAuthority ;
1721import org .springframework .security .crypto .bcrypt .BCryptPasswordEncoder ;
1822import org .springframework .stereotype .Service ;
1923
24+ import java .time .LocalDateTime ;
25+
2026@ Service
2127public class AuthService { // 로그인 및 회원가입 서비스
2228
@@ -26,6 +32,7 @@ public class AuthService { // 로그인 및 회원가입 서비스
2632 private final JwtProvider jwtProvider ;
2733 private final int refreshedMS ;
2834 private final TokenBlacklistService tokenBlacklistService ;
35+ private final RefreshTokenRepository refreshTokenRepository ;
2936
3037 // 생성자 주입 방식
3138 public AuthService (
@@ -34,13 +41,15 @@ public AuthService(
3441 BCryptPasswordEncoder bCryptPasswordEncoder ,
3542 JwtProvider jwtProvider ,
3643 @ Value ("${jwt.refreshedMs}" ) int refreshedMS ,
37- TokenBlacklistService tokenBlacklistService ) {
44+ TokenBlacklistService tokenBlacklistService ,
45+ RefreshTokenRepository refreshTokenRepository ) {
3846 this .memberRepository = memberRepository ;
3947 this .certificationRepository = certificationRepository ;
4048 this .bCryptPasswordEncoder = bCryptPasswordEncoder ;
4149 this .jwtProvider = jwtProvider ;
4250 this .refreshedMS = refreshedMS ;
4351 this .tokenBlacklistService = tokenBlacklistService ;
52+ this .refreshTokenRepository = refreshTokenRepository ;
4453 }
4554
4655
@@ -126,8 +135,16 @@ public TokenInfo signIn(SignInRequestDto signInRequestDto, HttpServletResponse r
126135 TokenInfo token = jwtProvider .generateToken (username , role ); // 로그인이 완료되면 토큰 생성
127136 String refreshToken = jwtProvider .generateRefreshToken (username , role );
128137
129- response . addCookie ( createCookie ( refreshToken )); // 쿠키에 refresh 토큰 담음
138+ LocalDateTime expiration = jwtProvider . getExpiration ( refreshToken );
130139
140+ // 로그인 성공 후, Refresh Token을 DB에 저장
141+ refreshTokenRepository .save (new RefreshToken (
142+ username ,
143+ refreshToken ,
144+ expiration
145+ ));
146+
147+ response .addCookie (createCookie (refreshToken )); // 쿠키에 refresh 토큰 담음
131148
132149 return token ; // 응답 body에는 access 토큰 반환
133150 }
@@ -142,11 +159,62 @@ private Cookie createCookie(String value) { // 쿠키 생성 메소드
142159 }
143160
144161 public void signOut (String accessToken ) {
145- long expireMillis = jwtProvider .getExpiration (accessToken );
146- boolean isNewlySaved = tokenBlacklistService .saveIfNotExists (accessToken , expireMillis );
162+ LocalDateTime expiration = jwtProvider .getExpiration (accessToken );
163+ boolean isNewlySaved = tokenBlacklistService .saveIfNotExists (accessToken , expiration );
147164
148165 if (!isNewlySaved ) {
149166 throw new CustomException (ErrorCode .ALREADY_SIGN_OUT );
150167 }
168+
169+ Authentication authentication = jwtProvider .getAuthentication (accessToken );
170+ Member member = (Member ) authentication .getPrincipal ();
171+ String username = member .getUsername ();
172+
173+ // Refresh Token 삭제
174+ refreshTokenRepository .findById (username )
175+ .ifPresent (refreshTokenRepository ::delete );
176+ }
177+
178+ public TokenInfo reissue (String oldRefreshToken , HttpServletResponse response ) {
179+ if (!jwtProvider .validateToken (oldRefreshToken )) {
180+ throw new CustomException (ErrorCode .INVALID_REFRESH_TOKEN );
181+ }
182+
183+ Authentication authentication = jwtProvider .getAuthentication (oldRefreshToken );
184+ Member member = (Member ) authentication .getPrincipal ();
185+ String username = member .getUsername ();
186+
187+ RefreshToken saved = refreshTokenRepository .findById (username )
188+ .orElseThrow (() -> new CustomException (ErrorCode .REFRESH_TOKEN_NOT_FOUND ));
189+
190+ if (!saved .getRefreshToken ().equals (oldRefreshToken )) {
191+ throw new CustomException (ErrorCode .INVALID_REFRESH_TOKEN );
192+ }
193+
194+ // Access Token 재발급
195+ String role = extractRole (authentication );
196+ String newAccessToken = jwtProvider .generateAccessToken (username , role );
197+
198+ // Refresh Token 회전(Rotation)
199+ String newRefreshToken = jwtProvider .generateRefreshToken (username , role );
200+ LocalDateTime expiration = jwtProvider .getExpiration (newRefreshToken );
201+ saved .update (newRefreshToken , expiration );
202+ refreshTokenRepository .save (saved );
203+
204+ response .addCookie (createCookie (newRefreshToken ));
205+
206+ return TokenInfo .builder ()
207+ .grantType ("Bearer" )
208+ .accessToken (newAccessToken )
209+ .role (role )
210+ .build ();
211+ }
212+
213+ private String extractRole (Authentication authentication ) {
214+ return authentication .getAuthorities ().stream ()
215+ .findFirst ()
216+ .map (GrantedAuthority ::getAuthority )
217+ .orElseThrow (() -> new CustomException (ErrorCode .ROLE_NOT_FOUND ))
218+ .replace ("ROLE_" , "" );
151219 }
152- }
220+ }
0 commit comments