Skip to content

Commit 3b5dce2

Browse files
authored
Feature/53 volunteer signin signout 기능(#62)
* refactor(package): 패키지 이동 * style(ApiResponse): 불필요한 개행 제거 * feat(application.yml): front, back root url 추가 * refactor(package): 패키지 이동 * feat(cookie): 쿠키 삭제(덮어씌우기), 토큰 타입 추가(SIGNOUT), 파일명 변경 * feat(RefreshToken): 리프레시 토큰 삭제 수정 - 리프레시 토큰 삭제의 파라미터를 엑세스 토큰에서 사용자 ID로 변경 - 리프레시 토큰이 존재하지 않아도 로그아웃이 가능하도록 로직 수정 - 리프레시 토큰이 없는 상태에서 로그아웃이 불가능한 것은 비정상적인 동작으로 판단 - 레포지토리에서 사용자 ID로 리프레시 토큰을 찾는 메서드 추가 * feat(SignOut): 로그아웃 로직 구현 및 리프레시 토큰 삭제 처리 - 로그아웃 시 액세스 토큰 쿠키를 삭제(덮어씌우기)하는 로직 추가 - 리프레시 토큰을 사용자 ID를 기반으로 삭제하도록 처리 * test(CookieService): 액세스 토큰 설정 및 삭제 로직 테스트 추가 - 액세스 토큰 설정 동작 검증 - 토큰 값, HttpOnly, Secure, Path 등 쿠키 속성 확인 - 액세스 토큰 삭제 동작 검증 - 쿠키 값에 SIGNOUT이 포함된 것을 확인 - 쿠키 값이 삭제(Max-Age=0)되었는지 확인 * feat(OAuth): OAuth URL 생성 서비스 구현 - OAuth 인증 URL을 동적으로 생성 - 백엔드 Root URL과 OAuth Provider 이름을 조합하여 URL 생성 - /oauth2/authorization/{provider} 형태의 URL 반환 로직 구현 - 환경에 따라 백엔드 Root URL을 설정할 수 있도록 @value("${app.back-url}") 적용 * test(GenerateOAuthUrlService): OAuth URL 생성 로직 테스트 추가 - OAuth Provider에 따라 올바른 URL이 생성되는지 검증 * test(SignOutVolunteerService): 로그아웃 동작 테스트 추가 - 테스트 후 레디스 delete all - 액세스 토큰 쿠키 삭제 및 리프레시 토큰 제거 검증 - 리프레시 토큰이 없는 경우에도 예외가 발생하지 않는지 확인 - 테스트 시 MockHttpServletResponse를 활용하여 쿠키 동작 검증 - 쿠키 name이 중복으로 저장되는 문제 회피 (트러블슈팅 기록) * feat(application.yml): front/back url 변수 처리 * feat(VolunteerSignController): OAuth 로그인 및 로그아웃 API 추가 - OAuth 로그인 URL 생성 API 추가 - [POST] /api/volunteer/sign-in/oauth/{oauthProvider} - 지원되는 OAuth 제공자에 따라 리다이렉트 URL 생성 및 반환 - 지원되지 않는 OAuth 제공자는 `BadRequestException` 처리 - 로그아웃 API 추가 - [POST] /api/volunteer/sign-out - 액세스 토큰 쿠키 삭제 및 리프레시 토큰 제거 - 성공 시 표준 응답 형식(`ApiResponse.ok`)으로 메시지 반환 - GenerateOAuthUrlUseCase를 사용해 OAuth URL 동적 생성 - SignOutVolunteerUseCase를 사용해 로그아웃 로직 처리 * feat(application.yml): naver-redirect-uri 변수 처리 * CICD/ 환경 변수 추가 (#61) * cicd: 환경변수 추가 - OAuth 연동을 위한 환경변수 추가 * refactor(package): C/Q 구분 삭제 * feat(application.yml): UTF-8, KR 설정 * test(VolunteerSignController): OAuth 로그인 및 로그아웃 통합 테스트 추가 - 유효한 OAuth 제공자로 로그인 URL 생성 테스트 추가 - 지원되지 않는 OAuth 제공자에 대해 400 에러 반환 테스트 추가 - 로그아웃 요청 시 성공 메시지 반환 테스트 추가 * test(@transactional): 불필요한 트랜잭션 어노테이션 삭제 * refactor: 불필요한 어노테이션 삭제 * refactor(package): 패키지 이동 * style(ApiResponse): 불필요한 개행 제거 * feat(application.yml): front, back root url 추가 * refactor(package): 패키지 이동 * feat(cookie): 쿠키 삭제(덮어씌우기), 토큰 타입 추가(SIGNOUT), 파일명 변경 * feat(RefreshToken): 리프레시 토큰 삭제 수정 - 리프레시 토큰 삭제의 파라미터를 엑세스 토큰에서 사용자 ID로 변경 - 리프레시 토큰이 존재하지 않아도 로그아웃이 가능하도록 로직 수정 - 리프레시 토큰이 없는 상태에서 로그아웃이 불가능한 것은 비정상적인 동작으로 판단 - 레포지토리에서 사용자 ID로 리프레시 토큰을 찾는 메서드 추가 * feat(SignOut): 로그아웃 로직 구현 및 리프레시 토큰 삭제 처리 - 로그아웃 시 액세스 토큰 쿠키를 삭제(덮어씌우기)하는 로직 추가 - 리프레시 토큰을 사용자 ID를 기반으로 삭제하도록 처리 * test(CookieService): 액세스 토큰 설정 및 삭제 로직 테스트 추가 - 액세스 토큰 설정 동작 검증 - 토큰 값, HttpOnly, Secure, Path 등 쿠키 속성 확인 - 액세스 토큰 삭제 동작 검증 - 쿠키 값에 SIGNOUT이 포함된 것을 확인 - 쿠키 값이 삭제(Max-Age=0)되었는지 확인 * feat(OAuth): OAuth URL 생성 서비스 구현 - OAuth 인증 URL을 동적으로 생성 - 백엔드 Root URL과 OAuth Provider 이름을 조합하여 URL 생성 - /oauth2/authorization/{provider} 형태의 URL 반환 로직 구현 - 환경에 따라 백엔드 Root URL을 설정할 수 있도록 @value("${app.back-url}") 적용 * test(GenerateOAuthUrlService): OAuth URL 생성 로직 테스트 추가 - OAuth Provider에 따라 올바른 URL이 생성되는지 검증 * test(SignOutVolunteerService): 로그아웃 동작 테스트 추가 - 테스트 후 레디스 delete all - 액세스 토큰 쿠키 삭제 및 리프레시 토큰 제거 검증 - 리프레시 토큰이 없는 경우에도 예외가 발생하지 않는지 확인 - 테스트 시 MockHttpServletResponse를 활용하여 쿠키 동작 검증 - 쿠키 name이 중복으로 저장되는 문제 회피 (트러블슈팅 기록) * feat(application.yml): front/back url 변수 처리 * feat(VolunteerSignController): OAuth 로그인 및 로그아웃 API 추가 - OAuth 로그인 URL 생성 API 추가 - [POST] /api/volunteer/sign-in/oauth/{oauthProvider} - 지원되는 OAuth 제공자에 따라 리다이렉트 URL 생성 및 반환 - 지원되지 않는 OAuth 제공자는 `BadRequestException` 처리 - 로그아웃 API 추가 - [POST] /api/volunteer/sign-out - 액세스 토큰 쿠키 삭제 및 리프레시 토큰 제거 - 성공 시 표준 응답 형식(`ApiResponse.ok`)으로 메시지 반환 - GenerateOAuthUrlUseCase를 사용해 OAuth URL 동적 생성 - SignOutVolunteerUseCase를 사용해 로그아웃 로직 처리 * feat(application.yml): naver-redirect-uri 변수 처리 * refactor(package): C/Q 구분 삭제 * feat(application.yml): UTF-8, KR 설정 * test(VolunteerSignController): OAuth 로그인 및 로그아웃 통합 테스트 추가 - 유효한 OAuth 제공자로 로그인 URL 생성 테스트 추가 - 지원되지 않는 OAuth 제공자에 대해 400 에러 반환 테스트 추가 - 로그아웃 요청 시 성공 메시지 반환 테스트 추가 * test(@transactional): 불필요한 트랜잭션 어노테이션 삭제 * refactor: 불필요한 어노테이션 삭제 * refactor: 불필요한 import 제거
1 parent ab20dd4 commit 3b5dce2

35 files changed

+490
-82
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.somemore.auth.cookie;
2+
3+
import com.somemore.auth.jwt.domain.TokenType;
4+
import jakarta.servlet.http.HttpServletResponse;
5+
import lombok.RequiredArgsConstructor;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.springframework.http.ResponseCookie;
8+
import org.springframework.stereotype.Service;
9+
10+
@Service
11+
@RequiredArgsConstructor
12+
@Slf4j
13+
public class CookieService implements CookieUseCase {
14+
15+
@Override
16+
public void setAccessToken(HttpServletResponse response, String value) {
17+
ResponseCookie cookie = generateCookie(TokenType.ACCESS, value);
18+
response.addHeader("Set-Cookie", cookie.toString());
19+
}
20+
21+
@Override
22+
public void deleteAccessToken(HttpServletResponse response) {
23+
ResponseCookie cookie = generateCookie(TokenType.SIGNOUT, TokenType.SIGNOUT.name());
24+
response.addHeader("Set-Cookie", cookie.toString());
25+
}
26+
27+
private static ResponseCookie generateCookie(TokenType tokenType, String value) {
28+
return ResponseCookie.from(TokenType.ACCESS.name(), value) // 덮어쓰기 위해서 고정 값
29+
.httpOnly(true)
30+
.secure(true)
31+
.path("/")
32+
.maxAge(tokenType.getPeriodInSeconds())
33+
.sameSite("Lax")
34+
.build();
35+
}
36+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.somemore.auth.cookie;
2+
3+
import jakarta.servlet.http.HttpServletResponse;
4+
5+
public interface CookieUseCase {
6+
void setAccessToken(HttpServletResponse response, String value);
7+
8+
void deleteAccessToken(HttpServletResponse response);
9+
}

src/main/java/com/somemore/auth/cookie/SetCookieService.java

Lines changed: 0 additions & 30 deletions
This file was deleted.

src/main/java/com/somemore/auth/cookie/SetCookieUseCase.java

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/main/java/com/somemore/auth/jwt/domain/TokenType.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
@Getter
66
public enum TokenType {
77
ACCESS(1000 * 60 * 30),
8-
REFRESH(1000 * 60 * 60 * 24 * 7);
8+
REFRESH(1000 * 60 * 60 * 24 * 7),
9+
SIGNOUT(0);
910

1011
private final int period;
1112

src/main/java/com/somemore/auth/jwt/filter/JwtAuthFilter.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.somemore.auth.jwt.filter;
22

3-
import com.somemore.auth.UserRole;
43
import com.somemore.auth.jwt.domain.EncodedToken;
54
import com.somemore.auth.jwt.exception.JwtErrorType;
65
import com.somemore.auth.jwt.exception.JwtException;

src/main/java/com/somemore/auth/UserRole.java renamed to src/main/java/com/somemore/auth/jwt/filter/UserRole.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.somemore.auth;
1+
package com.somemore.auth.jwt.filter;
22

33
public enum UserRole {
44
VOLUNTEER,

src/main/java/com/somemore/auth/jwt/refresh/manager/RedisRefreshTokenManager.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,8 @@ public void save(RefreshToken refreshToken) {
2626
}
2727

2828
@Override
29-
public void removeRefreshToken(EncodedToken accessToken) {
30-
RefreshToken refreshToken = refreshTokenRepository.findByAccessToken(accessToken.value())
31-
.orElseThrow(() -> new JwtException(JwtErrorType.EXPIRED_TOKEN));
32-
33-
refreshTokenRepository.delete(refreshToken);
29+
public void removeRefreshToken(String userId) {
30+
refreshTokenRepository.findByUserId(userId)
31+
.ifPresent(refreshTokenRepository::delete);
3432
}
3533
}

src/main/java/com/somemore/auth/jwt/refresh/manager/RefreshTokenManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ public interface RefreshTokenManager {
88

99
void save(RefreshToken refreshToken);
1010

11-
void removeRefreshToken(EncodedToken accessToken);
11+
void removeRefreshToken(String userId);
1212
}

src/main/java/com/somemore/auth/jwt/refresh/repository/RefreshTokenRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@
1010
public interface RefreshTokenRepository extends CrudRepository<RefreshToken, Integer> {
1111

1212
Optional<RefreshToken> findByAccessToken(String accessToken);
13+
14+
Optional<RefreshToken> findByUserId(String userId);
1315
}

0 commit comments

Comments
 (0)