From 2572b87b3516bd5fb82fa8aec69a25f47dd15cb7 Mon Sep 17 00:00:00 2001 From: pkl0912 Date: Mon, 7 Oct 2024 17:28:51 +0900 Subject: [PATCH 01/14] =?UTF-8?q?chore:=20token=20record=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/server/poptato/global/dto/TokenPair.java | 6 ++++++ .../java/server/poptato/global/dto/UserCreateResponse.java | 7 +++++++ 2 files changed, 13 insertions(+) create mode 100644 src/main/java/server/poptato/global/dto/TokenPair.java create mode 100644 src/main/java/server/poptato/global/dto/UserCreateResponse.java diff --git a/src/main/java/server/poptato/global/dto/TokenPair.java b/src/main/java/server/poptato/global/dto/TokenPair.java new file mode 100644 index 0000000..872b327 --- /dev/null +++ b/src/main/java/server/poptato/global/dto/TokenPair.java @@ -0,0 +1,6 @@ +package server.poptato.global.dto; + +public record TokenPair( + String accessToken, String refreshToken +) { +} diff --git a/src/main/java/server/poptato/global/dto/UserCreateResponse.java b/src/main/java/server/poptato/global/dto/UserCreateResponse.java new file mode 100644 index 0000000..eda7edd --- /dev/null +++ b/src/main/java/server/poptato/global/dto/UserCreateResponse.java @@ -0,0 +1,7 @@ +package server.poptato.global.dto; + +public record UserCreateResponse( + String accessToken, + String refreshToken +) { +} From b8c7df5ffa3eb9ecd561c5b85c7e87b2701f71e4 Mon Sep 17 00:00:00 2001 From: pkl0912 Date: Mon, 7 Oct 2024 17:52:27 +0900 Subject: [PATCH 02/14] =?UTF-8?q?chore:=20config=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 14 +- .../java/server/poptato/config/WebConfig.java | 36 +++++ .../server/poptato/config/jwt/JwtService.java | 136 ++++++++++++++++++ .../poptato/config/redis/RedisConfig.java | 34 +++++ .../config/resolver/kakao/KakaoCode.java | 11 ++ .../resolver/kakao/KakaoCodeResolver.java | 38 +++++ .../poptato/config/resolver/user/UserId.java | 11 ++ .../config/resolver/user/UserResolver.java | 47 ++++++ .../errorcode/BaseExceptionErrorCode.java | 4 + 9 files changed, 329 insertions(+), 2 deletions(-) create mode 100644 src/main/java/server/poptato/config/WebConfig.java create mode 100644 src/main/java/server/poptato/config/jwt/JwtService.java create mode 100644 src/main/java/server/poptato/config/redis/RedisConfig.java create mode 100644 src/main/java/server/poptato/config/resolver/kakao/KakaoCode.java create mode 100644 src/main/java/server/poptato/config/resolver/kakao/KakaoCodeResolver.java create mode 100644 src/main/java/server/poptato/config/resolver/user/UserId.java create mode 100644 src/main/java/server/poptato/config/resolver/user/UserResolver.java diff --git a/build.gradle b/build.gradle index 5d11fdc..053341f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { id 'java' - id 'org.springframework.boot' version '3.3.4' - id 'io.spring.dependency-management' version '1.1.6' + id 'org.springframework.boot' version '3.2.1' + id 'io.spring.dependency-management' version '1.1.4' } group = 'server' @@ -48,6 +48,16 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testImplementation 'com.h2database:h2' + + implementation 'com.squareup.okhttp3:okhttp:4.9.3' + implementation 'com.google.code.gson:gson:2.9.0' + + //JWT + implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.2' + implementation group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.2' + implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2' + + implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.1.0' } tasks.named('test') { diff --git a/src/main/java/server/poptato/config/WebConfig.java b/src/main/java/server/poptato/config/WebConfig.java new file mode 100644 index 0000000..45543be --- /dev/null +++ b/src/main/java/server/poptato/config/WebConfig.java @@ -0,0 +1,36 @@ +package server.poptato.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import server.poptato.config.resolver.kakao.KakaoCodeResolver; +import server.poptato.config.resolver.user.UserResolver; + +import java.util.List; + +@Configuration +@RequiredArgsConstructor +public class WebConfig implements WebMvcConfigurer { + private final UserResolver userResolver; + private final KakaoCodeResolver kakaoCodeResolver; + + @Override + public void addCorsMappings(final CorsRegistry registry) { + registry.addMapping("/**") + .allowedOriginPatterns("*") + .allowedMethods(HttpMethod.GET.name(), HttpMethod.POST.name(), HttpMethod.PUT.name(), HttpMethod.DELETE.name()) + .allowedHeaders("Authorization", "Content-Type") + .allowCredentials(true) + .maxAge(3000); + } + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(userResolver); + resolvers.add(kakaoCodeResolver); + + } +} \ No newline at end of file diff --git a/src/main/java/server/poptato/config/jwt/JwtService.java b/src/main/java/server/poptato/config/jwt/JwtService.java new file mode 100644 index 0000000..0434a2f --- /dev/null +++ b/src/main/java/server/poptato/config/jwt/JwtService.java @@ -0,0 +1,136 @@ +package server.poptato.config.jwt; + + + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Header; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import server.poptato.global.dto.TokenPair; +import server.poptato.global.exception.BaseException; +import server.poptato.global.response.BaseErrorResponse; + +import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.util.Base64; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +import static server.poptato.global.exception.errorcode.BaseExceptionErrorCode.TOKEN_TIME_EXPIRED_EXCEPTION; + + +@Service +@RequiredArgsConstructor +public class JwtService { + @Value("${jwt.secret}") + private String jwtSecret; + private static final String USER_ID = "USER_ID"; + private static final String ACCESS_TOKEN = "ACCESS_TOKEN"; + private static final String REFRESH_TOKEN = "REFRESH_TOKEN"; + public static final int MINUTE_IN_MILLISECONDS = 60 * 1000; + public static final long DAYS_IN_MILLISECONDS = 24 * 60 * 60 * 1000L; + public static final int ACCESS_TOKEN_EXPIRATION_MINUTE = 10; + public static final int REFRESH_TOKEN_EXPIRATION_DAYS = 14; + private final RedisTemplate redisTemplate; + + @PostConstruct + protected void init() { + jwtSecret = Base64.getEncoder() + .encodeToString(jwtSecret.getBytes(StandardCharsets.UTF_8)); + } + + public String createAccessToken(final String userId) { + final Claims claims = getAccessTokenClaims(); + + claims.put(USER_ID, userId); + return createToken(claims); + } + + public String createRefreshToken(final String userId) { + final Claims claims = getRefreshTokenClaims(); + + claims.put(USER_ID, userId); + return createToken(claims); + } + + public boolean verifyToken(final String token) { + try { + final Claims claims = getBody(token); + return true; + } catch (RuntimeException e) { + if (e instanceof ExpiredJwtException) { + throw new BaseException(TOKEN_TIME_EXPIRED_EXCEPTION); + } + return false; + } + } + + public String getUserIdInToken(final String token) { + final Claims claims = getBody(token); + return (String) claims.get(USER_ID); + } + + public TokenPair generateTokenPair(final String userId) { + final String accessToken = createAccessToken(userId); + final String refreshToken = createRefreshToken(userId); + saveRefreshToken(userId, refreshToken); + return new TokenPair(accessToken, refreshToken); + } + + public boolean compareRefreshToken(final String userId, final String refreshToken) { + final String storedRefreshToken = redisTemplate.opsForValue().get(userId); + if (storedRefreshToken == null) return false; + return storedRefreshToken.equals(refreshToken); + } + + public void saveRefreshToken(final String userId, final String refreshToken) { + redisTemplate.opsForValue().set(userId, refreshToken, REFRESH_TOKEN_EXPIRATION_DAYS, TimeUnit.DAYS); + } + + private String createToken(final Claims claims) { + return Jwts.builder() + .setHeaderParam(Header.TYPE, Header.JWT_TYPE) + .setClaims(claims) + .signWith(getSigningKey()) + .compact(); + } + + private Claims getRefreshTokenClaims() { + final Date now = new Date(); + return Jwts.claims() + .setSubject(REFRESH_TOKEN) + .setIssuedAt(now) + .setExpiration(new Date(now.getTime() + REFRESH_TOKEN_EXPIRATION_DAYS * DAYS_IN_MILLISECONDS)); + } + + private Claims getAccessTokenClaims() { + final Date now = new Date(); + return Jwts.claims() + .setSubject(ACCESS_TOKEN) + .setIssuedAt(now) + .setExpiration(new Date(now.getTime() + ACCESS_TOKEN_EXPIRATION_MINUTE * MINUTE_IN_MILLISECONDS)); + } + + private Claims getBody(final String token) { + return Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build() + .parseClaimsJws(token) + .getBody(); + } + + private Key getSigningKey() { + final byte[] keyBytes = jwtSecret.getBytes(StandardCharsets.UTF_8); + return Keys.hmacShaKeyFor(keyBytes); + } + + public void deleteRefreshToken(final String userId) { + redisTemplate.delete(userId); + } +} diff --git a/src/main/java/server/poptato/config/redis/RedisConfig.java b/src/main/java/server/poptato/config/redis/RedisConfig.java new file mode 100644 index 0000000..a3b853e --- /dev/null +++ b/src/main/java/server/poptato/config/redis/RedisConfig.java @@ -0,0 +1,34 @@ +package server.poptato.config.redis; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +public class RedisConfig { + @Value("${spring.data.redis.host}") + private String host; + + @Value("${spring.data.redis.port}") + private int port; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + return new LettuceConnectionFactory(host, port); + } + + @Bean + @Primary + public RedisTemplate getRedisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + return redisTemplate; + } +} diff --git a/src/main/java/server/poptato/config/resolver/kakao/KakaoCode.java b/src/main/java/server/poptato/config/resolver/kakao/KakaoCode.java new file mode 100644 index 0000000..e7d4622 --- /dev/null +++ b/src/main/java/server/poptato/config/resolver/kakao/KakaoCode.java @@ -0,0 +1,11 @@ +package server.poptato.config.resolver.kakao; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface KakaoCode { +} diff --git a/src/main/java/server/poptato/config/resolver/kakao/KakaoCodeResolver.java b/src/main/java/server/poptato/config/resolver/kakao/KakaoCodeResolver.java new file mode 100644 index 0000000..1c64f24 --- /dev/null +++ b/src/main/java/server/poptato/config/resolver/kakao/KakaoCodeResolver.java @@ -0,0 +1,38 @@ +package server.poptato.config.resolver.kakao; + + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; +import server.poptato.global.exception.BaseException; +import server.poptato.global.response.BaseErrorResponse; + +import static server.poptato.global.exception.errorcode.BaseExceptionErrorCode.EMPTY_KAKAO_CODE_EXCEPTION; +import static server.poptato.global.exception.errorcode.BaseExceptionErrorCode.NO_REQUEST_PARAMETER; + + +@Component +@RequiredArgsConstructor +public class KakaoCodeResolver implements HandlerMethodArgumentResolver { + private static final String AUTHORIZATION = "authorization"; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(KakaoCode.class) && String.class.equals(parameter.getParameterType()); + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + final HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); + final String token = request.getHeader(AUTHORIZATION); + if (token == null || token.isBlank() || !token.startsWith("Bearer ")) { + throw new BaseException(EMPTY_KAKAO_CODE_EXCEPTION); + } + return token.substring("Bearer ".length()); + } +} diff --git a/src/main/java/server/poptato/config/resolver/user/UserId.java b/src/main/java/server/poptato/config/resolver/user/UserId.java new file mode 100644 index 0000000..bf46ab0 --- /dev/null +++ b/src/main/java/server/poptato/config/resolver/user/UserId.java @@ -0,0 +1,11 @@ +package server.poptato.config.resolver.user; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface UserId { +} diff --git a/src/main/java/server/poptato/config/resolver/user/UserResolver.java b/src/main/java/server/poptato/config/resolver/user/UserResolver.java new file mode 100644 index 0000000..9ad250b --- /dev/null +++ b/src/main/java/server/poptato/config/resolver/user/UserResolver.java @@ -0,0 +1,47 @@ +package server.poptato.config.resolver.user; + + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; +import server.poptato.config.jwt.JwtService; +import server.poptato.global.exception.BaseException; +import server.poptato.global.response.BaseErrorResponse; + +import static server.poptato.global.exception.errorcode.BaseExceptionErrorCode.*; + + +@Component +@RequiredArgsConstructor +public class UserResolver implements HandlerMethodArgumentResolver { + private final JwtService jwtService; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(UserId.class) && Long.class.equals(parameter.getParameterType()); + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + final HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); + final String token = request.getHeader("Authorization"); + if (token == null || token.isBlank() || !token.startsWith("Bearer ")) { + throw new BaseException(TOKEN_NOT_CONTAINED_EXCEPTION); + } + final String encodedUserId = token.substring("Bearer ".length()); + if (!jwtService.verifyToken(encodedUserId)) { + throw new BaseException(TOKEN_TIME_EXPIRED_EXCEPTION); + } + final String decodedUserId = jwtService.getUserIdInToken(encodedUserId); + try { + return Long.parseLong(decodedUserId); + } catch (NumberFormatException e) { + return new BaseErrorResponse(INVALID_TOKEN_EXCEPTION); + } + } +} diff --git a/src/main/java/server/poptato/global/exception/errorcode/BaseExceptionErrorCode.java b/src/main/java/server/poptato/global/exception/errorcode/BaseExceptionErrorCode.java index e127d6a..8d7c932 100644 --- a/src/main/java/server/poptato/global/exception/errorcode/BaseExceptionErrorCode.java +++ b/src/main/java/server/poptato/global/exception/errorcode/BaseExceptionErrorCode.java @@ -21,6 +21,10 @@ public enum BaseExceptionErrorCode implements ResponseStatus { INAPPROPRIATE_DATA(2003, HttpStatus.BAD_REQUEST.value(), "입력한 정보가 올바르지 않습니다."), NO_COOKIE(2004,HttpStatus.BAD_REQUEST.value(),"쿠키가 존재하지 않습니다."), NO_REQUEST_PARAMETER(2005,HttpStatus.BAD_REQUEST.value(),"요청 파라미터는 필수로 입력해야합니다."), + TOKEN_NOT_CONTAINED_EXCEPTION(2006,HttpStatus.BAD_REQUEST.value(),"토큰은 필수로 입력해야합니다."), + TOKEN_TIME_EXPIRED_EXCEPTION(2007,HttpStatus.BAD_REQUEST.value(),"토큰 시간이 만료되었습니다."), + INVALID_TOKEN_EXCEPTION(2008,HttpStatus.BAD_REQUEST.value(),"토큰이 유효하지 않습니다."), + EMPTY_KAKAO_CODE_EXCEPTION(2009,HttpStatus.BAD_REQUEST.value(),"카카오 코드를 입력해야 합니다"), /** * 3000: Server, Database 오류 (INTERNAL_SERVER_ERROR) From f37f39467d6be03f5ef1b1af2f7ba5f3edd83326 Mon Sep 17 00:00:00 2001 From: pkl0912 Date: Mon, 7 Oct 2024 17:59:55 +0900 Subject: [PATCH 03/14] =?UTF-8?q?chore:=20auth=20dto=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../poptato/auth/application/dto/request/TokenRequestDto.java | 4 ++++ .../auth/application/dto/response/LoginResponseDto.java | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 src/main/java/server/poptato/auth/application/dto/request/TokenRequestDto.java create mode 100644 src/main/java/server/poptato/auth/application/dto/response/LoginResponseDto.java diff --git a/src/main/java/server/poptato/auth/application/dto/request/TokenRequestDto.java b/src/main/java/server/poptato/auth/application/dto/request/TokenRequestDto.java new file mode 100644 index 0000000..29ea5a1 --- /dev/null +++ b/src/main/java/server/poptato/auth/application/dto/request/TokenRequestDto.java @@ -0,0 +1,4 @@ +package server.poptato.auth.application.dto.request; + +public record TokenRequestDto(String accessToken, String refreshToken) { +} diff --git a/src/main/java/server/poptato/auth/application/dto/response/LoginResponseDto.java b/src/main/java/server/poptato/auth/application/dto/response/LoginResponseDto.java new file mode 100644 index 0000000..0bab349 --- /dev/null +++ b/src/main/java/server/poptato/auth/application/dto/response/LoginResponseDto.java @@ -0,0 +1,4 @@ +package server.poptato.auth.application.dto.response; + +public record LoginResponseDto(String accessToken, String refreshToken) { +} From ceca9bda7df977b8607d8e7456bd285592325c4a Mon Sep 17 00:00:00 2001 From: pkl0912 Date: Mon, 7 Oct 2024 18:06:54 +0900 Subject: [PATCH 04/14] =?UTF-8?q?feat:=20Auth=20service,=20controller=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../poptato/auth/api/AuthController.java | 41 ++++++++++++ .../auth/application/service/AuthService.java | 62 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/main/java/server/poptato/auth/api/AuthController.java create mode 100644 src/main/java/server/poptato/auth/application/service/AuthService.java diff --git a/src/main/java/server/poptato/auth/api/AuthController.java b/src/main/java/server/poptato/auth/api/AuthController.java new file mode 100644 index 0000000..32c2b04 --- /dev/null +++ b/src/main/java/server/poptato/auth/api/AuthController.java @@ -0,0 +1,41 @@ +package server.poptato.auth.api; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import server.poptato.auth.application.dto.response.LoginResponseDto; +import server.poptato.config.resolver.kakao.KakaoCode; +import server.poptato.global.response.BaseResponse; + +@RestController +@RequestMapping("/auth") +@RequiredArgsConstructor +public class AuthController { + + private static final String ORIGIN = "origin"; + private final AuthService authService; + + @PostMapping("/login") + public BaseResponse login( + @KakaoCode String kakaoCode, + HttpServletRequest request) { + String originHeader = request.getHeader(ORIGIN); + authService.login(originHeader, kakaoCode) + return BaseResponse.success(SOCIAL_LOGIN_SUCCESS, ); + } + + @PostMapping("/logout") + public SuccessNonDataResponse logout(HttpServletRequest request) { + Long userId = (Long) request.getAttribute("userId"); // `userId`를 헤더에서 가져온다고 가정 + authService.logout(userId); + return SuccessNonDataResponse.success(LOGOUT_SUCCESS); + } + + @PostMapping("/refresh") + public SuccessResponse refresh(@RequestBody final TokenRequestDto tokenRequestDto) { + return SuccessResponse.success(REFRESH_SUCCESS, authService.refresh(tokenRequestDto)); + } +} diff --git a/src/main/java/server/poptato/auth/application/service/AuthService.java b/src/main/java/server/poptato/auth/application/service/AuthService.java new file mode 100644 index 0000000..069030c --- /dev/null +++ b/src/main/java/server/poptato/auth/application/service/AuthService.java @@ -0,0 +1,62 @@ +package server.poptato.auth.application.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import server.poptato.auth.application.dto.response.LoginResponseDto; +import server.poptato.config.jwt.JwtService; + +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class AuthService { + private final JwtService jwtService; + private final KakaoSocialService kakaoSocialService; + private final UserRepository userRepository; + + public LoginResponseDto login(final String baseUrl, final String kakaoCode) { + String kakaoId = kakaoSocialService.getIdFromKakao(baseUrl, kakaoCode); + Optional user = userRepository.findByKakaoId(kakaoId); + if (user.isEmpty()) { + User newUser = User.builder() + .kakaoId(kakaoId) + .build(); + userRepository.save(newUser); + + TokenPair tokenPair = jwtService.generateTokenPair(String.valueOf(newUser.getId())); + throw new NotFoundUserException(USER_NOT_FOUND_EXCEPTION, tokenPair); + } + TokenPair tokenPair = jwtService.generateTokenPair(String.valueOf(user.get().getId())); + return new LoginResponseDto(tokenPair.accessToken(), tokenPair.refreshToken()); + } + + public UserCreateResponse createUserToken(String useId) { + + TokenPair tokenPair = jwtService.generateTokenPair(useId); + UserCreateResponse userCreateResponse = new UserCreateResponse(tokenPair.accessToken(), tokenPair.refreshToken()); + + return userCreateResponse; + } + + + + public void logout(final Long userId) { + final User user = userRepository.findById(userId).orElseThrow(() -> new NotFoundException(USER_NOT_FOUND_EXCEPTION)); + jwtService.deleteRefreshToken(String.valueOf(userId)); + } + + public TokenPair refresh(final TokenRequestDto tokenRequestDto) { + if (!jwtService.verifyToken(tokenRequestDto.refreshToken())) + throw new UnAuthorizedException(TOKEN_TIME_EXPIRED_EXCEPTION); + + final String userId = jwtService.getUserIdInToken(tokenRequestDto.refreshToken()); + final User user = userRepository.findById(Long.parseLong(userId)).orElseThrow(() -> new NotFoundException(USER_NOT_FOUND_EXCEPTION)); + + if (!jwtService.compareRefreshToken(userId, tokenRequestDto.refreshToken())) + throw new UnAuthorizedException(TOKEN_TIME_EXPIRED_EXCEPTION); + + final TokenPair tokenPair = jwtService.generateTokenPair(userId); + jwtService.saveRefreshToken(userId, tokenPair.refreshToken()); + return tokenPair; + } +} From 3321ca91e1d9f1fee2dd4054a5ee31ab46af286a Mon Sep 17 00:00:00 2001 From: pkl0912 Date: Mon, 7 Oct 2024 18:10:37 +0900 Subject: [PATCH 05/14] =?UTF-8?q?feat:=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?-=20external=20=ED=8C=A8=ED=82=A4=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../external/kakao/SocialPlatform.java | 13 +++++++ .../external/kakao/SocialServiceProvider.java | 30 +++++++++++++++ .../response/KakaoAccessTokenResponse.java | 8 ++++ .../kakao/dto/response/KakaoUserResponse.java | 4 ++ .../external/kakao/feign/KakaoApiClient.java | 19 ++++++++++ .../kakao/feign/KakaoAuthApiClient.java | 21 +++++++++++ .../kakao/service/KakaoSocialService.java | 37 +++++++++++++++++++ .../external/kakao/service/SocialService.java | 7 ++++ 8 files changed, 139 insertions(+) create mode 100644 src/main/java/server/poptato/external/kakao/SocialPlatform.java create mode 100644 src/main/java/server/poptato/external/kakao/SocialServiceProvider.java create mode 100644 src/main/java/server/poptato/external/kakao/dto/response/KakaoAccessTokenResponse.java create mode 100644 src/main/java/server/poptato/external/kakao/dto/response/KakaoUserResponse.java create mode 100644 src/main/java/server/poptato/external/kakao/feign/KakaoApiClient.java create mode 100644 src/main/java/server/poptato/external/kakao/feign/KakaoAuthApiClient.java create mode 100644 src/main/java/server/poptato/external/kakao/service/KakaoSocialService.java create mode 100644 src/main/java/server/poptato/external/kakao/service/SocialService.java diff --git a/src/main/java/server/poptato/external/kakao/SocialPlatform.java b/src/main/java/server/poptato/external/kakao/SocialPlatform.java new file mode 100644 index 0000000..ee34584 --- /dev/null +++ b/src/main/java/server/poptato/external/kakao/SocialPlatform.java @@ -0,0 +1,13 @@ +package server.poptato.external.kakao; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public enum SocialPlatform { + KAKAO("카카오"); + + private final String value; +} diff --git a/src/main/java/server/poptato/external/kakao/SocialServiceProvider.java b/src/main/java/server/poptato/external/kakao/SocialServiceProvider.java new file mode 100644 index 0000000..8b98435 --- /dev/null +++ b/src/main/java/server/poptato/external/kakao/SocialServiceProvider.java @@ -0,0 +1,30 @@ +package server.poptato.external.kakao; + + + +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import server.poptato.external.kakao.service.KakaoSocialService; +import server.poptato.external.kakao.service.SocialService; + +import java.util.HashMap; +import java.util.Map; + +@Component +@RequiredArgsConstructor +public class SocialServiceProvider { + + private static final Map socialServiceMap = new HashMap<>(); + + private final KakaoSocialService kakaoSocialService; + + @PostConstruct + void initializeSocialServiceMap() { + socialServiceMap.put(SocialPlatform.KAKAO, kakaoSocialService); + } + + public SocialService getSocialService(SocialPlatform socialPlatform) { + return socialServiceMap.get(socialPlatform); + } +} diff --git a/src/main/java/server/poptato/external/kakao/dto/response/KakaoAccessTokenResponse.java b/src/main/java/server/poptato/external/kakao/dto/response/KakaoAccessTokenResponse.java new file mode 100644 index 0000000..566a5df --- /dev/null +++ b/src/main/java/server/poptato/external/kakao/dto/response/KakaoAccessTokenResponse.java @@ -0,0 +1,8 @@ +package server.poptato.external.kakao.dto.response; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public record KakaoAccessTokenResponse(String accessToken, String refreshToken) { +} diff --git a/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserResponse.java b/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserResponse.java new file mode 100644 index 0000000..8092a6a --- /dev/null +++ b/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserResponse.java @@ -0,0 +1,4 @@ +package server.poptato.external.kakao.dto.response; + +public record KakaoUserResponse(Long id) { +} diff --git a/src/main/java/server/poptato/external/kakao/feign/KakaoApiClient.java b/src/main/java/server/poptato/external/kakao/feign/KakaoApiClient.java new file mode 100644 index 0000000..a742e64 --- /dev/null +++ b/src/main/java/server/poptato/external/kakao/feign/KakaoApiClient.java @@ -0,0 +1,19 @@ +package server.poptato.external.kakao.feign; + + + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.http.HttpHeaders; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import server.poptato.external.kakao.dto.response.KakaoUserResponse; + + +@ComponentScan +@FeignClient(name = "kakaoApiClient", url = "https://kapi.kakao.com") +public interface KakaoApiClient { + + @GetMapping(value = "/v2/user/me") + KakaoUserResponse getUserInformation(@RequestHeader(HttpHeaders.AUTHORIZATION) String accessToken); +} diff --git a/src/main/java/server/poptato/external/kakao/feign/KakaoAuthApiClient.java b/src/main/java/server/poptato/external/kakao/feign/KakaoAuthApiClient.java new file mode 100644 index 0000000..9336827 --- /dev/null +++ b/src/main/java/server/poptato/external/kakao/feign/KakaoAuthApiClient.java @@ -0,0 +1,21 @@ +package server.poptato.external.kakao.feign; + + + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import server.poptato.external.kakao.dto.response.KakaoAccessTokenResponse; + +@FeignClient(name = "kakaoAuthApiClient", url = "https://kauth.kakao.com") +public interface KakaoAuthApiClient { + + @PostMapping(value = "/oauth/token", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + KakaoAccessTokenResponse getOAuth2AccessToken( + @RequestParam("grant_type") String grantType, + @RequestParam("client_id") String clientId, + @RequestParam("redirect_uri") String redirectUri, + @RequestParam("code") String code + ); +} diff --git a/src/main/java/server/poptato/external/kakao/service/KakaoSocialService.java b/src/main/java/server/poptato/external/kakao/service/KakaoSocialService.java new file mode 100644 index 0000000..3e7e410 --- /dev/null +++ b/src/main/java/server/poptato/external/kakao/service/KakaoSocialService.java @@ -0,0 +1,37 @@ +package server.poptato.external.kakao.service; + + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import server.poptato.external.kakao.dto.response.KakaoAccessTokenResponse; +import server.poptato.external.kakao.dto.response.KakaoUserResponse; +import server.poptato.external.kakao.feign.KakaoApiClient; +import server.poptato.external.kakao.feign.KakaoAuthApiClient; + +@Service +@RequiredArgsConstructor +public class KakaoSocialService extends SocialService { + + @Value("${kakao.client-id}") + private String clientId; + private static final String Bearer = "Bearer "; + private static final String GRANT_TYPE = "authorization_code"; + private static final String KAKAO_ROUTER = "/login/oauth2/code/kakao"; + private final KakaoAuthApiClient kakaoAuthApiClient; + private final KakaoApiClient kakaoApiClient; + + @Override + public String getIdFromKakao(String baseUrl, String kakaoCode) { + String redirectUrl = baseUrl + KAKAO_ROUTER; + KakaoAccessTokenResponse tokenResponse = kakaoAuthApiClient.getOAuth2AccessToken( + GRANT_TYPE, + clientId, + redirectUrl, + kakaoCode + ); + + KakaoUserResponse userResponse = kakaoApiClient.getUserInformation(Bearer + tokenResponse.accessToken()); + return String.valueOf(userResponse.id()); + } +} diff --git a/src/main/java/server/poptato/external/kakao/service/SocialService.java b/src/main/java/server/poptato/external/kakao/service/SocialService.java new file mode 100644 index 0000000..719f3c5 --- /dev/null +++ b/src/main/java/server/poptato/external/kakao/service/SocialService.java @@ -0,0 +1,7 @@ +package server.poptato.external.kakao.service; + + +public abstract class SocialService { + public abstract String getIdFromKakao(String baseUrl, String kakaoCode); + +} \ No newline at end of file From 1edacc531bb02cbe8a1574abc155d8396d56085c Mon Sep 17 00:00:00 2001 From: pkl0912 Date: Mon, 7 Oct 2024 18:31:28 +0900 Subject: [PATCH 06/14] =?UTF-8?q?chore:=20user=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EB=B0=8F=20=EC=97=94=ED=8B=B0=EB=94=94=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20response=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/poptato/PoptatoApplication.java | 2 ++ .../poptato/auth/api/AuthController.java | 18 ++++++++----- .../auth/application/service/AuthService.java | 20 ++++++++++---- .../errorcode/BaseExceptionErrorCode.java | 1 + .../poptato/user/domain/entity/User.java | 27 +++++++++++++++++++ .../domain/repository/UserRepository.java | 10 +++++++ 6 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 src/main/java/server/poptato/user/domain/entity/User.java create mode 100644 src/main/java/server/poptato/user/domain/repository/UserRepository.java diff --git a/src/main/java/server/poptato/PoptatoApplication.java b/src/main/java/server/poptato/PoptatoApplication.java index 7e98553..3baf390 100644 --- a/src/main/java/server/poptato/PoptatoApplication.java +++ b/src/main/java/server/poptato/PoptatoApplication.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication +@EnableFeignClients public class PoptatoApplication { public static void main(String[] args) { diff --git a/src/main/java/server/poptato/auth/api/AuthController.java b/src/main/java/server/poptato/auth/api/AuthController.java index 32c2b04..3d2f4db 100644 --- a/src/main/java/server/poptato/auth/api/AuthController.java +++ b/src/main/java/server/poptato/auth/api/AuthController.java @@ -6,10 +6,15 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import server.poptato.auth.application.dto.request.TokenRequestDto; import server.poptato.auth.application.dto.response.LoginResponseDto; +import server.poptato.auth.application.service.AuthService; import server.poptato.config.resolver.kakao.KakaoCode; +import server.poptato.global.dto.TokenPair; import server.poptato.global.response.BaseResponse; +import static server.poptato.global.exception.errorcode.BaseExceptionErrorCode.SUCCESS; + @RestController @RequestMapping("/auth") @RequiredArgsConstructor @@ -23,19 +28,20 @@ public BaseResponse login( @KakaoCode String kakaoCode, HttpServletRequest request) { String originHeader = request.getHeader(ORIGIN); - authService.login(originHeader, kakaoCode) - return BaseResponse.success(SOCIAL_LOGIN_SUCCESS, ); + LoginResponseDto response = authService.login(originHeader, kakaoCode); + return new BaseResponse<>(response); } @PostMapping("/logout") - public SuccessNonDataResponse logout(HttpServletRequest request) { + public BaseResponse logout(HttpServletRequest request) { Long userId = (Long) request.getAttribute("userId"); // `userId`를 헤더에서 가져온다고 가정 authService.logout(userId); - return SuccessNonDataResponse.success(LOGOUT_SUCCESS); + return new BaseResponse(SUCCESS); } @PostMapping("/refresh") - public SuccessResponse refresh(@RequestBody final TokenRequestDto tokenRequestDto) { - return SuccessResponse.success(REFRESH_SUCCESS, authService.refresh(tokenRequestDto)); + public BaseResponse refresh(@RequestBody final TokenRequestDto tokenRequestDto) { + TokenPair response = authService.refresh(tokenRequestDto); + return new BaseResponse<>(response); } } diff --git a/src/main/java/server/poptato/auth/application/service/AuthService.java b/src/main/java/server/poptato/auth/application/service/AuthService.java index 069030c..8434fd1 100644 --- a/src/main/java/server/poptato/auth/application/service/AuthService.java +++ b/src/main/java/server/poptato/auth/application/service/AuthService.java @@ -2,11 +2,21 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import server.poptato.auth.application.dto.request.TokenRequestDto; import server.poptato.auth.application.dto.response.LoginResponseDto; import server.poptato.config.jwt.JwtService; +import server.poptato.external.kakao.service.KakaoSocialService; +import server.poptato.global.dto.TokenPair; +import server.poptato.global.dto.UserCreateResponse; +import server.poptato.global.exception.BaseException; +import server.poptato.user.domain.entity.User; +import server.poptato.user.domain.repository.UserRepository; import java.util.Optional; +import static server.poptato.global.exception.errorcode.BaseExceptionErrorCode.TOKEN_TIME_EXPIRED_EXCEPTION; +import static server.poptato.global.exception.errorcode.BaseExceptionErrorCode.USER_NOT_FOUND_EXCEPTION; + @Service @RequiredArgsConstructor public class AuthService { @@ -24,7 +34,7 @@ public LoginResponseDto login(final String baseUrl, final String kakaoCode) { userRepository.save(newUser); TokenPair tokenPair = jwtService.generateTokenPair(String.valueOf(newUser.getId())); - throw new NotFoundUserException(USER_NOT_FOUND_EXCEPTION, tokenPair); + return new LoginResponseDto(tokenPair.accessToken(), tokenPair.refreshToken()); } TokenPair tokenPair = jwtService.generateTokenPair(String.valueOf(user.get().getId())); return new LoginResponseDto(tokenPair.accessToken(), tokenPair.refreshToken()); @@ -41,19 +51,19 @@ public UserCreateResponse createUserToken(String useId) { public void logout(final Long userId) { - final User user = userRepository.findById(userId).orElseThrow(() -> new NotFoundException(USER_NOT_FOUND_EXCEPTION)); + final User user = userRepository.findById(userId).orElseThrow(() -> new BaseException(USER_NOT_FOUND_EXCEPTION)); jwtService.deleteRefreshToken(String.valueOf(userId)); } public TokenPair refresh(final TokenRequestDto tokenRequestDto) { if (!jwtService.verifyToken(tokenRequestDto.refreshToken())) - throw new UnAuthorizedException(TOKEN_TIME_EXPIRED_EXCEPTION); + throw new BaseException(TOKEN_TIME_EXPIRED_EXCEPTION); final String userId = jwtService.getUserIdInToken(tokenRequestDto.refreshToken()); - final User user = userRepository.findById(Long.parseLong(userId)).orElseThrow(() -> new NotFoundException(USER_NOT_FOUND_EXCEPTION)); + final User user = userRepository.findById(Long.parseLong(userId)).orElseThrow(() -> new BaseException(USER_NOT_FOUND_EXCEPTION)); if (!jwtService.compareRefreshToken(userId, tokenRequestDto.refreshToken())) - throw new UnAuthorizedException(TOKEN_TIME_EXPIRED_EXCEPTION); + throw new BaseException(TOKEN_TIME_EXPIRED_EXCEPTION); final TokenPair tokenPair = jwtService.generateTokenPair(userId); jwtService.saveRefreshToken(userId, tokenPair.refreshToken()); diff --git a/src/main/java/server/poptato/global/exception/errorcode/BaseExceptionErrorCode.java b/src/main/java/server/poptato/global/exception/errorcode/BaseExceptionErrorCode.java index 8d7c932..13e37fb 100644 --- a/src/main/java/server/poptato/global/exception/errorcode/BaseExceptionErrorCode.java +++ b/src/main/java/server/poptato/global/exception/errorcode/BaseExceptionErrorCode.java @@ -25,6 +25,7 @@ public enum BaseExceptionErrorCode implements ResponseStatus { TOKEN_TIME_EXPIRED_EXCEPTION(2007,HttpStatus.BAD_REQUEST.value(),"토큰 시간이 만료되었습니다."), INVALID_TOKEN_EXCEPTION(2008,HttpStatus.BAD_REQUEST.value(),"토큰이 유효하지 않습니다."), EMPTY_KAKAO_CODE_EXCEPTION(2009,HttpStatus.BAD_REQUEST.value(),"카카오 코드를 입력해야 합니다"), + USER_NOT_FOUND_EXCEPTION(2009,HttpStatus.NOT_FOUND.value(),"유저가 존재하지 않습니다"), /** * 3000: Server, Database 오류 (INTERNAL_SERVER_ERROR) diff --git a/src/main/java/server/poptato/user/domain/entity/User.java b/src/main/java/server/poptato/user/domain/entity/User.java new file mode 100644 index 0000000..7300ee5 --- /dev/null +++ b/src/main/java/server/poptato/user/domain/entity/User.java @@ -0,0 +1,27 @@ +package server.poptato.user.domain.entity; + +import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Entity +@Getter +@SuperBuilder +@Inheritance(strategy = InheritanceType.JOINED) +@NoArgsConstructor +@AllArgsConstructor +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + private String kakaoId; + + private String name; + +} diff --git a/src/main/java/server/poptato/user/domain/repository/UserRepository.java b/src/main/java/server/poptato/user/domain/repository/UserRepository.java new file mode 100644 index 0000000..edad900 --- /dev/null +++ b/src/main/java/server/poptato/user/domain/repository/UserRepository.java @@ -0,0 +1,10 @@ +package server.poptato.user.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import server.poptato.user.domain.entity.User; + +import java.util.Optional; + +public interface UserRepository extends JpaRepository { + public Optional findByKakaoId(String kakaoId); +} From 31807702c95dc18a5ab71ab6fbcb396c5a560cb1 Mon Sep 17 00:00:00 2001 From: pkl0912 Date: Mon, 7 Oct 2024 19:24:36 +0900 Subject: [PATCH 07/14] =?UTF-8?q?fix:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85,=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=8B=9C=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20=EA=B5=AC=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/server/poptato/auth/api/AuthController.java | 9 +++++++-- .../application/dto/response/LoginResponseDto.java | 2 +- .../poptato/auth/application/service/AuthService.java | 4 ++-- .../exception/errorcode/BaseExceptionErrorCode.java | 3 +++ .../server/poptato/global/response/BaseResponse.java | 10 +++++----- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/main/java/server/poptato/auth/api/AuthController.java b/src/main/java/server/poptato/auth/api/AuthController.java index 3d2f4db..6e011bc 100644 --- a/src/main/java/server/poptato/auth/api/AuthController.java +++ b/src/main/java/server/poptato/auth/api/AuthController.java @@ -12,8 +12,9 @@ import server.poptato.config.resolver.kakao.KakaoCode; import server.poptato.global.dto.TokenPair; import server.poptato.global.response.BaseResponse; +import server.poptato.global.response.status.ResponseStatus; -import static server.poptato.global.exception.errorcode.BaseExceptionErrorCode.SUCCESS; +import static server.poptato.global.exception.errorcode.BaseExceptionErrorCode.*; @RestController @RequestMapping("/auth") @@ -29,7 +30,11 @@ public BaseResponse login( HttpServletRequest request) { String originHeader = request.getHeader(ORIGIN); LoginResponseDto response = authService.login(originHeader, kakaoCode); - return new BaseResponse<>(response); + if (response.isNewUser()) { + return new BaseResponse<>(SUCCESS_REGISTER, response); // 회원가입 성공 응답 + } else { + return new BaseResponse<>(SUCCESS_LOGIN, response); // 로그인 성공 응답 + } } @PostMapping("/logout") diff --git a/src/main/java/server/poptato/auth/application/dto/response/LoginResponseDto.java b/src/main/java/server/poptato/auth/application/dto/response/LoginResponseDto.java index 0bab349..20e1035 100644 --- a/src/main/java/server/poptato/auth/application/dto/response/LoginResponseDto.java +++ b/src/main/java/server/poptato/auth/application/dto/response/LoginResponseDto.java @@ -1,4 +1,4 @@ package server.poptato.auth.application.dto.response; -public record LoginResponseDto(String accessToken, String refreshToken) { +public record LoginResponseDto(String accessToken, String refreshToken, boolean isNewUser) { } diff --git a/src/main/java/server/poptato/auth/application/service/AuthService.java b/src/main/java/server/poptato/auth/application/service/AuthService.java index 8434fd1..0b4063a 100644 --- a/src/main/java/server/poptato/auth/application/service/AuthService.java +++ b/src/main/java/server/poptato/auth/application/service/AuthService.java @@ -34,10 +34,10 @@ public LoginResponseDto login(final String baseUrl, final String kakaoCode) { userRepository.save(newUser); TokenPair tokenPair = jwtService.generateTokenPair(String.valueOf(newUser.getId())); - return new LoginResponseDto(tokenPair.accessToken(), tokenPair.refreshToken()); + return new LoginResponseDto(tokenPair.accessToken(), tokenPair.refreshToken(), true); } TokenPair tokenPair = jwtService.generateTokenPair(String.valueOf(user.get().getId())); - return new LoginResponseDto(tokenPair.accessToken(), tokenPair.refreshToken()); + return new LoginResponseDto(tokenPair.accessToken(), tokenPair.refreshToken(), false); } public UserCreateResponse createUserToken(String useId) { diff --git a/src/main/java/server/poptato/global/exception/errorcode/BaseExceptionErrorCode.java b/src/main/java/server/poptato/global/exception/errorcode/BaseExceptionErrorCode.java index 13e37fb..5637840 100644 --- a/src/main/java/server/poptato/global/exception/errorcode/BaseExceptionErrorCode.java +++ b/src/main/java/server/poptato/global/exception/errorcode/BaseExceptionErrorCode.java @@ -11,6 +11,9 @@ public enum BaseExceptionErrorCode implements ResponseStatus { * 1000: 요청 성공 (OK) */ SUCCESS(1000, HttpStatus.OK.value(), "요청에 성공하였습니다."), + SUCCESS_LOGIN(1001, HttpStatus.OK.value(), "로그인에 성공하였습니다."), + SUCCESS_REGISTER(1002, HttpStatus.OK.value(), "회원가입에 성공하였습니다."), + /** * 2000: Request 오류 (BAD_REQUEST) diff --git a/src/main/java/server/poptato/global/response/BaseResponse.java b/src/main/java/server/poptato/global/response/BaseResponse.java index b6679bf..8759924 100644 --- a/src/main/java/server/poptato/global/response/BaseResponse.java +++ b/src/main/java/server/poptato/global/response/BaseResponse.java @@ -18,14 +18,14 @@ public class BaseResponse implements ResponseStatus { private final T result; - public BaseResponse(T result) { - this.code = SUCCESS.getCode(); - this.status = SUCCESS.getStatus(); - this.message = SUCCESS.getMessage(); + public BaseResponse(ResponseStatus status, T result) { + this.code = status.getCode(); + this.status = status.getStatus(); + this.message = status.getMessage(); this.result = result; } - public BaseResponse() { + public BaseResponse(T result) { this.code = SUCCESS.getCode(); this.status = SUCCESS.getStatus(); this.message = SUCCESS.getMessage(); From feed5eab037f7730adb3a44137fafe8c7aafe361 Mon Sep 17 00:00:00 2001 From: pkl0912 Date: Mon, 7 Oct 2024 20:24:44 +0900 Subject: [PATCH 08/14] =?UTF-8?q?feat:=20=EC=B9=B4=EC=B9=B4=EC=98=A4?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=9D=B4=EB=A6=84=20=EA=B0=80=EC=A0=B8?= =?UTF-8?q?=EC=98=A4=EA=B8=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../poptato/auth/application/service/AuthService.java | 6 +++++- .../external/kakao/dto/response/KakaoUserInfo.java | 4 ++++ .../external/kakao/dto/response/KakaoUserResponse.java | 7 +++++-- .../external/kakao/service/KakaoSocialService.java | 8 ++++++-- .../poptato/external/kakao/service/SocialService.java | 4 +++- 5 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 src/main/java/server/poptato/external/kakao/dto/response/KakaoUserInfo.java diff --git a/src/main/java/server/poptato/auth/application/service/AuthService.java b/src/main/java/server/poptato/auth/application/service/AuthService.java index 0b4063a..d793949 100644 --- a/src/main/java/server/poptato/auth/application/service/AuthService.java +++ b/src/main/java/server/poptato/auth/application/service/AuthService.java @@ -5,6 +5,7 @@ import server.poptato.auth.application.dto.request.TokenRequestDto; import server.poptato.auth.application.dto.response.LoginResponseDto; import server.poptato.config.jwt.JwtService; +import server.poptato.external.kakao.dto.response.KakaoUserInfo; import server.poptato.external.kakao.service.KakaoSocialService; import server.poptato.global.dto.TokenPair; import server.poptato.global.dto.UserCreateResponse; @@ -25,11 +26,14 @@ public class AuthService { private final UserRepository userRepository; public LoginResponseDto login(final String baseUrl, final String kakaoCode) { - String kakaoId = kakaoSocialService.getIdFromKakao(baseUrl, kakaoCode); + KakaoUserInfo info = kakaoSocialService.getIdAndNickNameFromKakao(baseUrl, kakaoCode); + String kakaoId = info.kakaoId(); + String name = info.nickname(); Optional user = userRepository.findByKakaoId(kakaoId); if (user.isEmpty()) { User newUser = User.builder() .kakaoId(kakaoId) + .name(name) .build(); userRepository.save(newUser); diff --git a/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserInfo.java b/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserInfo.java new file mode 100644 index 0000000..e716748 --- /dev/null +++ b/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserInfo.java @@ -0,0 +1,4 @@ +package server.poptato.external.kakao.dto.response; + +public record KakaoUserInfo(String kakaoId, String nickname) { +} diff --git a/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserResponse.java b/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserResponse.java index 8092a6a..cc7966f 100644 --- a/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserResponse.java +++ b/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserResponse.java @@ -1,4 +1,7 @@ package server.poptato.external.kakao.dto.response; -public record KakaoUserResponse(Long id) { -} +public record KakaoUserResponse(Long id, KakaoUserProperties properties) { + + public record KakaoUserProperties(String nickname) { + } +} \ No newline at end of file diff --git a/src/main/java/server/poptato/external/kakao/service/KakaoSocialService.java b/src/main/java/server/poptato/external/kakao/service/KakaoSocialService.java index 3e7e410..8be53b1 100644 --- a/src/main/java/server/poptato/external/kakao/service/KakaoSocialService.java +++ b/src/main/java/server/poptato/external/kakao/service/KakaoSocialService.java @@ -5,6 +5,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import server.poptato.external.kakao.dto.response.KakaoAccessTokenResponse; +import server.poptato.external.kakao.dto.response.KakaoUserInfo; import server.poptato.external.kakao.dto.response.KakaoUserResponse; import server.poptato.external.kakao.feign.KakaoApiClient; import server.poptato.external.kakao.feign.KakaoAuthApiClient; @@ -22,7 +23,7 @@ public class KakaoSocialService extends SocialService { private final KakaoApiClient kakaoApiClient; @Override - public String getIdFromKakao(String baseUrl, String kakaoCode) { + public KakaoUserInfo getIdAndNickNameFromKakao(String baseUrl, String kakaoCode) { String redirectUrl = baseUrl + KAKAO_ROUTER; KakaoAccessTokenResponse tokenResponse = kakaoAuthApiClient.getOAuth2AccessToken( GRANT_TYPE, @@ -31,7 +32,10 @@ public String getIdFromKakao(String baseUrl, String kakaoCode) { kakaoCode ); + // 액세스 토큰으로 사용자 정보 요청 KakaoUserResponse userResponse = kakaoApiClient.getUserInformation(Bearer + tokenResponse.accessToken()); - return String.valueOf(userResponse.id()); + + // ID와 닉네임을 함께 반환 + return new KakaoUserInfo(String.valueOf(userResponse.id()), userResponse.properties().nickname()); } } diff --git a/src/main/java/server/poptato/external/kakao/service/SocialService.java b/src/main/java/server/poptato/external/kakao/service/SocialService.java index 719f3c5..d0ac2bd 100644 --- a/src/main/java/server/poptato/external/kakao/service/SocialService.java +++ b/src/main/java/server/poptato/external/kakao/service/SocialService.java @@ -1,7 +1,9 @@ package server.poptato.external.kakao.service; +import server.poptato.external.kakao.dto.response.KakaoUserInfo; + public abstract class SocialService { - public abstract String getIdFromKakao(String baseUrl, String kakaoCode); + public abstract KakaoUserInfo getIdAndNickNameFromKakao(String baseUrl, String kakaoCode); } \ No newline at end of file From 193b15d59e6dd063bd177b6f5421809b8abc57ff Mon Sep 17 00:00:00 2001 From: pkl0912 Date: Mon, 7 Oct 2024 20:42:09 +0900 Subject: [PATCH 09/14] =?UTF-8?q?fix:=20userId=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/application/dto/response/LoginResponseDto.java | 2 +- .../server/poptato/auth/application/service/AuthService.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/server/poptato/auth/application/dto/response/LoginResponseDto.java b/src/main/java/server/poptato/auth/application/dto/response/LoginResponseDto.java index 20e1035..322b370 100644 --- a/src/main/java/server/poptato/auth/application/dto/response/LoginResponseDto.java +++ b/src/main/java/server/poptato/auth/application/dto/response/LoginResponseDto.java @@ -1,4 +1,4 @@ package server.poptato.auth.application.dto.response; -public record LoginResponseDto(String accessToken, String refreshToken, boolean isNewUser) { +public record LoginResponseDto(String accessToken, String refreshToken, boolean isNewUser, Long userId) { } diff --git a/src/main/java/server/poptato/auth/application/service/AuthService.java b/src/main/java/server/poptato/auth/application/service/AuthService.java index d793949..179e1b9 100644 --- a/src/main/java/server/poptato/auth/application/service/AuthService.java +++ b/src/main/java/server/poptato/auth/application/service/AuthService.java @@ -38,10 +38,10 @@ public LoginResponseDto login(final String baseUrl, final String kakaoCode) { userRepository.save(newUser); TokenPair tokenPair = jwtService.generateTokenPair(String.valueOf(newUser.getId())); - return new LoginResponseDto(tokenPair.accessToken(), tokenPair.refreshToken(), true); + return new LoginResponseDto(tokenPair.accessToken(), tokenPair.refreshToken(), true, newUser.getId()); } TokenPair tokenPair = jwtService.generateTokenPair(String.valueOf(user.get().getId())); - return new LoginResponseDto(tokenPair.accessToken(), tokenPair.refreshToken(), false); + return new LoginResponseDto(tokenPair.accessToken(), tokenPair.refreshToken(), false, user.get().getId()); } public UserCreateResponse createUserToken(String useId) { From 5f70786326aa0b3bbf6d3aa1690212be710b4efb Mon Sep 17 00:00:00 2001 From: pkl0912 Date: Tue, 8 Oct 2024 13:15:20 +0900 Subject: [PATCH 10/14] =?UTF-8?q?feat:=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{application/dto => api}/request/TokenRequestDto.java | 0 .../application/{dto => }/response/LoginResponseDto.java | 0 .../java/server/poptato/global/dto/UserCreateResponse.java | 7 ------- 3 files changed, 7 deletions(-) rename src/main/java/server/poptato/auth/{application/dto => api}/request/TokenRequestDto.java (100%) rename src/main/java/server/poptato/auth/application/{dto => }/response/LoginResponseDto.java (100%) delete mode 100644 src/main/java/server/poptato/global/dto/UserCreateResponse.java diff --git a/src/main/java/server/poptato/auth/application/dto/request/TokenRequestDto.java b/src/main/java/server/poptato/auth/api/request/TokenRequestDto.java similarity index 100% rename from src/main/java/server/poptato/auth/application/dto/request/TokenRequestDto.java rename to src/main/java/server/poptato/auth/api/request/TokenRequestDto.java diff --git a/src/main/java/server/poptato/auth/application/dto/response/LoginResponseDto.java b/src/main/java/server/poptato/auth/application/response/LoginResponseDto.java similarity index 100% rename from src/main/java/server/poptato/auth/application/dto/response/LoginResponseDto.java rename to src/main/java/server/poptato/auth/application/response/LoginResponseDto.java diff --git a/src/main/java/server/poptato/global/dto/UserCreateResponse.java b/src/main/java/server/poptato/global/dto/UserCreateResponse.java deleted file mode 100644 index eda7edd..0000000 --- a/src/main/java/server/poptato/global/dto/UserCreateResponse.java +++ /dev/null @@ -1,7 +0,0 @@ -package server.poptato.global.dto; - -public record UserCreateResponse( - String accessToken, - String refreshToken -) { -} From e38ea07312305a9d5fa534e9cc524cbf8ed4f326 Mon Sep 17 00:00:00 2001 From: pkl0912 Date: Tue, 8 Oct 2024 13:22:26 +0900 Subject: [PATCH 11/14] =?UTF-8?q?refactor:=20repository=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/poptato/PoptatoApplication.java | 2 ++ .../poptato/auth/api/AuthController.java | 5 ++-- .../auth/api/request/TokenRequestDto.java | 2 +- .../response/LoginResponseDto.java | 2 +- .../auth/application/service/AuthService.java | 22 +++++---------- .../kakao/dto/response/KakaoUserInfo.java | 2 +- .../kakao/dto/response/KakaoUserResponse.java | 7 +++-- .../kakao/service/KakaoSocialService.java | 4 +-- .../external/kakao/service/SocialService.java | 2 +- .../poptato/user/domain/entity/User.java | 27 ++++++++++++++----- .../domain/repository/UserRepository.java | 7 +++-- .../infra/repository/JpaUserRepository.java | 12 +++++++++ 12 files changed, 57 insertions(+), 37 deletions(-) create mode 100644 src/main/java/server/poptato/user/infra/repository/JpaUserRepository.java diff --git a/src/main/java/server/poptato/PoptatoApplication.java b/src/main/java/server/poptato/PoptatoApplication.java index 3baf390..5b69848 100644 --- a/src/main/java/server/poptato/PoptatoApplication.java +++ b/src/main/java/server/poptato/PoptatoApplication.java @@ -3,9 +3,11 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication @EnableFeignClients +@EnableJpaAuditing public class PoptatoApplication { public static void main(String[] args) { diff --git a/src/main/java/server/poptato/auth/api/AuthController.java b/src/main/java/server/poptato/auth/api/AuthController.java index 6e011bc..54f1b7a 100644 --- a/src/main/java/server/poptato/auth/api/AuthController.java +++ b/src/main/java/server/poptato/auth/api/AuthController.java @@ -6,13 +6,12 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import server.poptato.auth.application.dto.request.TokenRequestDto; -import server.poptato.auth.application.dto.response.LoginResponseDto; +import server.poptato.auth.api.request.TokenRequestDto; +import server.poptato.auth.application.response.LoginResponseDto; import server.poptato.auth.application.service.AuthService; import server.poptato.config.resolver.kakao.KakaoCode; import server.poptato.global.dto.TokenPair; import server.poptato.global.response.BaseResponse; -import server.poptato.global.response.status.ResponseStatus; import static server.poptato.global.exception.errorcode.BaseExceptionErrorCode.*; diff --git a/src/main/java/server/poptato/auth/api/request/TokenRequestDto.java b/src/main/java/server/poptato/auth/api/request/TokenRequestDto.java index 29ea5a1..35d801c 100644 --- a/src/main/java/server/poptato/auth/api/request/TokenRequestDto.java +++ b/src/main/java/server/poptato/auth/api/request/TokenRequestDto.java @@ -1,4 +1,4 @@ -package server.poptato.auth.application.dto.request; +package server.poptato.auth.api.request; public record TokenRequestDto(String accessToken, String refreshToken) { } diff --git a/src/main/java/server/poptato/auth/application/response/LoginResponseDto.java b/src/main/java/server/poptato/auth/application/response/LoginResponseDto.java index 322b370..efd19ad 100644 --- a/src/main/java/server/poptato/auth/application/response/LoginResponseDto.java +++ b/src/main/java/server/poptato/auth/application/response/LoginResponseDto.java @@ -1,4 +1,4 @@ -package server.poptato.auth.application.dto.response; +package server.poptato.auth.application.response; public record LoginResponseDto(String accessToken, String refreshToken, boolean isNewUser, Long userId) { } diff --git a/src/main/java/server/poptato/auth/application/service/AuthService.java b/src/main/java/server/poptato/auth/application/service/AuthService.java index 179e1b9..b6e5f19 100644 --- a/src/main/java/server/poptato/auth/application/service/AuthService.java +++ b/src/main/java/server/poptato/auth/application/service/AuthService.java @@ -2,16 +2,16 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import server.poptato.auth.application.dto.request.TokenRequestDto; -import server.poptato.auth.application.dto.response.LoginResponseDto; +import server.poptato.auth.api.request.TokenRequestDto; +import server.poptato.auth.application.response.LoginResponseDto; import server.poptato.config.jwt.JwtService; import server.poptato.external.kakao.dto.response.KakaoUserInfo; import server.poptato.external.kakao.service.KakaoSocialService; import server.poptato.global.dto.TokenPair; -import server.poptato.global.dto.UserCreateResponse; import server.poptato.global.exception.BaseException; import server.poptato.user.domain.entity.User; import server.poptato.user.domain.repository.UserRepository; +import server.poptato.user.infra.repository.JpaUserRepository; import java.util.Optional; @@ -23,17 +23,19 @@ public class AuthService { private final JwtService jwtService; private final KakaoSocialService kakaoSocialService; - private final UserRepository userRepository; + private final JpaUserRepository userRepository; public LoginResponseDto login(final String baseUrl, final String kakaoCode) { - KakaoUserInfo info = kakaoSocialService.getIdAndNickNameFromKakao(baseUrl, kakaoCode); + KakaoUserInfo info = kakaoSocialService.getIdAndNickNameAndEmailFromKakao(baseUrl, kakaoCode); String kakaoId = info.kakaoId(); String name = info.nickname(); + String email = info.email(); Optional user = userRepository.findByKakaoId(kakaoId); if (user.isEmpty()) { User newUser = User.builder() .kakaoId(kakaoId) .name(name) + .email(email) .build(); userRepository.save(newUser); @@ -44,16 +46,6 @@ public LoginResponseDto login(final String baseUrl, final String kakaoCode) { return new LoginResponseDto(tokenPair.accessToken(), tokenPair.refreshToken(), false, user.get().getId()); } - public UserCreateResponse createUserToken(String useId) { - - TokenPair tokenPair = jwtService.generateTokenPair(useId); - UserCreateResponse userCreateResponse = new UserCreateResponse(tokenPair.accessToken(), tokenPair.refreshToken()); - - return userCreateResponse; - } - - - public void logout(final Long userId) { final User user = userRepository.findById(userId).orElseThrow(() -> new BaseException(USER_NOT_FOUND_EXCEPTION)); jwtService.deleteRefreshToken(String.valueOf(userId)); diff --git a/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserInfo.java b/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserInfo.java index e716748..2d4b00d 100644 --- a/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserInfo.java +++ b/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserInfo.java @@ -1,4 +1,4 @@ package server.poptato.external.kakao.dto.response; -public record KakaoUserInfo(String kakaoId, String nickname) { +public record KakaoUserInfo(String kakaoId, String nickname, String email) { } diff --git a/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserResponse.java b/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserResponse.java index cc7966f..a6294c5 100644 --- a/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserResponse.java +++ b/src/main/java/server/poptato/external/kakao/dto/response/KakaoUserResponse.java @@ -1,7 +1,10 @@ package server.poptato.external.kakao.dto.response; -public record KakaoUserResponse(Long id, KakaoUserProperties properties) { +public record KakaoUserResponse(Long id, KakaoUserProperties properties, KakaoAccount kakao_account) { public record KakaoUserProperties(String nickname) { } -} \ No newline at end of file + + public record KakaoAccount(String email) { // 이메일을 담을 필드 추가 + } +} diff --git a/src/main/java/server/poptato/external/kakao/service/KakaoSocialService.java b/src/main/java/server/poptato/external/kakao/service/KakaoSocialService.java index 8be53b1..c9fe664 100644 --- a/src/main/java/server/poptato/external/kakao/service/KakaoSocialService.java +++ b/src/main/java/server/poptato/external/kakao/service/KakaoSocialService.java @@ -23,7 +23,7 @@ public class KakaoSocialService extends SocialService { private final KakaoApiClient kakaoApiClient; @Override - public KakaoUserInfo getIdAndNickNameFromKakao(String baseUrl, String kakaoCode) { + public KakaoUserInfo getIdAndNickNameAndEmailFromKakao(String baseUrl, String kakaoCode) { String redirectUrl = baseUrl + KAKAO_ROUTER; KakaoAccessTokenResponse tokenResponse = kakaoAuthApiClient.getOAuth2AccessToken( GRANT_TYPE, @@ -36,6 +36,6 @@ public KakaoUserInfo getIdAndNickNameFromKakao(String baseUrl, String kakaoCode) KakaoUserResponse userResponse = kakaoApiClient.getUserInformation(Bearer + tokenResponse.accessToken()); // ID와 닉네임을 함께 반환 - return new KakaoUserInfo(String.valueOf(userResponse.id()), userResponse.properties().nickname()); + return new KakaoUserInfo(String.valueOf(userResponse.id()), userResponse.properties().nickname(), userResponse.kakao_account().email()); } } diff --git a/src/main/java/server/poptato/external/kakao/service/SocialService.java b/src/main/java/server/poptato/external/kakao/service/SocialService.java index d0ac2bd..346ae80 100644 --- a/src/main/java/server/poptato/external/kakao/service/SocialService.java +++ b/src/main/java/server/poptato/external/kakao/service/SocialService.java @@ -4,6 +4,6 @@ import server.poptato.external.kakao.dto.response.KakaoUserInfo; public abstract class SocialService { - public abstract KakaoUserInfo getIdAndNickNameFromKakao(String baseUrl, String kakaoCode); + public abstract KakaoUserInfo getIdAndNickNameAndEmailFromKakao(String baseUrl, String kakaoCode); } \ No newline at end of file diff --git a/src/main/java/server/poptato/user/domain/entity/User.java b/src/main/java/server/poptato/user/domain/entity/User.java index 7300ee5..6445cc1 100644 --- a/src/main/java/server/poptato/user/domain/entity/User.java +++ b/src/main/java/server/poptato/user/domain/entity/User.java @@ -2,17 +2,19 @@ import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; +import lombok.*; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDateTime; @Entity @Getter -@SuperBuilder -@Inheritance(strategy = InheritanceType.JOINED) -@NoArgsConstructor +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor +@EntityListeners(AuditingEntityListener.class) // Auditing 기능 추 public class User { @Id @@ -22,6 +24,17 @@ public class User { @NotNull private String kakaoId; + @NotNull private String name; + @NotNull + private String email; + + @CreatedDate // 엔티티가 처음 생성될 때 시간 자동 저장 + @Column(updatable = false) // 생성일은 수정 불가 + private LocalDateTime createDate; + + @LastModifiedDate // 엔티티가 수정될 때 시간 자동 저장 + private LocalDateTime modifyDate; + } diff --git a/src/main/java/server/poptato/user/domain/repository/UserRepository.java b/src/main/java/server/poptato/user/domain/repository/UserRepository.java index edad900..e2fe7fd 100644 --- a/src/main/java/server/poptato/user/domain/repository/UserRepository.java +++ b/src/main/java/server/poptato/user/domain/repository/UserRepository.java @@ -1,10 +1,9 @@ package server.poptato.user.domain.repository; -import org.springframework.data.jpa.repository.JpaRepository; import server.poptato.user.domain.entity.User; import java.util.Optional; -public interface UserRepository extends JpaRepository { - public Optional findByKakaoId(String kakaoId); -} +public interface UserRepository { + Optional findByKakaoId(String kakaoId); +} \ No newline at end of file diff --git a/src/main/java/server/poptato/user/infra/repository/JpaUserRepository.java b/src/main/java/server/poptato/user/infra/repository/JpaUserRepository.java new file mode 100644 index 0000000..d764c8d --- /dev/null +++ b/src/main/java/server/poptato/user/infra/repository/JpaUserRepository.java @@ -0,0 +1,12 @@ +package server.poptato.user.infra.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import server.poptato.user.domain.entity.User; +import server.poptato.user.domain.repository.UserRepository; + +import java.util.Optional; + +public interface JpaUserRepository extends UserRepository, JpaRepository { + @Override + Optional findByKakaoId(String kakaoId); +} \ No newline at end of file From 5f9a2f95645905ce52fb74bbfb0940f0b1d50a88 Mon Sep 17 00:00:00 2001 From: pkl0912 Date: Tue, 8 Oct 2024 13:25:37 +0900 Subject: [PATCH 12/14] =?UTF-8?q?refactor:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EB=A1=9C=EC=A7=81=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/application/service/AuthService.java | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main/java/server/poptato/auth/application/service/AuthService.java b/src/main/java/server/poptato/auth/application/service/AuthService.java index b6e5f19..ad18013 100644 --- a/src/main/java/server/poptato/auth/application/service/AuthService.java +++ b/src/main/java/server/poptato/auth/application/service/AuthService.java @@ -32,15 +32,7 @@ public LoginResponseDto login(final String baseUrl, final String kakaoCode) { String email = info.email(); Optional user = userRepository.findByKakaoId(kakaoId); if (user.isEmpty()) { - User newUser = User.builder() - .kakaoId(kakaoId) - .name(name) - .email(email) - .build(); - userRepository.save(newUser); - - TokenPair tokenPair = jwtService.generateTokenPair(String.valueOf(newUser.getId())); - return new LoginResponseDto(tokenPair.accessToken(), tokenPair.refreshToken(), true, newUser.getId()); + return createNewUserResponse(kakaoId, name, email); } TokenPair tokenPair = jwtService.generateTokenPair(String.valueOf(user.get().getId())); return new LoginResponseDto(tokenPair.accessToken(), tokenPair.refreshToken(), false, user.get().getId()); @@ -65,4 +57,17 @@ public TokenPair refresh(final TokenRequestDto tokenRequestDto) { jwtService.saveRefreshToken(userId, tokenPair.refreshToken()); return tokenPair; } + + private LoginResponseDto createNewUserResponse(String kakaoId, String name, String email) { + User newUser = User.builder() + .kakaoId(kakaoId) + .name(name) + .email(email) + .build(); + userRepository.save(newUser); + + // 토큰 발급 및 응답 객체 생성 + TokenPair tokenPair = jwtService.generateTokenPair(String.valueOf(newUser.getId())); + return new LoginResponseDto(tokenPair.accessToken(), tokenPair.refreshToken(), true, newUser.getId()); + } } From c88cbcc4e9c472fd5a1730d30c3b56363c17ea6d Mon Sep 17 00:00:00 2001 From: pkl0912 Date: Tue, 8 Oct 2024 13:52:28 +0900 Subject: [PATCH 13/14] =?UTF-8?q?refactor:=20userId=20resolver=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/server/poptato/auth/api/AuthController.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/server/poptato/auth/api/AuthController.java b/src/main/java/server/poptato/auth/api/AuthController.java index 54f1b7a..ed83c5e 100644 --- a/src/main/java/server/poptato/auth/api/AuthController.java +++ b/src/main/java/server/poptato/auth/api/AuthController.java @@ -9,7 +9,9 @@ import server.poptato.auth.api.request.TokenRequestDto; import server.poptato.auth.application.response.LoginResponseDto; import server.poptato.auth.application.service.AuthService; +import server.poptato.config.jwt.JwtService; import server.poptato.config.resolver.kakao.KakaoCode; +import server.poptato.config.resolver.user.UserId; import server.poptato.global.dto.TokenPair; import server.poptato.global.response.BaseResponse; @@ -22,6 +24,7 @@ public class AuthController { private static final String ORIGIN = "origin"; private final AuthService authService; + private final JwtService jwtService; @PostMapping("/login") public BaseResponse login( @@ -37,12 +40,10 @@ public BaseResponse login( } @PostMapping("/logout") - public BaseResponse logout(HttpServletRequest request) { - Long userId = (Long) request.getAttribute("userId"); // `userId`를 헤더에서 가져온다고 가정 + public BaseResponse logout(@UserId Long userId) { authService.logout(userId); return new BaseResponse(SUCCESS); } - @PostMapping("/refresh") public BaseResponse refresh(@RequestBody final TokenRequestDto tokenRequestDto) { TokenPair response = authService.refresh(tokenRequestDto); From 58fd3b5e8433d15191b0e6e4b8e18df64f2d9999 Mon Sep 17 00:00:00 2001 From: pkl0912 Date: Tue, 8 Oct 2024 23:30:30 +0900 Subject: [PATCH 14/14] =?UTF-8?q?refactor:=20login=20api=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../poptato/auth/api/AuthController.java | 20 ++++------- .../auth/application/service/AuthService.java | 2 -- .../application/service}/JwtService.java | 3 +- .../java/server/poptato/config/WebConfig.java | 7 ++-- .../kakao/resolver}/KakaoCode.java | 2 +- .../kakao/resolver}/KakaoCodeResolver.java | 4 +-- .../external/kakao/resolver/OriginHeader.java | 11 +++++++ .../kakao/resolver/OriginResolver.java | 33 +++++++++++++++++++ .../redis/RedisConfig.java | 2 +- .../errorcode/BaseExceptionErrorCode.java | 4 ++- .../user => user/resolver}/UserId.java | 2 +- .../user => user/resolver}/UserResolver.java | 5 +-- 12 files changed, 66 insertions(+), 29 deletions(-) rename src/main/java/server/poptato/{config/jwt => auth/application/service}/JwtService.java (98%) rename src/main/java/server/poptato/{config/resolver/kakao => external/kakao/resolver}/KakaoCode.java (84%) rename src/main/java/server/poptato/{config/resolver/kakao => external/kakao/resolver}/KakaoCodeResolver.java (88%) create mode 100644 src/main/java/server/poptato/external/kakao/resolver/OriginHeader.java create mode 100644 src/main/java/server/poptato/external/kakao/resolver/OriginResolver.java rename src/main/java/server/poptato/{config => external}/redis/RedisConfig.java (96%) rename src/main/java/server/poptato/{config/resolver/user => user/resolver}/UserId.java (85%) rename src/main/java/server/poptato/{config/resolver/user => user/resolver}/UserResolver.java (93%) diff --git a/src/main/java/server/poptato/auth/api/AuthController.java b/src/main/java/server/poptato/auth/api/AuthController.java index ed83c5e..c96f376 100644 --- a/src/main/java/server/poptato/auth/api/AuthController.java +++ b/src/main/java/server/poptato/auth/api/AuthController.java @@ -1,6 +1,5 @@ package server.poptato.auth.api; -import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -9,11 +8,11 @@ import server.poptato.auth.api.request.TokenRequestDto; import server.poptato.auth.application.response.LoginResponseDto; import server.poptato.auth.application.service.AuthService; -import server.poptato.config.jwt.JwtService; -import server.poptato.config.resolver.kakao.KakaoCode; -import server.poptato.config.resolver.user.UserId; +import server.poptato.external.kakao.resolver.KakaoCode; +import server.poptato.external.kakao.resolver.OriginHeader; import server.poptato.global.dto.TokenPair; import server.poptato.global.response.BaseResponse; +import server.poptato.user.resolver.UserId; import static server.poptato.global.exception.errorcode.BaseExceptionErrorCode.*; @@ -22,21 +21,14 @@ @RequiredArgsConstructor public class AuthController { - private static final String ORIGIN = "origin"; private final AuthService authService; - private final JwtService jwtService; @PostMapping("/login") public BaseResponse login( @KakaoCode String kakaoCode, - HttpServletRequest request) { - String originHeader = request.getHeader(ORIGIN); + @OriginHeader String originHeader) { LoginResponseDto response = authService.login(originHeader, kakaoCode); - if (response.isNewUser()) { - return new BaseResponse<>(SUCCESS_REGISTER, response); // 회원가입 성공 응답 - } else { - return new BaseResponse<>(SUCCESS_LOGIN, response); // 로그인 성공 응답 - } + return new BaseResponse<>(SUCCESS, response); } @PostMapping("/logout") @@ -47,6 +39,6 @@ public BaseResponse logout(@UserId Long userId) { @PostMapping("/refresh") public BaseResponse refresh(@RequestBody final TokenRequestDto tokenRequestDto) { TokenPair response = authService.refresh(tokenRequestDto); - return new BaseResponse<>(response); + return new BaseResponse<>(SUCCESS, response); } } diff --git a/src/main/java/server/poptato/auth/application/service/AuthService.java b/src/main/java/server/poptato/auth/application/service/AuthService.java index ad18013..3535455 100644 --- a/src/main/java/server/poptato/auth/application/service/AuthService.java +++ b/src/main/java/server/poptato/auth/application/service/AuthService.java @@ -4,13 +4,11 @@ import org.springframework.stereotype.Service; import server.poptato.auth.api.request.TokenRequestDto; import server.poptato.auth.application.response.LoginResponseDto; -import server.poptato.config.jwt.JwtService; import server.poptato.external.kakao.dto.response.KakaoUserInfo; import server.poptato.external.kakao.service.KakaoSocialService; import server.poptato.global.dto.TokenPair; import server.poptato.global.exception.BaseException; import server.poptato.user.domain.entity.User; -import server.poptato.user.domain.repository.UserRepository; import server.poptato.user.infra.repository.JpaUserRepository; import java.util.Optional; diff --git a/src/main/java/server/poptato/config/jwt/JwtService.java b/src/main/java/server/poptato/auth/application/service/JwtService.java similarity index 98% rename from src/main/java/server/poptato/config/jwt/JwtService.java rename to src/main/java/server/poptato/auth/application/service/JwtService.java index 0434a2f..c049ee8 100644 --- a/src/main/java/server/poptato/config/jwt/JwtService.java +++ b/src/main/java/server/poptato/auth/application/service/JwtService.java @@ -1,4 +1,4 @@ -package server.poptato.config.jwt; +package server.poptato.auth.application.service; @@ -14,7 +14,6 @@ import org.springframework.stereotype.Service; import server.poptato.global.dto.TokenPair; import server.poptato.global.exception.BaseException; -import server.poptato.global.response.BaseErrorResponse; import java.nio.charset.StandardCharsets; import java.security.Key; diff --git a/src/main/java/server/poptato/config/WebConfig.java b/src/main/java/server/poptato/config/WebConfig.java index 45543be..ffa78f1 100644 --- a/src/main/java/server/poptato/config/WebConfig.java +++ b/src/main/java/server/poptato/config/WebConfig.java @@ -6,8 +6,9 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import server.poptato.config.resolver.kakao.KakaoCodeResolver; -import server.poptato.config.resolver.user.UserResolver; +import server.poptato.external.kakao.resolver.KakaoCodeResolver; +import server.poptato.external.kakao.resolver.OriginResolver; +import server.poptato.user.resolver.UserResolver; import java.util.List; @@ -16,6 +17,7 @@ public class WebConfig implements WebMvcConfigurer { private final UserResolver userResolver; private final KakaoCodeResolver kakaoCodeResolver; + private final OriginResolver originResolver; @Override public void addCorsMappings(final CorsRegistry registry) { @@ -31,6 +33,7 @@ public void addCorsMappings(final CorsRegistry registry) { public void addArgumentResolvers(List resolvers) { resolvers.add(userResolver); resolvers.add(kakaoCodeResolver); + resolvers.add(originResolver); } } \ No newline at end of file diff --git a/src/main/java/server/poptato/config/resolver/kakao/KakaoCode.java b/src/main/java/server/poptato/external/kakao/resolver/KakaoCode.java similarity index 84% rename from src/main/java/server/poptato/config/resolver/kakao/KakaoCode.java rename to src/main/java/server/poptato/external/kakao/resolver/KakaoCode.java index e7d4622..209e0e4 100644 --- a/src/main/java/server/poptato/config/resolver/kakao/KakaoCode.java +++ b/src/main/java/server/poptato/external/kakao/resolver/KakaoCode.java @@ -1,4 +1,4 @@ -package server.poptato.config.resolver.kakao; +package server.poptato.external.kakao.resolver; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/src/main/java/server/poptato/config/resolver/kakao/KakaoCodeResolver.java b/src/main/java/server/poptato/external/kakao/resolver/KakaoCodeResolver.java similarity index 88% rename from src/main/java/server/poptato/config/resolver/kakao/KakaoCodeResolver.java rename to src/main/java/server/poptato/external/kakao/resolver/KakaoCodeResolver.java index 1c64f24..e651290 100644 --- a/src/main/java/server/poptato/config/resolver/kakao/KakaoCodeResolver.java +++ b/src/main/java/server/poptato/external/kakao/resolver/KakaoCodeResolver.java @@ -1,4 +1,4 @@ -package server.poptato.config.resolver.kakao; +package server.poptato.external.kakao.resolver; import jakarta.servlet.http.HttpServletRequest; @@ -10,10 +10,8 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; import server.poptato.global.exception.BaseException; -import server.poptato.global.response.BaseErrorResponse; import static server.poptato.global.exception.errorcode.BaseExceptionErrorCode.EMPTY_KAKAO_CODE_EXCEPTION; -import static server.poptato.global.exception.errorcode.BaseExceptionErrorCode.NO_REQUEST_PARAMETER; @Component diff --git a/src/main/java/server/poptato/external/kakao/resolver/OriginHeader.java b/src/main/java/server/poptato/external/kakao/resolver/OriginHeader.java new file mode 100644 index 0000000..ee64856 --- /dev/null +++ b/src/main/java/server/poptato/external/kakao/resolver/OriginHeader.java @@ -0,0 +1,11 @@ +package server.poptato.external.kakao.resolver; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface OriginHeader { +} \ No newline at end of file diff --git a/src/main/java/server/poptato/external/kakao/resolver/OriginResolver.java b/src/main/java/server/poptato/external/kakao/resolver/OriginResolver.java new file mode 100644 index 0000000..c24656e --- /dev/null +++ b/src/main/java/server/poptato/external/kakao/resolver/OriginResolver.java @@ -0,0 +1,33 @@ +package server.poptato.external.kakao.resolver; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; +import server.poptato.global.exception.BaseException; + +import static server.poptato.global.exception.errorcode.BaseExceptionErrorCode.ORIGIN_HEADER_MISSING_EXCEPTION; + +@Component +public class OriginResolver implements HandlerMethodArgumentResolver { + + private static final String ORIGIN = "origin"; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(OriginHeader.class) && String.class.equals(parameter.getParameterType()); + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); + String originHeader = request.getHeader(ORIGIN); + if (originHeader == null || originHeader.isBlank()) { + throw new BaseException(ORIGIN_HEADER_MISSING_EXCEPTION); + } + return originHeader; + } +} \ No newline at end of file diff --git a/src/main/java/server/poptato/config/redis/RedisConfig.java b/src/main/java/server/poptato/external/redis/RedisConfig.java similarity index 96% rename from src/main/java/server/poptato/config/redis/RedisConfig.java rename to src/main/java/server/poptato/external/redis/RedisConfig.java index a3b853e..5747454 100644 --- a/src/main/java/server/poptato/config/redis/RedisConfig.java +++ b/src/main/java/server/poptato/external/redis/RedisConfig.java @@ -1,4 +1,4 @@ -package server.poptato.config.redis; +package server.poptato.external.redis; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; diff --git a/src/main/java/server/poptato/global/exception/errorcode/BaseExceptionErrorCode.java b/src/main/java/server/poptato/global/exception/errorcode/BaseExceptionErrorCode.java index 5637840..ad93d06 100644 --- a/src/main/java/server/poptato/global/exception/errorcode/BaseExceptionErrorCode.java +++ b/src/main/java/server/poptato/global/exception/errorcode/BaseExceptionErrorCode.java @@ -28,7 +28,9 @@ public enum BaseExceptionErrorCode implements ResponseStatus { TOKEN_TIME_EXPIRED_EXCEPTION(2007,HttpStatus.BAD_REQUEST.value(),"토큰 시간이 만료되었습니다."), INVALID_TOKEN_EXCEPTION(2008,HttpStatus.BAD_REQUEST.value(),"토큰이 유효하지 않습니다."), EMPTY_KAKAO_CODE_EXCEPTION(2009,HttpStatus.BAD_REQUEST.value(),"카카오 코드를 입력해야 합니다"), - USER_NOT_FOUND_EXCEPTION(2009,HttpStatus.NOT_FOUND.value(),"유저가 존재하지 않습니다"), + ORIGIN_HEADER_MISSING_EXCEPTION(2010,HttpStatus.BAD_REQUEST.value(),"origin 헤더가 필요합니다"), + USER_NOT_FOUND_EXCEPTION(2011,HttpStatus.NOT_FOUND.value(),"유저가 존재하지 않습니다"), + /** * 3000: Server, Database 오류 (INTERNAL_SERVER_ERROR) diff --git a/src/main/java/server/poptato/config/resolver/user/UserId.java b/src/main/java/server/poptato/user/resolver/UserId.java similarity index 85% rename from src/main/java/server/poptato/config/resolver/user/UserId.java rename to src/main/java/server/poptato/user/resolver/UserId.java index bf46ab0..693e004 100644 --- a/src/main/java/server/poptato/config/resolver/user/UserId.java +++ b/src/main/java/server/poptato/user/resolver/UserId.java @@ -1,4 +1,4 @@ -package server.poptato.config.resolver.user; +package server.poptato.user.resolver; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/src/main/java/server/poptato/config/resolver/user/UserResolver.java b/src/main/java/server/poptato/user/resolver/UserResolver.java similarity index 93% rename from src/main/java/server/poptato/config/resolver/user/UserResolver.java rename to src/main/java/server/poptato/user/resolver/UserResolver.java index 9ad250b..f4907be 100644 --- a/src/main/java/server/poptato/config/resolver/user/UserResolver.java +++ b/src/main/java/server/poptato/user/resolver/UserResolver.java @@ -1,4 +1,4 @@ -package server.poptato.config.resolver.user; +package server.poptato.user.resolver; import jakarta.servlet.http.HttpServletRequest; @@ -9,9 +9,10 @@ import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; -import server.poptato.config.jwt.JwtService; +import server.poptato.auth.application.service.JwtService; import server.poptato.global.exception.BaseException; import server.poptato.global.response.BaseErrorResponse; +import server.poptato.user.resolver.UserId; import static server.poptato.global.exception.errorcode.BaseExceptionErrorCode.*;