diff --git a/src/main/java/ject/componote/domain/auth/application/AuthService.java b/src/main/java/ject/componote/domain/auth/application/AuthService.java index 6b72f557..255ffc40 100644 --- a/src/main/java/ject/componote/domain/auth/application/AuthService.java +++ b/src/main/java/ject/componote/domain/auth/application/AuthService.java @@ -14,7 +14,7 @@ import ject.componote.domain.auth.error.NotFoundSocialAccountException; import ject.componote.domain.auth.model.AuthPrincipal; import ject.componote.domain.auth.model.Nickname; -import ject.componote.domain.auth.util.TokenProvider; +import ject.componote.domain.auth.token.application.TokenService; import ject.componote.infra.storage.application.StorageService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -27,30 +27,29 @@ public class AuthService { private final StorageService storageService; private final MemberRepository memberRepository; private final SocialAccountRepository socialAccountRepository; - private final TokenProvider tokenProvider; + private final TokenService tokenService; @Transactional public MemberSignupResponse signup(final MemberSignupRequest request) { - final Long socialAccountId = request.socialAccountId(); + final Long socialAccountId = getSocialAccountId(request.socialAccountToken()); if (!socialAccountRepository.existsById(socialAccountId)) { - throw new NotFoundSocialAccountException(socialAccountId); + throw new NotFoundSocialAccountException(); } if (memberRepository.existsBySocialAccountId(socialAccountId)) { - throw new DuplicatedSignupException(socialAccountId); + throw new DuplicatedSignupException(); } - final Member member = memberRepository.save(request.toMember()); - final String accessToken = tokenProvider.createToken(AuthPrincipal.from(member)); -// storageService.moveImage(member.getProfileImage()); + final Member member = memberRepository.save(request.toMember(socialAccountId)); + storageService.moveImage(member.getProfileImage()); + final String accessToken = createAccessToken(member); return MemberSignupResponse.of(accessToken, member); } - // socialAccountId 만 가지고 로그인을 하는건 위험하지 않을까? 별도 암호화가 있으면 좋을 것 같음 - public MemberLoginResponse login(final MemberLoginRequest memberLoginRequest) { - final Long socialAccountId = memberLoginRequest.socialAccountId(); + public MemberLoginResponse login(final MemberLoginRequest request) { + final Long socialAccountId = getSocialAccountId(request.socialAccountToken()); final Member member = findMemberBySocialAccountId(socialAccountId); - final String accessToken = tokenProvider.createToken(AuthPrincipal.from(member)); + final String accessToken = createAccessToken(member); return MemberLoginResponse.of(accessToken, member); } @@ -63,7 +62,15 @@ public void validateNickname(final MemberNicknameValidateRequest request) { private Member findMemberBySocialAccountId(final Long socialAccountId) { return memberRepository.findBySocialAccountId(socialAccountId) - .orElseThrow(() -> NotFoundMemberException.createWhenInvalidSocialAccountId(socialAccountId)); + .orElseThrow(NotFoundMemberException::createWhenInvalidSocialAccountId); + } + + private String createAccessToken(final Member member) { + return tokenService.createAccessToken(AuthPrincipal.from(member)); + } + + private Long getSocialAccountId(final String socialAccountToken) { + return tokenService.extractSocialAccountTokenPayload(socialAccountToken); } @Transactional diff --git a/src/main/java/ject/componote/domain/auth/application/OAuthService.java b/src/main/java/ject/componote/domain/auth/application/OAuthService.java index 46ec6672..42cfbddd 100644 --- a/src/main/java/ject/componote/domain/auth/application/OAuthService.java +++ b/src/main/java/ject/componote/domain/auth/application/OAuthService.java @@ -2,20 +2,24 @@ import ject.componote.domain.auth.dao.MemberRepository; import ject.componote.domain.auth.domain.SocialAccount; -import ject.componote.domain.auth.dto.login.response.OAuthLoginResponse; import ject.componote.domain.auth.dto.authorize.response.OAuthAuthorizationUrlResponse; +import ject.componote.domain.auth.dto.login.response.OAuthLoginResponse; +import ject.componote.domain.auth.token.application.TokenService; import ject.componote.infra.oauth.application.OAuthClient; import ject.componote.infra.oauth.dto.authorize.response.OAuthAuthorizePayload; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import reactor.core.scheduler.Schedulers; @Service @RequiredArgsConstructor +@Transactional(readOnly = true) public class OAuthService { private final MemberRepository memberRepository; private final OAuthClient oAuthClient; private final OAuthResultHandler oauthResultHandler; + private final TokenService tokenService; public OAuthAuthorizationUrlResponse getOAuthAuthorizationCodeUrl(final String providerType) { final OAuthAuthorizePayload oAuthAuthorizePayload = oAuthClient.getAuthorizePayload(providerType); @@ -28,6 +32,7 @@ public OAuthLoginResponse login(final String providerType, final String code) { .map(oauthResultHandler::saveOrGet) .block(); final boolean isRegister = memberRepository.existsBySocialAccountId(socialAccount.getId()); - return OAuthLoginResponse.of(isRegister, socialAccount); + final String socialAccountToken = tokenService.createSocialAccountToken(socialAccount); + return OAuthLoginResponse.of(isRegister, socialAccountToken); } } diff --git a/src/main/java/ject/componote/domain/auth/dto/login/request/MemberLoginRequest.java b/src/main/java/ject/componote/domain/auth/dto/login/request/MemberLoginRequest.java index 4247e5ef..d4e9c9e7 100644 --- a/src/main/java/ject/componote/domain/auth/dto/login/request/MemberLoginRequest.java +++ b/src/main/java/ject/componote/domain/auth/dto/login/request/MemberLoginRequest.java @@ -2,5 +2,5 @@ import jakarta.validation.constraints.NotNull; -public record MemberLoginRequest(@NotNull Long socialAccountId) { +public record MemberLoginRequest(@NotNull String socialAccountToken) { } diff --git a/src/main/java/ject/componote/domain/auth/dto/login/response/OAuthLoginResponse.java b/src/main/java/ject/componote/domain/auth/dto/login/response/OAuthLoginResponse.java index b8df7b43..8f7966b9 100644 --- a/src/main/java/ject/componote/domain/auth/dto/login/response/OAuthLoginResponse.java +++ b/src/main/java/ject/componote/domain/auth/dto/login/response/OAuthLoginResponse.java @@ -1,9 +1,7 @@ package ject.componote.domain.auth.dto.login.response; -import ject.componote.domain.auth.domain.SocialAccount; - -public record OAuthLoginResponse(boolean isRegister, Long socialAccountId) { - public static OAuthLoginResponse of(final boolean isRegister, final SocialAccount socialAccount) { - return new OAuthLoginResponse(isRegister, socialAccount.getId()); +public record OAuthLoginResponse(boolean isRegister, String socialAccountToken) { + public static OAuthLoginResponse of(final boolean isRegister, final String socialAccountToken) { + return new OAuthLoginResponse(isRegister, socialAccountToken); } } diff --git a/src/main/java/ject/componote/domain/auth/dto/signup/request/MemberSignupRequest.java b/src/main/java/ject/componote/domain/auth/dto/signup/request/MemberSignupRequest.java index 4d33bab1..3c77570c 100644 --- a/src/main/java/ject/componote/domain/auth/dto/signup/request/MemberSignupRequest.java +++ b/src/main/java/ject/componote/domain/auth/dto/signup/request/MemberSignupRequest.java @@ -9,8 +9,8 @@ public record MemberSignupRequest( @NotBlank String nickname, @NotBlank String job, @Nullable String profileImageObjectKey, - @NotNull Long socialAccountId) { - public Member toMember() { + @NotNull String socialAccountToken) { + public Member toMember(final Long socialAccountId) { return Member.of( nickname, job, diff --git a/src/main/java/ject/componote/domain/auth/error/DuplicatedSignupException.java b/src/main/java/ject/componote/domain/auth/error/DuplicatedSignupException.java index 497ee701..760a674f 100644 --- a/src/main/java/ject/componote/domain/auth/error/DuplicatedSignupException.java +++ b/src/main/java/ject/componote/domain/auth/error/DuplicatedSignupException.java @@ -3,7 +3,7 @@ import org.springframework.http.HttpStatus; public class DuplicatedSignupException extends AuthException { - public DuplicatedSignupException(final Long socialAccountId) { - super("해당 소셜 ID로 이미 가입된 계정이 있습니다. 소셜 ID: " + socialAccountId, HttpStatus.BAD_REQUEST); + public DuplicatedSignupException() { + super("해당 소셜 ID로 이미 가입된 계정이 있습니다.", HttpStatus.BAD_REQUEST); } } diff --git a/src/main/java/ject/componote/domain/auth/error/NotFoundMemberException.java b/src/main/java/ject/componote/domain/auth/error/NotFoundMemberException.java index 3d58cc88..fc87d229 100644 --- a/src/main/java/ject/componote/domain/auth/error/NotFoundMemberException.java +++ b/src/main/java/ject/componote/domain/auth/error/NotFoundMemberException.java @@ -11,7 +11,7 @@ public static NotFoundMemberException createWhenInvalidMemberId(final Long membe return new NotFoundMemberException("일치하는 회원을 찾을 수 없습니다. 회원 ID: " + memberId, HttpStatus.NOT_FOUND); } - public static NotFoundMemberException createWhenInvalidSocialAccountId(final Long socialAccountId) { - return new NotFoundMemberException("소셜 ID에 해당하는 회원이 없습니다. 소셜 ID: " + socialAccountId, HttpStatus.NOT_FOUND); + public static NotFoundMemberException createWhenInvalidSocialAccountId() { + return new NotFoundMemberException("소셜 ID에 해당하는 회원이 없습니다.", HttpStatus.NOT_FOUND); } } diff --git a/src/main/java/ject/componote/domain/auth/error/NotFoundSocialAccountException.java b/src/main/java/ject/componote/domain/auth/error/NotFoundSocialAccountException.java index 39f7c948..7876905e 100644 --- a/src/main/java/ject/componote/domain/auth/error/NotFoundSocialAccountException.java +++ b/src/main/java/ject/componote/domain/auth/error/NotFoundSocialAccountException.java @@ -3,7 +3,7 @@ import org.springframework.http.HttpStatus; public class NotFoundSocialAccountException extends AuthException { - public NotFoundSocialAccountException(final Long socialAccountId) { - super("일치하는 소셜 정보를 찾을 수 없습니다. 소셜 ID: " + socialAccountId, HttpStatus.NOT_FOUND); + public NotFoundSocialAccountException() { + super("일치하는 소셜 정보를 찾을 수 없습니다.", HttpStatus.NOT_FOUND); } } diff --git a/src/main/java/ject/componote/domain/auth/token/application/TokenService.java b/src/main/java/ject/componote/domain/auth/token/application/TokenService.java new file mode 100644 index 00000000..21c06aea --- /dev/null +++ b/src/main/java/ject/componote/domain/auth/token/application/TokenService.java @@ -0,0 +1,109 @@ +package ject.componote.domain.auth.token.application; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import ject.componote.domain.auth.domain.SocialAccount; +import ject.componote.domain.auth.error.InvalidJWTException; +import ject.componote.domain.auth.token.error.InvalidSocialAccountTokenException; +import ject.componote.domain.auth.model.AuthPrincipal; +import ject.componote.domain.auth.token.model.TokenProvider; +import ject.componote.domain.auth.token.model.TokenType; +import ject.componote.domain.auth.token.repository.InMemoryTokenProviderRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.Date; + +@Component +@Slf4j +@RequiredArgsConstructor +public class TokenService { + private final InMemoryTokenProviderRepository tokenProviderRepository; + private final ObjectMapper objectMapper; + + public String createAccessToken(final AuthPrincipal authPrincipal) { + return createToken(authPrincipal, TokenType.ACCESS); + } + + public String createSocialAccountToken(final SocialAccount socialAccount) { + return createToken(socialAccount.getId(), TokenType.SOCIAL_ACCOUNT); + } + + public AuthPrincipal extractAccessTokenPayload(final String accessToken) { + try { + return objectMapper.readValue( + extractPayload(accessToken, TokenType.ACCESS), + AuthPrincipal.class + ); + } catch (JsonProcessingException e) { + throw new InvalidJWTException(); + } + } + + public Long extractSocialAccountTokenPayload(final String socialAccountToken) { + try { + validateToken(socialAccountToken, TokenType.SOCIAL_ACCOUNT); + final String subject = extractPayload(socialAccountToken, TokenType.SOCIAL_ACCOUNT); + return Long.valueOf(subject); + } catch (NumberFormatException e) { + throw new InvalidSocialAccountTokenException(socialAccountToken); + } + } + + public boolean validateAccessToken(final String accessToken) { + return validateToken(accessToken, TokenType.ACCESS); + } + + private String createToken(final T payload, final TokenType type) { + final TokenProvider provider = tokenProviderRepository.getProvider(type); + final String subject = createSubject(payload); + final Claims claims = Jwts.claims() + .setSubject(subject); + final Date now = new Date(); + final Date expirationDate = new Date(now.getTime() + provider.expiration()); + return Jwts.builder() + .setClaims(claims) + .setIssuedAt(now) + .setExpiration(expirationDate) + .signWith(provider.key()) + .compact(); + } + + private String extractPayload(final String token, final TokenType type) { + final TokenProvider provider = tokenProviderRepository.getProvider(type); + return Jwts.parserBuilder() + .setSigningKey(provider.key()) + .build() + .parseClaimsJws(token) + .getBody() + .getSubject(); + } + + private boolean validateToken(final String token, final TokenType type) { + try { + final TokenProvider provider = tokenProviderRepository.getProvider(type); + final Jws claims = Jwts.parserBuilder() + .setSigningKey(provider.key()) + .build() + .parseClaimsJws(token); + return !claims.getBody() + .getExpiration() + .before(new Date()); + } catch (JwtException | IllegalArgumentException e) { + return false; + } + } + + private String createSubject(final T payload) { + try { + return objectMapper.writeValueAsString(payload); + } catch (JsonProcessingException e) { + throw new IllegalStateException(); + } + } +} diff --git a/src/main/java/ject/componote/domain/auth/token/config/TokenConfig.java b/src/main/java/ject/componote/domain/auth/token/config/TokenConfig.java new file mode 100644 index 00000000..2cfd0811 --- /dev/null +++ b/src/main/java/ject/componote/domain/auth/token/config/TokenConfig.java @@ -0,0 +1,23 @@ +package ject.componote.domain.auth.token.config; + +import ject.componote.domain.auth.token.model.TokenAdapter; +import ject.componote.domain.auth.token.model.TokenProperties; +import ject.componote.domain.auth.token.repository.InMemoryTokenProviderRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableConfigurationProperties(TokenProperties.class) +@RequiredArgsConstructor +public class TokenConfig { + private final TokenProperties properties; + + @Bean + public InMemoryTokenProviderRepository inMemoryTokenProviderRepository() { + return new InMemoryTokenProviderRepository( + TokenAdapter.getTokenProviders(properties) + ); + } +} diff --git a/src/main/java/ject/componote/domain/auth/token/error/InvalidSocialAccountTokenException.java b/src/main/java/ject/componote/domain/auth/token/error/InvalidSocialAccountTokenException.java new file mode 100644 index 00000000..d2a794c2 --- /dev/null +++ b/src/main/java/ject/componote/domain/auth/token/error/InvalidSocialAccountTokenException.java @@ -0,0 +1,13 @@ +package ject.componote.domain.auth.token.error; + +import ject.componote.domain.auth.error.AuthException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; + +@Slf4j +public class InvalidSocialAccountTokenException extends AuthException { + public InvalidSocialAccountTokenException(final String socialAccountToken) { + super("유효하지 않은 socialAccountToken 입니다.", HttpStatus.BAD_REQUEST); + log.error("socialAccountToken: {}", socialAccountToken); + } +} diff --git a/src/main/java/ject/componote/domain/auth/token/model/TokenAdapter.java b/src/main/java/ject/componote/domain/auth/token/model/TokenAdapter.java new file mode 100644 index 00000000..756e9843 --- /dev/null +++ b/src/main/java/ject/componote/domain/auth/token/model/TokenAdapter.java @@ -0,0 +1,20 @@ +package ject.componote.domain.auth.token.model; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.Map; +import java.util.stream.Collectors; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class TokenAdapter { + public static Map getTokenProviders(final TokenProperties properties) { + return properties.getToken() + .keySet() + .stream() + .collect(Collectors.toMap( + TokenType::from, + key -> TokenProvider.from(properties.getAttributeFrom(key)) + )); + } +} diff --git a/src/main/java/ject/componote/domain/auth/token/model/TokenProperties.java b/src/main/java/ject/componote/domain/auth/token/model/TokenProperties.java new file mode 100644 index 00000000..b3a47f71 --- /dev/null +++ b/src/main/java/ject/componote/domain/auth/token/model/TokenProperties.java @@ -0,0 +1,28 @@ +package ject.componote.domain.auth.token.model; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.HashMap; +import java.util.Map; + +@ConfigurationProperties(prefix = "auth") +@Getter +@ToString +public class TokenProperties { + private final Map token = new HashMap<>(); + + public Token getAttributeFrom(final String key) { + return token.get(key); + } + + @Getter + @Setter + @ToString + public static class Token { + private Long expiration; + private String secretKey; + } +} diff --git a/src/main/java/ject/componote/domain/auth/token/model/TokenProvider.java b/src/main/java/ject/componote/domain/auth/token/model/TokenProvider.java new file mode 100644 index 00000000..48705dc8 --- /dev/null +++ b/src/main/java/ject/componote/domain/auth/token/model/TokenProvider.java @@ -0,0 +1,15 @@ +package ject.componote.domain.auth.token.model; + +import io.jsonwebtoken.security.Keys; + +import java.nio.charset.StandardCharsets; +import java.security.Key; + +public record TokenProvider(Long expiration, Key key) { + public static TokenProvider from(final TokenProperties.Token attribute) { + return new TokenProvider( + attribute.getExpiration(), + Keys.hmacShaKeyFor(attribute.getSecretKey().getBytes(StandardCharsets.UTF_8)) + ); + } +} diff --git a/src/main/java/ject/componote/domain/auth/token/model/TokenType.java b/src/main/java/ject/componote/domain/auth/token/model/TokenType.java new file mode 100644 index 00000000..a3ffa7a0 --- /dev/null +++ b/src/main/java/ject/componote/domain/auth/token/model/TokenType.java @@ -0,0 +1,19 @@ +package ject.componote.domain.auth.token.model; + +import lombok.Getter; + +@Getter +public enum TokenType { + ACCESS, + REFRESH, + SOCIAL_ACCOUNT; + + public static TokenType from(final String name) { + return valueOf(toConstantCase(name)); + } + + private static String toConstantCase(final String name) { + return name.replace("-", "_") + .toUpperCase(); + } +} diff --git a/src/main/java/ject/componote/domain/auth/token/repository/InMemoryTokenProviderRepository.java b/src/main/java/ject/componote/domain/auth/token/repository/InMemoryTokenProviderRepository.java new file mode 100644 index 00000000..c513c6d2 --- /dev/null +++ b/src/main/java/ject/componote/domain/auth/token/repository/InMemoryTokenProviderRepository.java @@ -0,0 +1,16 @@ +package ject.componote.domain.auth.token.repository; + +import ject.componote.domain.auth.token.model.TokenProvider; +import ject.componote.domain.auth.token.model.TokenType; +import lombok.RequiredArgsConstructor; + +import java.util.Map; + +@RequiredArgsConstructor +public class InMemoryTokenProviderRepository { + private final Map providers; + + public TokenProvider getProvider(final TokenType type) { + return providers.get(type); + } +} diff --git a/src/main/java/ject/componote/domain/auth/util/TokenProvider.java b/src/main/java/ject/componote/domain/auth/util/TokenProvider.java deleted file mode 100644 index 4f65e666..00000000 --- a/src/main/java/ject/componote/domain/auth/util/TokenProvider.java +++ /dev/null @@ -1,79 +0,0 @@ -package ject.componote.domain.auth.util; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jws; -import io.jsonwebtoken.JwtException; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.security.Keys; -import ject.componote.domain.auth.model.AuthPrincipal; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import java.nio.charset.StandardCharsets; -import java.security.Key; -import java.util.Date; - -@Component -@Slf4j -public class TokenProvider { - private final Long expiration; - private final Key key; - private final ObjectMapper objectMapper; - - public TokenProvider(@Value("${auth.access-token.expiration}") final Long expiration, - final ObjectMapper objectMapper, - @Value("${auth.access-token.secret-key}") final String secretKey) { - this.expiration = expiration; - this.objectMapper = objectMapper; - this.key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8)); - } - - public String createToken(final AuthPrincipal authPrincipal) { - final String payload = getPrincipalPayload(authPrincipal); - final Claims claims = Jwts.claims() - .setSubject(payload); - final Date now = new Date(); - final Date expirationDate = new Date(now.getTime() + expiration); - return Jwts.builder() - .setClaims(claims) - .setIssuedAt(now) - .setExpiration(expirationDate) - .signWith(key) - .compact(); - } - - public String getPayload(final String token) { - return Jwts.parserBuilder() - .setSigningKey(key) - .build() - .parseClaimsJws(token) - .getBody() - .getSubject(); - } - - public boolean validateToken(final String token) { - try { - final Jws claims = Jwts.parserBuilder() - .setSigningKey(key) - .build() - .parseClaimsJws(token); - return !claims.getBody() - .getExpiration() - .before(new Date()); - } catch (JwtException | IllegalArgumentException e) { - log.info("JWT 검증 실패 : {}", e.getMessage()); - return false; - } - } - - private String getPrincipalPayload(final AuthPrincipal authPrincipal) { - try { - return objectMapper.writeValueAsString(authPrincipal); - } catch (JsonProcessingException e) { - throw new IllegalStateException(); - } - } -} diff --git a/src/main/java/ject/componote/global/interceptor/AuthenticationInterceptor.java b/src/main/java/ject/componote/global/interceptor/AuthenticationInterceptor.java index 4f39a751..9008b13d 100644 --- a/src/main/java/ject/componote/global/interceptor/AuthenticationInterceptor.java +++ b/src/main/java/ject/componote/global/interceptor/AuthenticationInterceptor.java @@ -1,7 +1,5 @@ package ject.componote.global.interceptor; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import ject.componote.domain.auth.dao.MemberRepository; @@ -9,7 +7,7 @@ import ject.componote.domain.auth.error.InvalidJWTException; import ject.componote.domain.auth.error.NotFoundJWTException; import ject.componote.domain.auth.model.AuthPrincipal; -import ject.componote.domain.auth.util.TokenProvider; +import ject.componote.domain.auth.token.application.TokenService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -24,17 +22,14 @@ public class AuthenticationInterceptor implements HandlerInterceptor { private final String authAttributeKey; private final MemberRepository memberRepository; - private final ObjectMapper objectMapper; - private final TokenProvider tokenProvider; + private final TokenService tokenService; - public AuthenticationInterceptor(@Value("${auth.attribute-key}") final String authAttributeKey, + public AuthenticationInterceptor(@Value("${auth.token.access.attribute-key}") final String authAttributeKey, final MemberRepository memberRepository, - final ObjectMapper objectMapper, - final TokenProvider tokenProvider) { + final TokenService tokenService) { this.authAttributeKey = authAttributeKey; this.memberRepository = memberRepository; - this.objectMapper = objectMapper; - this.tokenProvider = tokenProvider; + this.tokenService = tokenService; } @Override @@ -76,7 +71,7 @@ private String getAccessToken(final HttpServletRequest request) { } private void validateToken(final String accessToken) { - if (!tokenProvider.validateToken(accessToken)) { + if (!tokenService.validateAccessToken(accessToken)) { throw new ExpiredJWTException(); } } @@ -89,11 +84,6 @@ private void validateAuthentication(final AuthPrincipal authPrincipal) { } private AuthPrincipal getAuthPrincipal(final String accessToken) { - try { - return objectMapper.readValue(tokenProvider.getPayload(accessToken), AuthPrincipal.class); - } catch (JsonProcessingException e) { - log.info("AuthPrincipal 변환 중 에러가 발생했습니다. accessToken : {}", accessToken); - throw new InvalidJWTException(); - } + return tokenService.extractAccessTokenPayload(accessToken); } } diff --git a/src/main/java/ject/componote/global/interceptor/MemberAuthorityInterceptor.java b/src/main/java/ject/componote/global/interceptor/MemberAuthorityInterceptor.java index 1478cf6b..a44db928 100644 --- a/src/main/java/ject/componote/global/interceptor/MemberAuthorityInterceptor.java +++ b/src/main/java/ject/componote/global/interceptor/MemberAuthorityInterceptor.java @@ -17,7 +17,7 @@ public class MemberAuthorityInterceptor implements HandlerInterceptor { private final String authAttributeKey; - public MemberAuthorityInterceptor(@Value("${auth.attribute-key}") final String authAttributeKey) { + public MemberAuthorityInterceptor(@Value("${auth.token.access.attribute-key}") final String authAttributeKey) { this.authAttributeKey = authAttributeKey; } diff --git a/src/main/java/ject/componote/global/resolver/AuthPrincipalResolver.java b/src/main/java/ject/componote/global/resolver/AuthPrincipalResolver.java index e50da4c4..d0006000 100644 --- a/src/main/java/ject/componote/global/resolver/AuthPrincipalResolver.java +++ b/src/main/java/ject/componote/global/resolver/AuthPrincipalResolver.java @@ -15,7 +15,7 @@ public class AuthPrincipalResolver implements HandlerMethodArgumentResolver { private final String authAttributeKey; - public AuthPrincipalResolver(@Value("${auth.attribute-key}") final String authAttributeKey) { + public AuthPrincipalResolver(@Value("${auth.token.access.attribute-key}") final String authAttributeKey) { this.authAttributeKey = authAttributeKey; } diff --git a/src/test/java/ject/componote/domain/auth/application/AuthServiceTest.java b/src/test/java/ject/componote/domain/auth/application/AuthServiceTest.java index 740025f6..6e2b7d19 100644 --- a/src/test/java/ject/componote/domain/auth/application/AuthServiceTest.java +++ b/src/test/java/ject/componote/domain/auth/application/AuthServiceTest.java @@ -12,7 +12,7 @@ import ject.componote.domain.auth.error.NotFoundSocialAccountException; import ject.componote.domain.auth.model.AuthPrincipal; import ject.componote.domain.auth.model.ProfileImage; -import ject.componote.domain.auth.util.TokenProvider; +import ject.componote.domain.auth.token.application.TokenService; import ject.componote.infra.storage.application.StorageService; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -42,11 +42,12 @@ class AuthServiceTest { SocialAccountRepository socialAccountRepository; @Mock - TokenProvider tokenProvider; + TokenService tokenService; @InjectMocks AuthService authService; + String socialAccountToken = "hello"; Long socialAccountId = 1L; Member member = KIM.생성(socialAccountId); ProfileImage profileImage = member.getProfileImage(); @@ -60,12 +61,14 @@ public void signup() throws Exception { member.getNickname().getValue(), member.getJob().name(), profileImageObjectKey, - socialAccountId + socialAccountToken ); final String accessToken = "accessToken"; final MemberSignupResponse expect = MemberSignupResponse.of(accessToken, member); // when + doReturn(socialAccountId).when(tokenService) + .extractSocialAccountTokenPayload(socialAccountToken); doReturn(true).when(socialAccountRepository) .existsById(socialAccountId); doReturn(false).when(memberRepository) @@ -74,8 +77,8 @@ public void signup() throws Exception { .save(any()); doNothing().when(storageService) .moveImage(profileImage); - doReturn(accessToken).when(tokenProvider) - .createToken(AuthPrincipal.from(member)); + doReturn(accessToken).when(tokenService) + .createAccessToken(AuthPrincipal.from(member)); final MemberSignupResponse actual = authService.signup(request); // then @@ -90,10 +93,12 @@ public void signupWhenAlreadyExist() throws Exception { member.getNickname().getValue(), member.getJob().name(), profileImageObjectKey, - socialAccountId + socialAccountToken ); // when + doReturn(socialAccountId).when(tokenService) + .extractSocialAccountTokenPayload(socialAccountToken); doReturn(true).when(socialAccountRepository) .existsById(socialAccountId); doReturn(true).when(memberRepository) @@ -112,10 +117,12 @@ public void signupWhenInvalidSocialAccountId() throws Exception { member.getNickname().getValue(), member.getJob().name(), profileImageObjectKey, - socialAccountId + socialAccountToken ); // when + doReturn(socialAccountId).when(tokenService) + .extractSocialAccountTokenPayload(socialAccountToken); doReturn(false).when(socialAccountRepository) .existsById(socialAccountId); @@ -129,14 +136,16 @@ public void signupWhenInvalidSocialAccountId() throws Exception { public void login() throws Exception { // given final String accessToken = "accessToken"; - final MemberLoginRequest request = new MemberLoginRequest(socialAccountId); + final MemberLoginRequest request = new MemberLoginRequest(socialAccountToken); final MemberLoginResponse expect = MemberLoginResponse.of(accessToken, member); // when + doReturn(socialAccountId).when(tokenService) + .extractSocialAccountTokenPayload(socialAccountToken); doReturn(Optional.of(member)).when(memberRepository) .findBySocialAccountId(socialAccountId); - doReturn(accessToken).when(tokenProvider) - .createToken(AuthPrincipal.from(member)); + doReturn(accessToken).when(tokenService) + .createAccessToken(AuthPrincipal.from(member)); final MemberLoginResponse actual = authService.login(request); // then @@ -147,9 +156,11 @@ public void login() throws Exception { @Test public void loginWhenInvalidSocialAccountId() throws Exception { // given - final MemberLoginRequest request = new MemberLoginRequest(socialAccountId); + final MemberLoginRequest request = new MemberLoginRequest(socialAccountToken); // when + doReturn(socialAccountId).when(tokenService) + .extractSocialAccountTokenPayload(socialAccountToken); doReturn(Optional.empty()).when(memberRepository) .findBySocialAccountId(socialAccountId); diff --git a/src/test/java/ject/componote/domain/auth/application/OAuthServiceTest.java b/src/test/java/ject/componote/domain/auth/application/OAuthServiceTest.java index dc56d70e..38e50177 100644 --- a/src/test/java/ject/componote/domain/auth/application/OAuthServiceTest.java +++ b/src/test/java/ject/componote/domain/auth/application/OAuthServiceTest.java @@ -4,6 +4,7 @@ import ject.componote.domain.auth.domain.SocialAccount; import ject.componote.domain.auth.dto.authorize.response.OAuthAuthorizationUrlResponse; import ject.componote.domain.auth.dto.login.response.OAuthLoginResponse; +import ject.componote.domain.auth.token.application.TokenService; import ject.componote.infra.oauth.application.OAuthClient; import ject.componote.infra.oauth.dto.authorize.response.OAuthAuthorizePayload; import ject.componote.infra.oauth.error.InvalidAuthorizationCodeException; @@ -22,7 +23,6 @@ import reactor.core.publisher.Mono; import java.net.URI; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -43,6 +43,9 @@ class OAuthServiceTest { @Mock OAuthResultHandler oAuthResultHandler; + @Mock + TokenService tokenService; + @InjectMocks OAuthService oAuthService; @@ -85,11 +88,14 @@ public void login(final String providerType) throws Exception { final OAuthProvider oAuthProvider = getOAuthProvider(providerType); final OAuthProfile oAuthProfile = getOAuthProfile(oAuthProvider); final SocialAccount socialAccount = KIM.생성(providerType); - final OAuthLoginResponse expect = OAuthLoginResponse.of(true, socialAccount); + final String socialAccountToken = "hello"; + final OAuthLoginResponse expect = OAuthLoginResponse.of(true, socialAccountToken); // when + doReturn(socialAccountToken).when(tokenService) + .createSocialAccountToken(socialAccount); doReturn(Mono.just(oAuthProfile)).when(oAuthClient) - .getProfile(providerType, code); + .getProfile(providerType, code); doReturn(socialAccount).when(oAuthResultHandler) .saveOrGet(oAuthProfile); doReturn(true).when(memberRepository)