diff --git a/src/main/java/com/somemore/develop/controller/TokenGenerateMachine.java b/src/main/java/com/somemore/develop/controller/TokenGenerateMachine.java deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/main/java/com/somemore/global/auth/controller/AuthController.java b/src/main/java/com/somemore/global/auth/controller/AuthController.java new file mode 100644 index 000000000..f5fdeccde --- /dev/null +++ b/src/main/java/com/somemore/global/auth/controller/AuthController.java @@ -0,0 +1,45 @@ +package com.somemore.global.auth.controller; + +import com.somemore.global.auth.annotation.CurrentUser; +import com.somemore.global.auth.dto.UserInfoResponseDto; +import com.somemore.global.auth.jwt.domain.EncodedToken; +import com.somemore.global.auth.usecase.AuthQueryUseCase; +import com.somemore.global.common.response.ApiResponse; +import com.somemore.user.domain.UserRole; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.UUID; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/auth") +public class AuthController { + + private final AuthQueryUseCase authQueryUseCase; + + @GetMapping("/user-info") + public ApiResponse getUserInfo( + @CurrentUser UUID userId + ) { + UserRole role = authQueryUseCase.getRoleByUserId(userId); + + return ApiResponse.ok(HttpStatus.OK.value(), + UserInfoResponseDto.of(userId, role), + "유저 정보 응답 성공"); + } + + @GetMapping("/token") + public ApiResponse getToken( + @CurrentUser UUID userId + ) { + EncodedToken accessToken = authQueryUseCase.getAccessTokenByUserId(userId); + + return ApiResponse.ok(HttpStatus.OK.value(), + accessToken.getValueWithPrefix(), + "액세스 토큰 응답 성공"); + } +} diff --git a/src/main/java/com/somemore/global/auth/controller/UserInfoQueryController.java b/src/main/java/com/somemore/global/auth/controller/UserInfoQueryController.java deleted file mode 100644 index 5c0608701..000000000 --- a/src/main/java/com/somemore/global/auth/controller/UserInfoQueryController.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.somemore.global.auth.controller; - -import com.somemore.global.auth.dto.UserInfoResponseDto; -import com.somemore.global.auth.jwt.exception.JwtErrorType; -import com.somemore.global.common.response.ApiResponse; -import com.somemore.global.exception.BadRequestException; -import lombok.RequiredArgsConstructor; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/token") -public class UserInfoQueryController { - - @GetMapping("/userinfo") - public ApiResponse getUserInfoBySCH() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - - String userId = authentication.getPrincipal().toString(); - String role = authentication.getAuthorities().stream() - .findFirst() - .map(GrantedAuthority::getAuthority) - .orElseThrow(() -> new BadRequestException(JwtErrorType.INVALID_TOKEN.getMessage())); - - return ApiResponse.ok(200, - new UserInfoResponseDto(userId, role), - "유저 정보 응답 성공"); - } -} diff --git a/src/main/java/com/somemore/global/auth/cookie/CookieService.java b/src/main/java/com/somemore/global/auth/cookie/CookieService.java index 4cc7f7250..08da37937 100644 --- a/src/main/java/com/somemore/global/auth/cookie/CookieService.java +++ b/src/main/java/com/somemore/global/auth/cookie/CookieService.java @@ -13,21 +13,19 @@ public class CookieService implements CookieUseCase { @Override - public void setAccessToken(HttpServletResponse response, String value) { - ResponseCookie cookie = generateCookie(TokenType.ACCESS, value); + public void setToken(HttpServletResponse response, String value, TokenType tokenType) { + ResponseCookie cookie = generateCookie(tokenType, value); response.addHeader("Set-Cookie", cookie.toString()); - log.info("SET_COOKIE_ACCESS_TOKEN = {}", value); } @Override public void deleteAccessToken(HttpServletResponse response) { - ResponseCookie cookie = generateCookie(TokenType.SIGNOUT, TokenType.SIGNOUT.name()); + ResponseCookie cookie = generateCookie(TokenType.SIGN_OUT, TokenType.SIGN_OUT.name()); response.addHeader("Set-Cookie", cookie.toString()); - log.info("DELETE_COOKIE_ACCESS_TOKEN"); } private static ResponseCookie generateCookie(TokenType tokenType, String value) { - return ResponseCookie.from(TokenType.ACCESS.name(), value) + return ResponseCookie.from(TokenType.ACCESS.getDescription(), value) .domain(".somemore.site") .httpOnly(true) .secure(true) diff --git a/src/main/java/com/somemore/global/auth/cookie/CookieUseCase.java b/src/main/java/com/somemore/global/auth/cookie/CookieUseCase.java index 5c41dadeb..0ed851839 100644 --- a/src/main/java/com/somemore/global/auth/cookie/CookieUseCase.java +++ b/src/main/java/com/somemore/global/auth/cookie/CookieUseCase.java @@ -1,9 +1,11 @@ package com.somemore.global.auth.cookie; +import com.somemore.global.auth.jwt.domain.TokenType; import jakarta.servlet.http.HttpServletResponse; public interface CookieUseCase { - void setAccessToken(HttpServletResponse response, String value); + + void setToken(HttpServletResponse response, String value, TokenType tokenType); void deleteAccessToken(HttpServletResponse response); } diff --git a/src/main/java/com/somemore/global/auth/dto/UserInfoResponseDto.java b/src/main/java/com/somemore/global/auth/dto/UserInfoResponseDto.java index 3347c6f9a..3e2ccbd81 100644 --- a/src/main/java/com/somemore/global/auth/dto/UserInfoResponseDto.java +++ b/src/main/java/com/somemore/global/auth/dto/UserInfoResponseDto.java @@ -1,8 +1,11 @@ package com.somemore.global.auth.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.somemore.user.domain.UserRole; import io.swagger.v3.oas.annotations.media.Schema; +import java.util.UUID; + @Schema(description = "유저 정보 DTO") public record UserInfoResponseDto( @JsonProperty("USER_ID") @@ -13,4 +16,7 @@ public record UserInfoResponseDto( @Schema(description = "유저 ROLE") String role ) { + public static UserInfoResponseDto of(UUID userId, UserRole role) { + return new UserInfoResponseDto(userId.toString(), role.getAuthority()); + } } diff --git a/src/main/java/com/somemore/global/auth/idpw/filter/IdPwAuthFilter.java b/src/main/java/com/somemore/global/auth/idpw/filter/IdPwAuthFilter.java index 8833aa4ac..0e58e7790 100644 --- a/src/main/java/com/somemore/global/auth/idpw/filter/IdPwAuthFilter.java +++ b/src/main/java/com/somemore/global/auth/idpw/filter/IdPwAuthFilter.java @@ -1,7 +1,6 @@ package com.somemore.global.auth.idpw.filter; import com.fasterxml.jackson.databind.ObjectMapper; -import com.somemore.global.auth.cookie.CookieUseCase; import com.somemore.global.auth.jwt.domain.EncodedToken; import com.somemore.global.auth.jwt.usecase.GenerateTokensOnLoginUseCase; import com.somemore.user.domain.UserRole; @@ -47,7 +46,7 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR String userId = authResult.getName(); String role = extractRole(authResult); EncodedToken accessToken = - generateTokensOnLoginUseCase.saveRefreshTokenAndReturnAccessToken( + generateTokensOnLoginUseCase.generateAuthTokensAndReturnAccessToken( UUID.fromString(userId), UserRole.from(role)); diff --git a/src/main/java/com/somemore/global/auth/jwt/domain/TokenType.java b/src/main/java/com/somemore/global/auth/jwt/domain/TokenType.java index 7b91403e2..a155c239d 100644 --- a/src/main/java/com/somemore/global/auth/jwt/domain/TokenType.java +++ b/src/main/java/com/somemore/global/auth/jwt/domain/TokenType.java @@ -1,20 +1,27 @@ package com.somemore.global.auth.jwt.domain; -import lombok.Getter; +import lombok.RequiredArgsConstructor; -@Getter +import java.time.Duration; + +@RequiredArgsConstructor public enum TokenType { - ACCESS(1000 * 60 * 30), - REFRESH(1000 * 60 * 60 * 24 * 7), - SIGNOUT(0); + ACCESS(Duration.ofMinutes(30)), + REFRESH(Duration.ofDays(7)), + SIGN_IN(Duration.ofMinutes(1)), + SIGN_OUT(Duration.ZERO); + + private final Duration period; - private final int period; + public String getDescription() { + return this.name() + "_TOKEN"; + } - TokenType(int period) { - this.period = period; + public int getPeriodInMillis() { + return Math.toIntExact(period.toMillis()); } public int getPeriodInSeconds() { - return Math.toIntExact(period / 1000); + return Math.toIntExact(period.getSeconds()); } } diff --git a/src/main/java/com/somemore/global/auth/jwt/generator/HmacJwtGenerator.java b/src/main/java/com/somemore/global/auth/jwt/generator/HmacJwtGenerator.java index 6cb909011..6d00476c4 100644 --- a/src/main/java/com/somemore/global/auth/jwt/generator/HmacJwtGenerator.java +++ b/src/main/java/com/somemore/global/auth/jwt/generator/HmacJwtGenerator.java @@ -23,7 +23,7 @@ public class HmacJwtGenerator implements JwtGenerator { public EncodedToken generateToken(String userId, String role, TokenType tokenType) { Claims claims = buildClaims(userId, role); Instant now = Instant.now(); - Instant expiration = now.plusMillis(tokenType.getPeriod()); + Instant expiration = now.plusMillis(tokenType.getPeriodInMillis()); String uniqueId = UUID.randomUUID().toString(); // JTI return new EncodedToken(Jwts.builder() diff --git a/src/main/java/com/somemore/global/auth/jwt/refresh/manager/RedisRefreshTokenManager.java b/src/main/java/com/somemore/global/auth/jwt/refresh/manager/RedisRefreshTokenManager.java index 84e88b4bd..1185a3fde 100644 --- a/src/main/java/com/somemore/global/auth/jwt/refresh/manager/RedisRefreshTokenManager.java +++ b/src/main/java/com/somemore/global/auth/jwt/refresh/manager/RedisRefreshTokenManager.java @@ -8,6 +8,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.UUID; + @Service @RequiredArgsConstructor public class RedisRefreshTokenManager implements RefreshTokenManager { @@ -15,11 +17,17 @@ public class RedisRefreshTokenManager implements RefreshTokenManager { private final RefreshTokenRepository refreshTokenRepository; @Override - public RefreshToken findRefreshToken(EncodedToken accessToken) { + public RefreshToken findRefreshTokenByAccessToken(EncodedToken accessToken) { return refreshTokenRepository.findByAccessToken(accessToken.value()) .orElseThrow(() -> new JwtException(JwtErrorType.EXPIRED_TOKEN)); } + @Override + public RefreshToken findRefreshTokenByUserId(UUID userId) { + return refreshTokenRepository.findByUserId(userId.toString()) + .orElseThrow(() -> new JwtException(JwtErrorType.EXPIRED_TOKEN)); + } + @Override public void save(RefreshToken refreshToken) { refreshTokenRepository.save(refreshToken); diff --git a/src/main/java/com/somemore/global/auth/jwt/refresh/manager/RefreshTokenManager.java b/src/main/java/com/somemore/global/auth/jwt/refresh/manager/RefreshTokenManager.java index c2343b094..d4598d14c 100644 --- a/src/main/java/com/somemore/global/auth/jwt/refresh/manager/RefreshTokenManager.java +++ b/src/main/java/com/somemore/global/auth/jwt/refresh/manager/RefreshTokenManager.java @@ -3,8 +3,12 @@ import com.somemore.global.auth.jwt.domain.EncodedToken; import com.somemore.global.auth.jwt.refresh.domain.RefreshToken; +import java.util.UUID; + public interface RefreshTokenManager { - RefreshToken findRefreshToken(EncodedToken accessToken); + RefreshToken findRefreshTokenByAccessToken(EncodedToken accessToken); + + RefreshToken findRefreshTokenByUserId(UUID userId); void save(RefreshToken refreshToken); diff --git a/src/main/java/com/somemore/global/auth/jwt/refresh/refresher/DefaultJwtRefresher.java b/src/main/java/com/somemore/global/auth/jwt/refresh/refresher/DefaultJwtRefresher.java index a9f492276..86430634e 100644 --- a/src/main/java/com/somemore/global/auth/jwt/refresh/refresher/DefaultJwtRefresher.java +++ b/src/main/java/com/somemore/global/auth/jwt/refresh/refresher/DefaultJwtRefresher.java @@ -24,7 +24,7 @@ public class DefaultJwtRefresher implements JwtRefresher { @Override public EncodedToken refreshAccessToken(EncodedToken accessToken) { - RefreshToken refreshToken = refreshTokenManager.findRefreshToken(accessToken); + RefreshToken refreshToken = refreshTokenManager.findRefreshTokenByAccessToken(accessToken); EncodedToken refreshTokenValue = new EncodedToken(refreshToken.getRefreshToken()); jwtValidator.validateToken(refreshTokenValue); diff --git a/src/main/java/com/somemore/global/auth/jwt/service/GenerateTokensOnLoginService.java b/src/main/java/com/somemore/global/auth/jwt/service/GenerateTokensOnLoginService.java index b7e3998e9..fc62ddc4d 100644 --- a/src/main/java/com/somemore/global/auth/jwt/service/GenerateTokensOnLoginService.java +++ b/src/main/java/com/somemore/global/auth/jwt/service/GenerateTokensOnLoginService.java @@ -24,26 +24,32 @@ public class GenerateTokensOnLoginService implements GenerateTokensOnLoginUseCas private final RefreshTokenManager refreshTokenManager; @Override - public EncodedToken saveRefreshTokenAndReturnAccessToken(UUID userId, UserRole role) { - EncodedToken accessToken = jwtGenerator.generateToken( - userId.toString(), - role.getAuthority(), - TokenType.ACCESS - ); - RefreshToken refreshToken = generateRefreshToken(userId, role, accessToken); + public EncodedToken generateLoginToken(UUID userId, UserRole role) { + return generateToken(userId, role, TokenType.SIGN_IN); + } + + @Override + public EncodedToken generateAuthTokensAndReturnAccessToken(UUID userId, UserRole role) { + EncodedToken accessToken = generateToken(userId, role, TokenType.ACCESS); + RefreshToken refreshToken = generateRefreshTokenWithAccessToken(userId, role, accessToken); saveRefreshToken(refreshToken); return accessToken; } - private RefreshToken generateRefreshToken(UUID userId, UserRole role, EncodedToken accessToken) { + private EncodedToken generateToken(UUID userId, UserRole role, TokenType tokenType) { + return jwtGenerator.generateToken( + userId.toString(), + role.getAuthority(), + tokenType + ); + } + + private RefreshToken generateRefreshTokenWithAccessToken(UUID userId, UserRole role, EncodedToken accessToken) { return new RefreshToken( userId.toString(), accessToken, - jwtGenerator.generateToken( - userId.toString(), - role.getAuthority(), - TokenType.REFRESH) + generateToken(userId, role, TokenType.REFRESH) ); } diff --git a/src/main/java/com/somemore/global/auth/jwt/service/JwtService.java b/src/main/java/com/somemore/global/auth/jwt/service/JwtService.java index 6321821bf..43600e8d3 100644 --- a/src/main/java/com/somemore/global/auth/jwt/service/JwtService.java +++ b/src/main/java/com/somemore/global/auth/jwt/service/JwtService.java @@ -48,7 +48,7 @@ public Claims getClaims(EncodedToken token) { private void handleJwtExpiredException(JwtException e, EncodedToken accessToken, HttpServletResponse response) { if (e.getErrorType() == JwtErrorType.EXPIRED_TOKEN) { EncodedToken refreshedToken = jwtRefresher.refreshAccessToken(accessToken); - cookieUseCase.setAccessToken(response, refreshedToken.value()); + cookieUseCase.setToken(response, refreshedToken.value(), TokenType.ACCESS); return; } throw e; diff --git a/src/main/java/com/somemore/global/auth/jwt/usecase/GenerateTokensOnLoginUseCase.java b/src/main/java/com/somemore/global/auth/jwt/usecase/GenerateTokensOnLoginUseCase.java index 6f1786bb8..4c7c74828 100644 --- a/src/main/java/com/somemore/global/auth/jwt/usecase/GenerateTokensOnLoginUseCase.java +++ b/src/main/java/com/somemore/global/auth/jwt/usecase/GenerateTokensOnLoginUseCase.java @@ -6,5 +6,6 @@ import java.util.UUID; public interface GenerateTokensOnLoginUseCase { - EncodedToken saveRefreshTokenAndReturnAccessToken(UUID userId, UserRole role); + EncodedToken generateLoginToken(UUID userId, UserRole role); + EncodedToken generateAuthTokensAndReturnAccessToken(UUID userId, UserRole role); } diff --git a/src/main/java/com/somemore/global/auth/oauth/handler/CustomOAuthSuccessHandler.java b/src/main/java/com/somemore/global/auth/oauth/handler/CustomOAuthSuccessHandler.java index 12eb7da18..36ec7e2a2 100644 --- a/src/main/java/com/somemore/global/auth/oauth/handler/CustomOAuthSuccessHandler.java +++ b/src/main/java/com/somemore/global/auth/oauth/handler/CustomOAuthSuccessHandler.java @@ -1,6 +1,8 @@ package com.somemore.global.auth.oauth.handler; +import com.somemore.global.auth.cookie.CookieUseCase; import com.somemore.global.auth.jwt.domain.EncodedToken; +import com.somemore.global.auth.jwt.domain.TokenType; import com.somemore.global.auth.jwt.usecase.GenerateTokensOnLoginUseCase; import com.somemore.global.auth.oauth.processor.OAuthUserProcessor; import com.somemore.global.auth.redirect.RedirectUseCase; @@ -24,10 +26,10 @@ public class CustomOAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHan private final OAuthUserProcessor oauthUserProcessor; private final GenerateTokensOnLoginUseCase generateTokensOnLoginUseCase; + private final CookieUseCase cookieUseCase; private final RedirectUseCase redirectUseCase; - public static final String AUTHORIZATION = "Authorization"; - public static final String MAIN_PATH = "/main"; + public static final String SUCCESS_PATH = "/success"; @Override public void onAuthenticationSuccess(HttpServletRequest request, @@ -42,15 +44,18 @@ public void onAuthenticationSuccess(HttpServletRequest request, private void redirect(HttpServletRequest request, HttpServletResponse response) { // TODO 유저 정보 커스텀 확인 분기 - redirectUseCase.redirect(request, response, MAIN_PATH); + redirectUseCase.redirect(request, response, SUCCESS_PATH); } private void processAccessToken(HttpServletResponse response, UUID userId) { - EncodedToken accessToken = - generateTokensOnLoginUseCase.saveRefreshTokenAndReturnAccessToken( + generateTokensOnLoginUseCase.generateAuthTokensAndReturnAccessToken( + userId, UserRole.getOAuthUserDefaultRole()); + + EncodedToken loginToken = + generateTokensOnLoginUseCase.generateLoginToken( userId, UserRole.getOAuthUserDefaultRole()); - response.addHeader(AUTHORIZATION, accessToken.getValueWithPrefix()); + cookieUseCase.setToken(response, loginToken.getValueWithPrefix(), TokenType.SIGN_IN); } private OAuth2User extractOAuthUser(Authentication authentication) { diff --git a/src/main/java/com/somemore/global/auth/usecase/AuthQueryService.java b/src/main/java/com/somemore/global/auth/usecase/AuthQueryService.java new file mode 100644 index 000000000..9da0b7180 --- /dev/null +++ b/src/main/java/com/somemore/global/auth/usecase/AuthQueryService.java @@ -0,0 +1,32 @@ +package com.somemore.global.auth.usecase; + +import com.somemore.global.auth.jwt.domain.EncodedToken; +import com.somemore.global.auth.jwt.refresh.manager.RefreshTokenManager; +import com.somemore.user.domain.UserRole; +import com.somemore.user.usecase.UserQueryUseCase; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.UUID; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class AuthQueryService implements AuthQueryUseCase { + + private final UserQueryUseCase userQueryUseCase; + private final RefreshTokenManager refreshTokenManager; + + @Override + public UserRole getRoleByUserId(UUID userId) { + return userQueryUseCase.getRoleById(userId); + } + + @Override + public EncodedToken getAccessTokenByUserId(UUID userId) { + return new EncodedToken( + refreshTokenManager.findRefreshTokenByUserId(userId) + .getAccessToken()); + } +} diff --git a/src/main/java/com/somemore/global/auth/usecase/AuthQueryUseCase.java b/src/main/java/com/somemore/global/auth/usecase/AuthQueryUseCase.java new file mode 100644 index 000000000..74caed416 --- /dev/null +++ b/src/main/java/com/somemore/global/auth/usecase/AuthQueryUseCase.java @@ -0,0 +1,11 @@ +package com.somemore.global.auth.usecase; + +import com.somemore.global.auth.jwt.domain.EncodedToken; +import com.somemore.user.domain.UserRole; + +import java.util.UUID; + +public interface AuthQueryUseCase { + UserRole getRoleByUserId(UUID userId); + EncodedToken getAccessTokenByUserId(UUID userId); +} diff --git a/src/main/java/com/somemore/user/repository/user/UserRepository.java b/src/main/java/com/somemore/user/repository/user/UserRepository.java index cf6a361a5..238ffddb1 100644 --- a/src/main/java/com/somemore/user/repository/user/UserRepository.java +++ b/src/main/java/com/somemore/user/repository/user/UserRepository.java @@ -1,6 +1,7 @@ package com.somemore.user.repository.user; import com.somemore.user.domain.User; +import com.somemore.user.domain.UserRole; import java.util.Optional; import java.util.UUID; @@ -9,6 +10,8 @@ public interface UserRepository { Optional findById(UUID id); + Optional findRoleById(UUID id); + Optional findByAccountId(String accountId); User save(User user); diff --git a/src/main/java/com/somemore/user/repository/user/UserRepositoryImpl.java b/src/main/java/com/somemore/user/repository/user/UserRepositoryImpl.java index 628d2f412..f08d4e3e2 100644 --- a/src/main/java/com/somemore/user/repository/user/UserRepositoryImpl.java +++ b/src/main/java/com/somemore/user/repository/user/UserRepositoryImpl.java @@ -4,6 +4,7 @@ import com.querydsl.jpa.impl.JPAQueryFactory; import com.somemore.user.domain.QUser; import com.somemore.user.domain.User; +import com.somemore.user.domain.UserRole; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @@ -29,6 +30,17 @@ public Optional findById(UUID id) { .fetchOne()); } + @Override + public Optional findRoleById(UUID id) { + return Optional.ofNullable( + queryFactory.select(user.role) + .from(user) + .where( + user.id.eq(id), + isNotDeleted()) + .fetchOne()); + } + @Override public Optional findByAccountId(String accountId) { return Optional.ofNullable( diff --git a/src/main/java/com/somemore/user/service/UserQueryService.java b/src/main/java/com/somemore/user/service/UserQueryService.java index 47d0c64c7..9f026c420 100644 --- a/src/main/java/com/somemore/user/service/UserQueryService.java +++ b/src/main/java/com/somemore/user/service/UserQueryService.java @@ -3,6 +3,7 @@ import com.somemore.global.exception.NoSuchElementException; import com.somemore.user.domain.User; import com.somemore.user.domain.UserCommonAttribute; +import com.somemore.user.domain.UserRole; import com.somemore.user.repository.user.UserRepository; import com.somemore.user.repository.usercommonattribute.UserCommonAttributeRepository; import com.somemore.user.usecase.UserQueryUseCase; @@ -30,6 +31,12 @@ public User getById(UUID id) { .orElseThrow(() -> new NoSuchElementException(NOT_EXIST_USER)); } + @Override + public UserRole getRoleById(UUID id) { + return userRepository.findRoleById(id) + .orElseThrow(() -> new NoSuchElementException(NOT_EXIST_USER)); + } + @Override public User getByAccountId(String accountId) { return userRepository.findByAccountId(accountId) diff --git a/src/main/java/com/somemore/user/usecase/UserQueryUseCase.java b/src/main/java/com/somemore/user/usecase/UserQueryUseCase.java index 08816005d..75339fab1 100644 --- a/src/main/java/com/somemore/user/usecase/UserQueryUseCase.java +++ b/src/main/java/com/somemore/user/usecase/UserQueryUseCase.java @@ -2,12 +2,14 @@ import com.somemore.user.domain.User; import com.somemore.user.domain.UserCommonAttribute; +import com.somemore.user.domain.UserRole; import java.util.UUID; public interface UserQueryUseCase { User getById(UUID id); + UserRole getRoleById(UUID userId); User getByAccountId(String accountId); UserCommonAttribute getCommonAttributeByUserId(UUID userId); boolean getIsCustomizedByUserId(UUID userId); diff --git a/src/test/java/com/somemore/domains/volunteer/service/SignOutVolunteerServiceTest.java b/src/test/java/com/somemore/domains/volunteer/service/SignOutVolunteerServiceTest.java index d4730c6c8..23b4e5e76 100644 --- a/src/test/java/com/somemore/domains/volunteer/service/SignOutVolunteerServiceTest.java +++ b/src/test/java/com/somemore/domains/volunteer/service/SignOutVolunteerServiceTest.java @@ -66,17 +66,17 @@ void signOutDeletesTokens() { jwtGenerator.generateToken(volunteerId, role.getAuthority(), TokenType.REFRESH)); refreshTokenManager.save(refreshToken); - cookieUseCase.setAccessToken(response, accessToken.value()); + cookieUseCase.setToken(response, accessToken.value(), TokenType.ACCESS); // When signOutVolunteerService.signOut(response, volunteerId); // Then - assertThatThrownBy(() -> refreshTokenManager.findRefreshToken(accessToken)) + assertThatThrownBy(() -> refreshTokenManager.findRefreshTokenByAccessToken(accessToken)) .isInstanceOf(JwtException.class) .hasMessage(JwtErrorType.EXPIRED_TOKEN.getMessage()); - assertThat(Arrays.toString(response.getCookies())).contains(TokenType.SIGNOUT.name()); + assertThat(Arrays.toString(response.getCookies())).contains(TokenType.SIGN_OUT.name()); } @Test @@ -87,6 +87,6 @@ void signOutWithoutRefreshToken() { // Then assertThatNoException().isThrownBy(() -> signOutVolunteerService.signOut(response, volunteerId)); - assertThat(Arrays.toString(response.getCookies())).contains(TokenType.SIGNOUT.name()); + assertThat(Arrays.toString(response.getCookies())).contains(TokenType.SIGN_OUT.name()); } } diff --git a/src/test/java/com/somemore/global/auth/cookie/CookieServiceTest.java b/src/test/java/com/somemore/global/auth/cookie/CookieServiceTest.java index 17ac5b790..4353db5ff 100644 --- a/src/test/java/com/somemore/global/auth/cookie/CookieServiceTest.java +++ b/src/test/java/com/somemore/global/auth/cookie/CookieServiceTest.java @@ -1,6 +1,5 @@ package com.somemore.global.auth.cookie; -import com.somemore.global.auth.cookie.CookieService; import com.somemore.global.auth.jwt.domain.TokenType; import org.junit.jupiter.api.Test; import org.springframework.mock.web.MockHttpServletResponse; @@ -18,11 +17,11 @@ void setAccessToken_ShouldSetCookie() { String accessToken = "test-access-token"; // When - cookieService.setAccessToken(response, accessToken); + cookieService.setToken(response, accessToken, TokenType.ACCESS); // Then String setCookieHeader = response.getHeader("Set-Cookie"); - assertThat(setCookieHeader).contains("ACCESS=" + accessToken); + assertThat(setCookieHeader).contains("ACCESS_TOKEN=" + accessToken); assertThat(setCookieHeader).contains("HttpOnly"); assertThat(setCookieHeader).contains("Secure"); assertThat(setCookieHeader).contains("Path=/"); @@ -38,7 +37,7 @@ void deleteAccessToken_ShouldRemoveCookie() { // Then String setCookieHeader = response.getHeader("Set-Cookie"); - assertThat(setCookieHeader).contains("ACCESS=" + TokenType.SIGNOUT.name()); // 빈 값 + assertThat(setCookieHeader).contains("ACCESS_TOKEN=" + TokenType.SIGN_OUT.name()); // 빈 값 assertThat(setCookieHeader).contains("Max-Age=0"); // 삭제 assertThat(setCookieHeader).contains("Path=/"); } diff --git a/src/test/java/com/somemore/global/auth/jwt/service/JwtServiceTest.java b/src/test/java/com/somemore/global/auth/jwt/service/JwtServiceTest.java index 16439e97a..568f3aa16 100644 --- a/src/test/java/com/somemore/global/auth/jwt/service/JwtServiceTest.java +++ b/src/test/java/com/somemore/global/auth/jwt/service/JwtServiceTest.java @@ -45,9 +45,27 @@ void tearDown() { .forEach(redisTemplate::delete); } - @DisplayName("토큰이 올바르게 생성된다") + @DisplayName("액세스 토큰이 올바르게 생성된다") @Test - void generateAndValidateToken() { + void generateAndValidateAccessToken() { + // given + String userId = UUID.randomUUID().toString(); + UserRole role = UserRole.VOLUNTEER; + TokenType tokenType = TokenType.ACCESS; + + // when + EncodedToken token = jwtService.generateToken(userId, role.getAuthority(), tokenType); + + // then + Claims claims = jwtService.getClaims(token); + assertThat(claims.get("id", String.class)).isEqualTo(userId); + assertThat(claims.get("role", String.class)).isEqualTo(role.getAuthority()); + assertThat(claims.getExpiration()).isNotNull(); + } + + @DisplayName("로그인 토큰이 올바르게 생성된다") + @Test + void generateAndValidateLoginToken() { // given String userId = UUID.randomUUID().toString(); UserRole role = UserRole.VOLUNTEER; @@ -81,8 +99,8 @@ void tokenExpirationPeriodIsExact() { long accessTokenDuration = accessClaims.getExpiration().getTime() - accessClaims.getIssuedAt().getTime(); long refreshTokenDuration = refreshClaims.getExpiration().getTime() - refreshClaims.getIssuedAt().getTime(); - assertThat(accessTokenDuration).isEqualTo(TokenType.ACCESS.getPeriod()); - assertThat(refreshTokenDuration).isEqualTo(TokenType.REFRESH.getPeriod()); + assertThat(accessTokenDuration).isEqualTo(TokenType.ACCESS.getPeriodInMillis()); + assertThat(refreshTokenDuration).isEqualTo(TokenType.REFRESH.getPeriodInMillis()); } @DisplayName("동일한 사용자로 여러 토큰 생성 시 서로 다른 값이어야 한다") @@ -100,24 +118,6 @@ void multipleTokensForSameUserAreDifferent() { assertThat(token1.value()).isNotEqualTo(token2.value()); } - @DisplayName("만료된 엑세스 토큰은 리프레시 토큰이 유효하다면 갱신된다") - @Test - void verifyAndRefreshExpiredToken() { - // given - String userId = UUID.randomUUID().toString(); - UserRole role = UserRole.VOLUNTEER; - EncodedToken expiredAccessToken = createExpiredToken(userId, role); - createAndSaveRefreshToken(userId, expiredAccessToken, Instant.now().plusMillis(TokenType.REFRESH.getPeriod())); - - MockHttpServletResponse mockResponse = new MockHttpServletResponse(); - - // when - jwtService.processAccessToken(expiredAccessToken, mockResponse); - - // then - assertRefreshedAccessToken(mockResponse); - } - @DisplayName("만료된 엑세스 토큰은 리프레시 토큰이 유효하지 않다면 갱신되지 않고 예외가 발생한다") @Test void throwExceptionWhenRefreshTokenIsInvalid() { @@ -164,7 +164,7 @@ void refreshedAccessTokenIsSetInCookie() { UserRole role = UserRole.VOLUNTEER; EncodedToken expiredAccessToken = createExpiredToken(userId, role); - createAndSaveRefreshToken(userId, expiredAccessToken, Instant.now().plusMillis(TokenType.REFRESH.getPeriod())); + createAndSaveRefreshToken(userId, expiredAccessToken, Instant.now().plusMillis(TokenType.REFRESH.getPeriodInMillis())); MockHttpServletResponse mockResponse = new MockHttpServletResponse(); @@ -173,7 +173,7 @@ void refreshedAccessTokenIsSetInCookie() { // then String cookieHeader = mockResponse.getHeader("Set-Cookie"); - assertThat(cookieHeader).contains("ACCESS="); + assertThat(cookieHeader).contains("ACCESS_TOKEN="); assertThat(cookieHeader).contains("HttpOnly"); assertThat(cookieHeader).contains("Secure"); } @@ -186,14 +186,14 @@ void refreshTokenIsUpdated() { UserRole role = UserRole.VOLUNTEER; EncodedToken expiredAccessToken = createExpiredToken(userId, role); - RefreshToken oldRefreshToken = createAndSaveRefreshToken(userId, expiredAccessToken, Instant.now().plusMillis(TokenType.REFRESH.getPeriod())); + RefreshToken oldRefreshToken = createAndSaveRefreshToken(userId, expiredAccessToken, Instant.now().plusMillis(TokenType.REFRESH.getPeriodInMillis())); EncodedToken newAccessToken = jwtService.generateToken(userId, role.getAuthority(), TokenType.ACCESS); - RefreshToken newRefreshToken = createAndSaveRefreshToken(userId, newAccessToken, Instant.now().plusMillis(TokenType.REFRESH.getPeriod())); + RefreshToken newRefreshToken = createAndSaveRefreshToken(userId, newAccessToken, Instant.now().plusMillis(TokenType.REFRESH.getPeriodInMillis())); // when // then - assertThatThrownBy(() -> refreshTokenManager.findRefreshToken(expiredAccessToken)) + assertThatThrownBy(() -> refreshTokenManager.findRefreshTokenByAccessToken(expiredAccessToken)) .isInstanceOf(JwtException.class) .hasMessage(JwtErrorType.EXPIRED_TOKEN.getMessage()); @@ -263,7 +263,6 @@ private EncodedToken createExpiredToken(String userId, UserRole role) { private RefreshToken createAndSaveRefreshToken(String userId, EncodedToken accessToken, Instant expiration) { Claims claims = buildClaims(userId, UserRole.VOLUNTEER); Instant now = Instant.now(); - Instant refreshExpiration = now.plusMillis(TokenType.REFRESH.getPeriod()); String uniqueId = UUID.randomUUID().toString(); // jti RefreshToken refreshToken = new RefreshToken( @@ -273,7 +272,7 @@ private RefreshToken createAndSaveRefreshToken(String userId, EncodedToken acces .claims(claims) .id(uniqueId) .issuedAt(Date.from(now)) - .expiration(Date.from(refreshExpiration)) + .expiration(Date.from(expiration)) .signWith(secretKey, Jwts.SIG.HS256) .compact())); @@ -289,15 +288,4 @@ private Claims buildClaims(String userId, UserRole role) { .build(); } - private void assertRefreshedAccessToken(MockHttpServletResponse mockResponse) { - String cookie = mockResponse.getHeader("Set-Cookie"); - assertThat(cookie).isNotNull(); - assertThat(cookie).contains(TokenType.ACCESS.name()); - - EncodedToken refreshedAccessToken = new EncodedToken( - cookie.split(";")[0].substring("ACCESS=".length())); - assertThatCode(() -> jwtValidator.validateToken(refreshedAccessToken)) - .doesNotThrowAnyException(); - } - } diff --git a/src/test/java/com/somemore/user/repository/user/UserRepositoryImplTest.java b/src/test/java/com/somemore/user/repository/user/UserRepositoryImplTest.java index 334176804..40d44270b 100644 --- a/src/test/java/com/somemore/user/repository/user/UserRepositoryImplTest.java +++ b/src/test/java/com/somemore/user/repository/user/UserRepositoryImplTest.java @@ -101,6 +101,22 @@ void findByInvalidId() { assertThat(findUser).isEmpty(); } + @DisplayName("유저 아이디로 유저 권한을 조회할 수 있다.") + @Test + void findRoleById() { + // given + UserAuthInfo userAuthInfo = new UserAuthInfo("test@test.test", "test"); + User user = User.from(userAuthInfo, UserRole.CENTER); + User savedUser = userRepository.save(user); + + // when + Optional role = userRepository.findRoleById(savedUser.getId()); + + // then + assertThat(role).isPresent(); + assertThat(role.get()).isEqualTo(savedUser.getRole()); + } + @DisplayName("유저 계정 아이디로 유저를 조회할 수 있다.") @Test void findByAccountId() { diff --git a/src/test/java/com/somemore/user/service/UserQueryServiceTest.java b/src/test/java/com/somemore/user/service/UserQueryServiceTest.java index d89ff667e..87c39279d 100644 --- a/src/test/java/com/somemore/user/service/UserQueryServiceTest.java +++ b/src/test/java/com/somemore/user/service/UserQueryServiceTest.java @@ -29,13 +29,14 @@ class UserQueryServiceTest extends IntegrationTestSupport { private UserCommonAttributeRepository userCommonAttributeRepository; private User user; + private UserCommonAttribute userCommonAttribute; @BeforeEach void setup() { UserAuthInfo userAuthInfo = UserAuthInfo.createForOAuth(OAuthProvider.NAVER); user = userRepository.save(User.from(userAuthInfo, UserRole.VOLUNTEER)); - userCommonAttributeRepository.save(UserCommonAttribute.createDefault(user.getId())); + userCommonAttribute = userCommonAttributeRepository.save(UserCommonAttribute.createDefault(user.getId())); } @@ -48,7 +49,24 @@ void getById() { User foundUser = userQueryService.getById(user.getId()); // then - assertThat(foundUser).isNotNull(); + assertThat(foundUser) + .isNotNull() + .isEqualTo(user); + + } + + @DisplayName("유저 아이디로 유저 권한을 조회할 수 있다.") + @Test + void getRoleById() { + // given + + // when + UserRole role = userQueryService.getRoleById(user.getId()); + + // then + assertThat(role) + .isNotNull() + .isEqualTo(user.getRole()); } @DisplayName("유저 계정 아이디로 유저를 조회할 수 있다.") @@ -60,7 +78,9 @@ void getByAccountId() { User foundUser = userQueryService.getByAccountId(user.getAccountId()); // then - assertThat(foundUser).isNotNull(); + assertThat(foundUser) + .isNotNull() + .isEqualTo(user); } @DisplayName("유저 아이디로 유저 공통 속성을 조회할 수 있다.") @@ -72,7 +92,9 @@ void getCommonAttributeByUserID() { UserCommonAttribute foundCommonAttribute = userQueryService.getCommonAttributeByUserId(user.getId()); // then - assertThat(foundCommonAttribute).isNotNull(); + assertThat(foundCommonAttribute) + .isNotNull() + .isEqualTo(userCommonAttribute); } @DisplayName("유저가 필수 입력 필드를 사용자화하지 않은 경우 기본 값 false를 반환한다.")