Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/#14 social login kakao #15

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/with/picme/PicmeApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/with/picme/common/SocialType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.with.picme.common;

public enum SocialType {
kakao
}
12 changes: 10 additions & 2 deletions src/main/java/com/with/picme/common/message/ErrorMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public enum ErrorMessage {
EMPTY_TOKEN("빈 토큰입니다."),
INVALID_PASSWORD("잘못된 비밀번호입니다."),
INVALID_EMAIL("잘못된 이메일입니다."),

/**
* exception
**/
Expand All @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ public enum ResponseMessage {
/*
user
*/
GET_USER_INFO("유저 정보 갖고오기 성공");
GET_USER_INFO("유저 정보 갖고오기 성공"),

/*
social
*/
CHECK_KAKAO_USER_SUCCESS("카카오 계정 확인 성공");

private final String message;
}
14 changes: 14 additions & 0 deletions src/main/java/com/with/picme/config/kakao/KakaoAuth.java
Original file line number Diff line number Diff line change
@@ -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")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기 url을 yml에 따로 빼주면 좋을 거 같아요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵!

public interface KakaoAuth {

@GetMapping("/v2/user/me")
KakaoUserResponseDto getProfileInfo(@RequestHeader("Authorization") String accessToken);
}
51 changes: 51 additions & 0 deletions src/main/java/com/with/picme/config/kakao/KakaoAuthImpl.java
Original file line number Diff line number Diff line change
@@ -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){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거는 어디서 쓰는걸까요? interface를 만들었으면 Override해야 의미가 있다고 생각합니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

authService -> kakao->check에 쓰입니다!
kakaoAuth를 interface로 구현한 이유는 ,@FeignClient의 문법때문이고,
kakaoUser을 갖고오는 로직을 한 파일로 엮기위해 KakaoAuthImpl을 만들었습니당,
인터페이스를 implement한 이유는 @FeignClient를 사용하기 위해서입니다!

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){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리턴값을 사용하지 않으니까 void는 어떤가요?!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아주 좋습니다.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chckSocialUser는 Check한다는 역할만 부여하고(true / false 반환) 위에 getKakaoUser에서 throw하는게 더 바람직할 거 같아요! 메소드의 이름과 역할이 명확한게 중요하다 생각해서 남깁니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵! 감사합니다!

if (kakaoUser.id() == null){
return false;
}
return true;
}

private KakaoUser checkSocialUserHaveEmail(KakaoUserResponseDto kakaoUserResponseDto){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

email 등록되어있음 갖다 쓰고, 없으면 안쓰는 형식일까요?!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵맞습니당!

String email = "";
if (kakaoUserResponseDto.kakao_account().email() != null) {
email = kakaoUserResponseDto.kakao_account().email();
}
return KakaoUser.of(kakaoUserResponseDto.id(), email);
}
Comment on lines +44 to +50
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

대왕꼼꼼!!! 👍👍👍👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

온냐가 노드로 구현한거 그대로 로직 작성한거얌~
체고지영

}
20 changes: 14 additions & 6 deletions src/main/java/com/with/picme/controller/AuthController.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<ApiResponse> createUser(@RequestBody @Valid AuthSignUpRequestDto request) {
Expand All @@ -33,4 +35,10 @@ public ResponseEntity<ApiResponse> signInUser(@RequestBody AuthSignInRequestDto
AuthSignInResponseDto response = authService.signInUser(request);
return ResponseEntity.ok(ApiResponse.success(SUCCESS_SIGN_IN.getMessage(), response));
}

@PostMapping("/kakao/check")
public ResponseEntity<ApiResponse> findSocialUser(@RequestBody AuthSocialCheckRequestDto request) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

말씀하신대로 해당 로직 다 service로 넘기는게 맞다고 생각해요.
getUser + findByKey 로직도 합쳐서 반환하도록 합시다!!! 예외처리도 Service에서 하고용~! 컨트롤러는 전달에만 신경쓰도록 합시당

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵알겠습니당!

AuthSocialCheckResponseDto response = authService.findSocialUser(request);
return ResponseEntity.ok(ApiResponse.success(CHECK_KAKAO_USER_SUCCESS.getMessage(),response));
}
}
Original file line number Diff line number Diff line change
@@ -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
) {
}
Original file line number Diff line number Diff line change
@@ -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();

}
}
15 changes: 15 additions & 0 deletions src/main/java/com/with/picme/dto/auth/kakao/KakaoAccount.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
21 changes: 21 additions & 0 deletions src/main/java/com/with/picme/dto/auth/kakao/KakaoUser.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.with.picme.dto.auth.kakao;


import lombok.Builder;

@Builder
public record KakaoUserResponseDto(
Long id,
KakaoAccount kakao_account
GaHee99 marked this conversation as resolved.
Show resolved Hide resolved
) {
public static KakaoUserResponseDto of(Long id, KakaoAccount kakaoAccount) {
return KakaoUserResponseDto
.builder()
.id(id)
.kakao_account(kakaoAccount)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@
@Setter
@Entity
@NoArgsConstructor
@Table(name = "\"AuthenticationProvider\"")
public class AuthenticationProvider {
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id")
private Long id;

@Column(name="provider_type")
@Enumerated(EnumType.STRING)
private ProviderType provider;

@OneToOne(fetch = FetchType.LAZY)
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/with/picme/entity/ProviderType.java
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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<AuthenticationProvider, Long> {
@EntityGraph(attributePaths = "user")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

알아가요! 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사해요!!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 어떤 어노테이션인지 잘 몰라서 간략하게 설명 부탁드려도 될까용?!~새로 배운다 야호!

Optional<AuthenticationProvider> findByIdAndProvider(Long id, ProviderType providerType);
}
11 changes: 7 additions & 4 deletions src/main/java/com/with/picme/service/AuthService.java
Original file line number Diff line number Diff line change
@@ -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);
}
38 changes: 33 additions & 5 deletions src/main/java/com/with/picme/service/AuthServiceImpl.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,6 +19,8 @@

import javax.persistence.EntityNotFoundException;

import java.util.Optional;

import static com.with.picme.common.message.ErrorMessage.*;

@RequiredArgsConstructor
Expand All @@ -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()))
Expand Down Expand Up @@ -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> authenticationProvider = authenticationProviderRepository.findByIdAndProvider(kakaoUser.userId(), kakaoUser.providerType());

// 카카오 계정으로 우리 서비스에 회원가입 함
if(authenticationProvider.isPresent()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

말씀드렸듯이 위에 getUser와 findByKey 합치는게 좋을거 같아요!
response = AuthSocialCheckResponseDto.of(user.userId(),user.email(),false);
response = AuthSocialCheckResponseDto.of(user.userId(),user.email(),true);

이거 차이가 true, false밖에 없는데, isPresent일때 true DTO 반환하고 아닐때는 false DTO 반환하면 될거 같아요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵!!! 감사합니다!

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()))
Expand Down