diff --git a/build.gradle b/build.gradle index 20c5b2c..56b7bb0 100644 --- a/build.gradle +++ b/build.gradle @@ -39,6 +39,8 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2' implementation 'org.springframework.boot:spring-boot-starter-validation' + // feignClient + implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-openfeign', version: '3.1.1' } tasks.named('test') { diff --git a/src/main/java/com/with/picme/PicmeApplication.java b/src/main/java/com/with/picme/PicmeApplication.java index 3876709..2692854 100644 --- a/src/main/java/com/with/picme/PicmeApplication.java +++ b/src/main/java/com/with/picme/PicmeApplication.java @@ -2,7 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; +@EnableFeignClients @SpringBootApplication public class PicmeApplication { diff --git a/src/main/java/com/with/picme/common/SocialType.java b/src/main/java/com/with/picme/common/SocialType.java new file mode 100644 index 0000000..e19550c --- /dev/null +++ b/src/main/java/com/with/picme/common/SocialType.java @@ -0,0 +1,5 @@ +package com.with.picme.common; + +public enum SocialType { + kakao +} diff --git a/src/main/java/com/with/picme/common/message/ErrorMessage.java b/src/main/java/com/with/picme/common/message/ErrorMessage.java index cc9b214..6dd4866 100644 --- a/src/main/java/com/with/picme/common/message/ErrorMessage.java +++ b/src/main/java/com/with/picme/common/message/ErrorMessage.java @@ -23,7 +23,7 @@ public enum ErrorMessage { EMPTY_TOKEN("빈 토큰입니다."), INVALID_PASSWORD("잘못된 비밀번호입니다."), INVALID_EMAIL("잘못된 이메일입니다."), - + /** * exception **/ @@ -38,7 +38,15 @@ public enum ErrorMessage { /** * user */ - CANT_GET_USERINFO("유저 아이디를 갖고올 수 없습니다."); + CANT_GET_USERINFO("유저 아이디를 갖고올 수 없습니다."), + + + /* + * social + */ + NO_SOCIAL_TYPE("제공하는 소셜 서비스가 다릅니다."), + NO_SOCIAL_USER("소셜 서비스에 가입하지 않은 유저입니다"), + NOT_FOUND_SOCIAL_TOKEN("소셜로그인 토큰이 유효하지 않습니다."); private final String message; } diff --git a/src/main/java/com/with/picme/common/message/ResponseMessage.java b/src/main/java/com/with/picme/common/message/ResponseMessage.java index 4754dd4..c7c9391 100644 --- a/src/main/java/com/with/picme/common/message/ResponseMessage.java +++ b/src/main/java/com/with/picme/common/message/ResponseMessage.java @@ -21,8 +21,12 @@ public enum ResponseMessage { /* user */ - GET_USER_INFO("유저 정보 갖고오기 성공"); + GET_USER_INFO("유저 정보 갖고오기 성공"), + /* + social + */ + CHECK_KAKAO_USER_SUCCESS("카카오 계정 확인 성공"); private final String message; } diff --git a/src/main/java/com/with/picme/config/kakao/KakaoAuth.java b/src/main/java/com/with/picme/config/kakao/KakaoAuth.java new file mode 100644 index 0000000..5c74d6c --- /dev/null +++ b/src/main/java/com/with/picme/config/kakao/KakaoAuth.java @@ -0,0 +1,14 @@ +package com.with.picme.config.kakao; + + +import com.with.picme.dto.auth.kakao.KakaoUserResponseDto; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; + +@FeignClient(name = "kakaoAuth", url = "https://kapi.kakao.com") +public interface KakaoAuth { + + @GetMapping("/v2/user/me") + KakaoUserResponseDto getProfileInfo(@RequestHeader("Authorization") String accessToken); +} diff --git a/src/main/java/com/with/picme/config/kakao/KakaoAuthImpl.java b/src/main/java/com/with/picme/config/kakao/KakaoAuthImpl.java new file mode 100644 index 0000000..f0977a8 --- /dev/null +++ b/src/main/java/com/with/picme/config/kakao/KakaoAuthImpl.java @@ -0,0 +1,51 @@ +package com.with.picme.config.kakao; + +import com.with.picme.common.message.ErrorMessage; +import com.with.picme.dto.auth.kakao.KakaoUser; +import com.with.picme.dto.auth.kakao.KakaoUserResponseDto; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import javax.persistence.EntityNotFoundException; + +import static com.with.picme.common.message.ErrorMessage.*; + +@RequiredArgsConstructor +@Component +public class KakaoAuthImpl implements KakaoAuth { + private final KakaoAuth kakaoAuth; + + @Override + public KakaoUserResponseDto getProfileInfo(String accessToken) { + try { + KakaoUserResponseDto kakaoProfile = kakaoAuth.getProfileInfo(accessToken); + return kakaoProfile; + } catch (Exception e) { + throw new EntityNotFoundException(ErrorMessage.NOT_FOUND_SOCIAL_TOKEN.getMessage()); + } + } + + public KakaoUser getKakaoUser(String accessToken){ + KakaoUserResponseDto user = this.getProfileInfo(accessToken); + if(!checkSocialUser(user)){ + throw new IllegalArgumentException(NO_SOCIAL_USER.getMessage()); + }; + KakaoUser kakaoUser = checkSocialUserHaveEmail(user); + return kakaoUser; + } + + private boolean checkSocialUser(KakaoUserResponseDto kakaoUser){ + if (kakaoUser.id() == null){ + return false; + } + return true; + } + + private KakaoUser checkSocialUserHaveEmail(KakaoUserResponseDto kakaoUserResponseDto){ + String email = ""; + if (kakaoUserResponseDto.kakao_account().email() != null) { + email = kakaoUserResponseDto.kakao_account().email(); + } + return KakaoUser.of(kakaoUserResponseDto.id(), email); + } +} diff --git a/src/main/java/com/with/picme/controller/AuthController.java b/src/main/java/com/with/picme/controller/AuthController.java index 34cff9b..e210f99 100644 --- a/src/main/java/com/with/picme/controller/AuthController.java +++ b/src/main/java/com/with/picme/controller/AuthController.java @@ -1,10 +1,10 @@ package com.with.picme.controller; import com.with.picme.common.ApiResponse; -import com.with.picme.dto.auth.AuthSignInRequestDto; -import com.with.picme.dto.auth.AuthSignInResponseDto; -import com.with.picme.dto.auth.AuthSignUpRequestDto; -import com.with.picme.dto.auth.AuthSignUpResponseDto; +import com.with.picme.dto.auth.*; +import com.with.picme.dto.auth.kakao.KakaoUser; +import com.with.picme.entity.User; +import com.with.picme.repository.AuthenticationProviderRepository; import com.with.picme.service.AuthServiceImpl; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -13,14 +13,16 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; -import static com.with.picme.common.message.ResponseMessage.SUCCESS_SIGN_IN; -import static com.with.picme.common.message.ResponseMessage.SUCCESS_SIGN_UP; + + +import static com.with.picme.common.message.ResponseMessage.*; @RestController @RequestMapping("/auth") @RequiredArgsConstructor public class AuthController { private final AuthServiceImpl authService; + private final AuthenticationProviderRepository authenticationProviderRepository; @PostMapping("") public ResponseEntity createUser(@RequestBody @Valid AuthSignUpRequestDto request) { @@ -33,4 +35,10 @@ public ResponseEntity signInUser(@RequestBody AuthSignInRequestDto AuthSignInResponseDto response = authService.signInUser(request); return ResponseEntity.ok(ApiResponse.success(SUCCESS_SIGN_IN.getMessage(), response)); } + + @PostMapping("/kakao/check") + public ResponseEntity findSocialUser(@RequestBody AuthSocialCheckRequestDto request) { + AuthSocialCheckResponseDto response = authService.findSocialUser(request); + return ResponseEntity.ok(ApiResponse.success(CHECK_KAKAO_USER_SUCCESS.getMessage(),response)); + } } diff --git a/src/main/java/com/with/picme/dto/auth/AuthSocialCheckRequestDto.java b/src/main/java/com/with/picme/dto/auth/AuthSocialCheckRequestDto.java new file mode 100644 index 0000000..3e70ed6 --- /dev/null +++ b/src/main/java/com/with/picme/dto/auth/AuthSocialCheckRequestDto.java @@ -0,0 +1,14 @@ +package com.with.picme.dto.auth; + + +import javax.validation.constraints.NotBlank; + + +public record AuthSocialCheckRequestDto( + @NotBlank(message="필요한 값이 없습니다") + String socialType, + + @NotBlank(message = "필요한 값이 없습니다.") + String token +) { +} diff --git a/src/main/java/com/with/picme/dto/auth/AuthSocialCheckResponseDto.java b/src/main/java/com/with/picme/dto/auth/AuthSocialCheckResponseDto.java new file mode 100644 index 0000000..1471fb0 --- /dev/null +++ b/src/main/java/com/with/picme/dto/auth/AuthSocialCheckResponseDto.java @@ -0,0 +1,20 @@ +package com.with.picme.dto.auth; + +import lombok.Builder; + +@Builder +public record AuthSocialCheckResponseDto ( + Long uid, + String email, + boolean isUser +){ + public static AuthSocialCheckResponseDto of(Long uid, String email, boolean isUser){ + return AuthSocialCheckResponseDto + .builder() + .uid(uid) + .email(email) + .isUser(isUser) + .build(); + + } +} diff --git a/src/main/java/com/with/picme/dto/auth/kakao/KakaoAccount.java b/src/main/java/com/with/picme/dto/auth/kakao/KakaoAccount.java new file mode 100644 index 0000000..b320aae --- /dev/null +++ b/src/main/java/com/with/picme/dto/auth/kakao/KakaoAccount.java @@ -0,0 +1,15 @@ +package com.with.picme.dto.auth.kakao; + +import lombok.Builder; + +@Builder +public record KakaoAccount( + String email +) { + public static KakaoAccount of(String email){ + return KakaoAccount + .builder() + .email(email) + .build(); + } +} diff --git a/src/main/java/com/with/picme/dto/auth/kakao/KakaoUser.java b/src/main/java/com/with/picme/dto/auth/kakao/KakaoUser.java new file mode 100644 index 0000000..00eeaf2 --- /dev/null +++ b/src/main/java/com/with/picme/dto/auth/kakao/KakaoUser.java @@ -0,0 +1,21 @@ +package com.with.picme.dto.auth.kakao; + + +import com.with.picme.entity.ProviderType; +import lombok.Builder; + +@Builder +public record KakaoUser( + Long userId, + String email, + ProviderType providerType +) { + public static KakaoUser of(Long userId, String email){ + return KakaoUser + .builder() + .userId(userId) + .email(email) + .providerType(ProviderType.kakao) + .build(); + } +} diff --git a/src/main/java/com/with/picme/dto/auth/kakao/KakaoUserResponseDto.java b/src/main/java/com/with/picme/dto/auth/kakao/KakaoUserResponseDto.java new file mode 100644 index 0000000..26e0746 --- /dev/null +++ b/src/main/java/com/with/picme/dto/auth/kakao/KakaoUserResponseDto.java @@ -0,0 +1,18 @@ +package com.with.picme.dto.auth.kakao; + + +import lombok.Builder; + +@Builder +public record KakaoUserResponseDto( + Long id, + KakaoAccount kakao_account +) { + public static KakaoUserResponseDto of(Long id, KakaoAccount kakaoAccount) { + return KakaoUserResponseDto + .builder() + .id(id) + .kakao_account(kakaoAccount) + .build(); + } +} diff --git a/src/main/java/com/with/picme/entity/AuthenticationProvider.java b/src/main/java/com/with/picme/entity/AuthenticationProvider.java index 3be4b08..b059108 100644 --- a/src/main/java/com/with/picme/entity/AuthenticationProvider.java +++ b/src/main/java/com/with/picme/entity/AuthenticationProvider.java @@ -13,6 +13,7 @@ @Setter @Entity @NoArgsConstructor +@Table(name = "\"AuthenticationProvider\"") public class AuthenticationProvider { @Id @GeneratedValue(strategy = IDENTITY) @@ -20,6 +21,7 @@ public class AuthenticationProvider { private Long id; @Column(name="provider_type") + @Enumerated(EnumType.STRING) private ProviderType provider; @OneToOne(fetch = FetchType.LAZY) diff --git a/src/main/java/com/with/picme/entity/ProviderType.java b/src/main/java/com/with/picme/entity/ProviderType.java index e099149..ebb53f3 100644 --- a/src/main/java/com/with/picme/entity/ProviderType.java +++ b/src/main/java/com/with/picme/entity/ProviderType.java @@ -1,5 +1,9 @@ package com.with.picme.entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; + public enum ProviderType { + @Enumerated(EnumType.STRING) kakao, naver, google } diff --git a/src/main/java/com/with/picme/repository/AuthenticationProviderRepository.java b/src/main/java/com/with/picme/repository/AuthenticationProviderRepository.java new file mode 100644 index 0000000..89c2a84 --- /dev/null +++ b/src/main/java/com/with/picme/repository/AuthenticationProviderRepository.java @@ -0,0 +1,14 @@ +package com.with.picme.repository; + +import com.with.picme.entity.AuthenticationProvider; +import com.with.picme.entity.ProviderType; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; + + +import java.util.Optional; + +public interface AuthenticationProviderRepository extends JpaRepository { + @EntityGraph(attributePaths = "user") + Optional findByIdAndProvider(Long id, ProviderType providerType); +} diff --git a/src/main/java/com/with/picme/service/AuthService.java b/src/main/java/com/with/picme/service/AuthService.java index 1c5ed90..a49edd7 100644 --- a/src/main/java/com/with/picme/service/AuthService.java +++ b/src/main/java/com/with/picme/service/AuthService.java @@ -1,12 +1,15 @@ package com.with.picme.service; -import com.with.picme.dto.auth.AuthSignInRequestDto; -import com.with.picme.dto.auth.AuthSignInResponseDto; -import com.with.picme.dto.auth.AuthSignUpRequestDto; -import com.with.picme.dto.auth.AuthSignUpResponseDto; +import com.with.picme.dto.auth.*; +import com.with.picme.dto.auth.kakao.KakaoUser; +import com.with.picme.entity.AuthenticationProvider; +import com.with.picme.entity.User; + +import java.util.Optional; public interface AuthService { AuthSignUpResponseDto createUser(AuthSignUpRequestDto request); AuthSignInResponseDto signInUser(AuthSignInRequestDto request); + AuthSocialCheckResponseDto findSocialUser(AuthSocialCheckRequestDto request); } diff --git a/src/main/java/com/with/picme/service/AuthServiceImpl.java b/src/main/java/com/with/picme/service/AuthServiceImpl.java index 11fd807..647cbd6 100644 --- a/src/main/java/com/with/picme/service/AuthServiceImpl.java +++ b/src/main/java/com/with/picme/service/AuthServiceImpl.java @@ -1,13 +1,16 @@ package com.with.picme.service; +import com.with.picme.common.SocialType; +import com.with.picme.common.message.ErrorMessage; +import com.with.picme.config.kakao.KakaoAuthImpl; import com.with.picme.config.SaltEncrypt; import com.with.picme.config.jwt.JwtTokenProvider; import com.with.picme.config.jwt.UserAuthentication; -import com.with.picme.dto.auth.AuthSignInRequestDto; -import com.with.picme.dto.auth.AuthSignInResponseDto; -import com.with.picme.dto.auth.AuthSignUpRequestDto; -import com.with.picme.dto.auth.AuthSignUpResponseDto; +import com.with.picme.dto.auth.*; +import com.with.picme.dto.auth.kakao.KakaoUser; +import com.with.picme.entity.AuthenticationProvider; import com.with.picme.entity.User; +import com.with.picme.repository.AuthenticationProviderRepository; import com.with.picme.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.security.core.Authentication; @@ -16,6 +19,8 @@ import javax.persistence.EntityNotFoundException; +import java.util.Optional; + import static com.with.picme.common.message.ErrorMessage.*; @RequiredArgsConstructor @@ -25,7 +30,8 @@ public class AuthServiceImpl implements AuthService { private final UserRepository userRepository; private final SaltEncrypt saltEncrypt; private final JwtTokenProvider tokenProvider; - + private final KakaoAuthImpl kakaoAuthImpl; + private final AuthenticationProviderRepository authenticationProviderRepository; @Override public AuthSignUpResponseDto createUser(AuthSignUpRequestDto request) { if (validateEmail(request.email())) @@ -66,6 +72,28 @@ public AuthSignInResponseDto signInUser(AuthSignInRequestDto request) { return AuthSignInResponseDto.of(user, accessToken); } + + @Override + public AuthSocialCheckResponseDto findSocialUser(AuthSocialCheckRequestDto request) { + if (!request.socialType().equals(SocialType.kakao.toString())) { //토큰 타입 확인 + throw new IllegalArgumentException(NO_SOCIAL_TYPE.getMessage()); + } + KakaoUser kakaoUser = kakaoAuthImpl.getKakaoUser("Bearer " + request.token()); //카카오 계정 확인 + + Optional authenticationProvider = authenticationProviderRepository.findByIdAndProvider(kakaoUser.userId(), kakaoUser.providerType()); + + // 카카오 계정으로 우리 서비스에 회원가입 함 + if(authenticationProvider.isPresent()) { + Long userId = authenticationProvider.get().getUser().getId(); + //유저 테이블에서 찾기 + userRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException(ErrorMessage.CANT_GET_USERINFO.getMessage())); + return AuthSocialCheckResponseDto.of(kakaoUser.userId(),kakaoUser.email(),true); + } + // 카카오 계정은 확인, 우리 서비스에는 회원가입 안됨 + return AuthSocialCheckResponseDto.of(kakaoUser.userId(),kakaoUser.email(),false); + } + private User checkPassword(String email, String password) { User user = userRepository.findByEmail(email); if (saltEncrypt.isMatch(password, user.getPassword()))