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: OAuth2 소셜 로그인 구현 (#53) #97

Merged
merged 7 commits into from
May 30, 2024
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.waither.userservice.controller;

import com.waither.userservice.dto.response.KakaoResDto;
import com.waither.userservice.global.response.ApiResponse;
import com.waither.userservice.service.AccountsService;
import com.waither.userservice.service.KakaoService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/user/oauth")
public class OAuthController {

private final KakaoService kakaoService;

private final AccountsService accountsService;

@GetMapping("/kakao/callback")
public ApiResponse<?> callback(@RequestParam("code") String code) {

String accessTokenFromKakao = kakaoService.getAccessTokenFromKakao(code);

KakaoResDto.UserInfoResponseDto userInfo = kakaoService.getUserInfo(accessTokenFromKakao);

String email = userInfo.getKakaoAccount().getEmail();
if (!accountsService.isUserRegistered(email)) {
accountsService.signup(userInfo);
}

return ApiResponse.onSuccess(accountsService.provideTokenForOAuth(email));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.waither.userservice.dto.converter;

import com.waither.userservice.dto.request.AccountReqDto;
import com.waither.userservice.dto.response.KakaoResDto;
import com.waither.userservice.entity.User;
import com.waither.userservice.entity.type.UserStatus;

public class AccountConverter {

public static User toUser(AccountReqDto.RegisterRequestDto requestDto, String encodedPw) {
return User.builder()
.email(requestDto.email())
.password(encodedPw)
.nickname("추워하는 곰탱이")
.status(UserStatus.ACTIVE)
.role("ROLE_USER")
.custom(true)
.build();
}

public static User toUser(KakaoResDto.UserInfoResponseDto userInfo) {
return User.builder()
.authId(userInfo.getId())
.nickname(userInfo.getKakaoAccount().getProfile().getNickName())
.email(userInfo.getKakaoAccount().getEmail())
.status(UserStatus.ACTIVE)
.custom(true)
.role("ROLE_USER")
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
package com.waither.userservice.dto.response;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.Date;
import java.util.HashMap;

public class KakaoResDto {

@Getter
@NoArgsConstructor //역직렬화를 위한 기본 생성자
@JsonIgnoreProperties(ignoreUnknown = true)
public static class TokenResponseDto {

@JsonProperty("token_type")
public String tokenType;
@JsonProperty("access_token")
public String accessToken;
@JsonProperty("id_token")
public String idToken;
@JsonProperty("expires_in")
public Integer expiresIn;
@JsonProperty("refresh_token")
public String refreshToken;
@JsonProperty("refresh_token_expires_in")
public Integer refreshTokenExpiresIn;
@JsonProperty("scope")
public String scope;
}

@Getter
@NoArgsConstructor //역직렬화를 위한 기본 생성자
@JsonIgnoreProperties(ignoreUnknown = true)
public static class UserInfoResponseDto {

/**
* 동의 항목
* 닉네임, 이메일 필수 동의
*/

//회원 번호
@JsonProperty("id")
public Long id;

//자동 연결 설정을 비활성화한 경우만 존재.
//true : 연결 상태, false : 연결 대기 상태
@JsonProperty("has_signed_up")
public Boolean hasSignedUp;

//서비스에 연결 완료된 시각. UTC
@JsonProperty("connected_at")
public Date connectedAt;

//카카오싱크 간편가입을 통해 로그인한 시각. UTC
@JsonProperty("synched_at")
public Date synchedAt;

//사용자 프로퍼티
@JsonProperty("properties")
public HashMap<String, String> properties;

//카카오 계정 정보
@JsonProperty("kakao_account")
public KakaoAccount kakaoAccount;

//uuid 등 추가 정보
@JsonProperty("for_partner")
public Partner partner;

@Getter
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class KakaoAccount {

//프로필 정보 제공 동의 여부
@JsonProperty("profile_needs_agreement")
public Boolean isProfileAgree;

//닉네임 제공 동의 여부
@JsonProperty("profile_nickname_needs_agreement")
public Boolean isNickNameAgree;

//프로필 사진 제공 동의 여부
@JsonProperty("profile_image_needs_agreement")
public Boolean isProfileImageAgree;

//사용자 프로필 정보
@JsonProperty("profile")
public Profile profile;

//이름 제공 동의 여부
@JsonProperty("name_needs_agreement")
public Boolean isNameAgree;

//카카오계정 이름
@JsonProperty("name")
public String name;

//이메일 제공 동의 여부
@JsonProperty("email_needs_agreement")
public Boolean isEmailAgree;

//이메일이 유효 여부
// true : 유효한 이메일, false : 이메일이 다른 카카오 계정에 사용돼 만료
@JsonProperty("is_email_valid")
public Boolean isEmailValid;

//이메일이 인증 여부
//true : 인증된 이메일, false : 인증되지 않은 이메일
@JsonProperty("is_email_verified")
public Boolean isEmailVerified;

//카카오계정 대표 이메일
@JsonProperty("email")
public String email;

//연령대 제공 동의 여부
@JsonProperty("age_range_needs_agreement")
public Boolean isAgeAgree;

//연령대
//참고 https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#req-user-info
@JsonProperty("age_range")
public String ageRange;

//출생 연도 제공 동의 여부
@JsonProperty("birthyear_needs_agreement")
public Boolean isBirthYearAgree;

//출생 연도 (YYYY 형식)
@JsonProperty("birthyear")
public String birthYear;

//생일 제공 동의 여부
@JsonProperty("birthday_needs_agreement")
public Boolean isBirthDayAgree;

//생일 (MMDD 형식)
@JsonProperty("birthday")
public String birthDay;

//생일 타입
// SOLAR(양력) 혹은 LUNAR(음력)
@JsonProperty("birthday_type")
public String birthDayType;

//성별 제공 동의 여부
@JsonProperty("gender_needs_agreement")
public Boolean isGenderAgree;

//성별
@JsonProperty("gender")
public String gender;

//전화번호 제공 동의 여부
@JsonProperty("phone_number_needs_agreement")
public Boolean isPhoneNumberAgree;

//전화번호
//국내 번호인 경우 +82 00-0000-0000 형식
@JsonProperty("phone_number")
public String phoneNumber;

//CI 동의 여부
@JsonProperty("ci_needs_agreement")
public Boolean isCIAgree;

//CI, 연계 정보
@JsonProperty("ci")
public String ci;

//CI 발급 시각, UTC
@JsonProperty("ci_authenticated_at")
public Date ciCreatedAt;

@Getter
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Profile {

//닉네임
@JsonProperty("nickname")
public String nickName;

//프로필 미리보기 이미지 URL
@JsonProperty("thumbnail_image_url")
public String thumbnailImageUrl;

//프로필 사진 URL
@JsonProperty("profile_image_url")
public String profileImageUrl;

//프로필 사진 URL 기본 프로필인지 여부
//true : 기본 프로필, false : 사용자 등록
@JsonProperty("is_default_image")
public String isDefaultImage;

//닉네임이 기본 닉네임인지 여부
//true : 기본 닉네임, false : 사용자 등록
@JsonProperty("is_default_nickname")
public Boolean isDefaultNickName;

}
}

@Getter
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Partner {
//고유 ID
@JsonProperty("uuid")
public String uuid;
}

}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.waither.userservice.entity;

import com.waither.userservice.entity.enums.UserStatus;
import com.waither.userservice.entity.type.AuthType;
import com.waither.userservice.global.BaseEntity;
import jakarta.persistence.*;
import lombok.*;
Expand All @@ -19,18 +20,26 @@ public class User extends BaseEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

// OAuth ID
@Column(name = "auth_id")
private Long authId;

// 유저 이메일
@Column(name = "email", nullable = false, unique = true)
private String email;

// 유저 비밀번호
@Column(name = "password", nullable = false)
@Column(name = "password")
private String password;

// 유저 닉네임
@Column(name = "nickname", nullable = false)
private String nickname;

// 회원 가입 타입
@Enumerated(EnumType.STRING)
private AuthType authType;

// 유저 상태 (active / 휴면 / 탈퇴 등)
@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.waither.userservice.entity.type;

public enum AuthType {

EMAIL,
KAKAO,
GOOGLE,
APPLE;
}
Loading
Loading