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(KL-155): create kakao oauth2 #58

Merged
merged 34 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
95628c8
KL-155/chore: create oauth2 config file
idealflower-k Aug 20, 2024
96fcb51
KL-155/chore: include oauth2 in application.yaml
idealflower-k Aug 20, 2024
4bca1ae
KL-155/feat: create kakao oauth2 controller, service
idealflower-k Aug 20, 2024
3084b91
KL-155/chore: add profile name in oauth2
idealflower-k Aug 21, 2024
a2818c3
KL-155/chore: rename user entity name
idealflower-k Aug 21, 2024
c93085f
KL-155/feat: create user not found error
idealflower-k Aug 21, 2024
e11ffed
KL-155/fix: fix gender converter
idealflower-k Aug 21, 2024
016468f
KL-155/feat: add find user by id method in UserUtil
idealflower-k Aug 21, 2024
6bef151
KL-155/feat: add static factory method in UserCreateRequest
idealflower-k Aug 21, 2024
f4e17e7
KL-155/feat: create oauth entity
idealflower-k Aug 21, 2024
178bfaf
KL-155/feat: create oauth repository
idealflower-k Aug 21, 2024
9789b9a
KL-155/feat: create kakao user info request dto
idealflower-k Aug 21, 2024
d0ec5bf
KL-155/refactor: refactor oauth service
idealflower-k Aug 21, 2024
4ce3e22
KL-155/refactor: oauth controller
idealflower-k Aug 21, 2024
44cf239
Merge branch 'develop' into KL-155/kakao-oauth-2-๊ตฌํ˜„
idealflower-k Aug 21, 2024
7af0aa6
KL-155/refactor: refactor kakao oauth login
idealflower-k Aug 22, 2024
742f420
KL-155/feat: create HttpClientErrorException handler
idealflower-k Aug 22, 2024
8142184
KL-155/refactor: refactor trimDoubleQuote method
idealflower-k Aug 22, 2024
8de5359
KL-155/style: rename oauth to oauth2
idealflower-k Aug 22, 2024
70c6ae6
Merge branch 'develop' into KL-155/kakao-oauth-2-๊ตฌํ˜„
idealflower-k Aug 22, 2024
c72375f
KL-155/chore: add swagger annotation
idealflower-k Aug 22, 2024
20c9577
KL-155/style: add final keyword
idealflower-k Aug 22, 2024
634850e
KL-155/style: add final keyword, rename oauth field
idealflower-k Aug 22, 2024
9ea914b
KL-155/refactor: handle 9973 in user constants
idealflower-k Aug 22, 2024
5f68e9c
KL-155/style: add final keyword
idealflower-k Aug 22, 2024
6110e62
KL-155/style: add final keyword
idealflower-k Aug 22, 2024
a299759
KL-155/style: add final keyword
idealflower-k Aug 22, 2024
68b325c
KL-155/style: improved readability oauthKakao
idealflower-k Aug 22, 2024
28c2ac5
KL-155/style: rename variable oauthMemberId
idealflower-k Aug 22, 2024
77e9328
KL-155/style: add final keyword
idealflower-k Aug 22, 2024
3cbf846
KL-155/fix: fix register User to create user
idealflower-k Aug 22, 2024
fbade72
KL-155/style: rename oauth2 to oauth
idealflower-k Aug 22, 2024
bded19b
KL-155/style: rename id to oauthMemberId
idealflower-k Aug 22, 2024
4f851dd
KL-155/style: change upper case
idealflower-k Aug 22, 2024
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,88 @@
package taco.klkl.domain.oauth.controller;

import java.net.URI;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
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;

import com.fasterxml.jackson.core.JsonProcessingException;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import taco.klkl.domain.oauth.service.OauthKakaoService;
import taco.klkl.domain.user.dto.response.UserDetailResponse;

@Slf4j
@RestController
@RequestMapping("/v1/oauth/kakao")
@RequiredArgsConstructor
@Tag(name = "9. ์ธ์ฆ/์ธ๊ฐ€", description = "์ธ์ฆ/์ธ๊ฐ€ API")
public class OauthKakaoController {

private final OauthKakaoService oauthKakaoService;

@Value("${spring.security.oauth2.client.registration.kakao.client-id}")
private String clientId;

@Value("${spring.security.oauth2.client.provider.kakao.authorization-uri}")
private String authorizationUri;

@Value("${spring.security.oauth2.client.registration.kakao.redirect-uri}")
private String redirectUri;

@Value("${api.main-url}")
private String mainUrl;

/**
* ํด๋ผ์ด์–ธํŠธ๋ฅผ Kakao Oauth URL๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธํ•ฉ๋‹ˆ๋‹ค.
* @return
*/
@GetMapping()
@Operation(summary = "kakao ๊ฐ„ํŽธ๋กœ๊ทธ์ธ ์š”์ฒญ", description = "์นด์นด์˜ค oauth๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.")
public ResponseEntity<Void> oauthKakao() {
final String location = getKakaoOauthLocation();
final URI locationUri = URI.create(location);

return ResponseEntity
.status(HttpStatus.FOUND)
.location(locationUri)
.build();
}

/**
* Kakao ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋ฐ›์€ code๊ฐ’์œผ๋กœ ์‚ฌ์šฉ์ž ๋กœ๊ทธ์ธ์ฒ˜๋ฆฌ๋ฅผ ํ•ฉ๋‹ˆ๋‹ค.
* @param code
* @return
* @throws JsonProcessingException
*/
// TODO: JWT์ ์šฉ์‹œ ํ† ํฐ ๊ด€๋ฆฌ ๋กœ์ง ์ถ”๊ฐ€
@GetMapping("/code")
@Operation(summary = "kakao ์‚ฌ์šฉ์ž ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ", description = "์นด์นด์˜ค API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.")
public UserDetailResponse processKakaoOauth2(@RequestParam("code") final String code) throws
JsonProcessingException {

return oauthKakaoService.kakaoOauthLogin(code);
}

/**
* Kakao Oauth ์š”์ฒญ URL์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
* @return
*/
private String getKakaoOauthLocation() {
StringBuilder location = new StringBuilder();
location.append(authorizationUri)
.append("?response_type=").append("code")
.append("&client_id=").append(clientId)
.append("&redirect_uri=").append(mainUrl).append(redirectUri);

return location.toString();
}

}
11 changes: 11 additions & 0 deletions src/main/java/taco/klkl/domain/oauth/dao/OauthRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package taco.klkl.domain.oauth.dao;

import org.springframework.data.jpa.repository.JpaRepository;

import taco.klkl.domain.oauth.domain.Oauth;

public interface OauthRepository extends JpaRepository<Oauth, Long> {
boolean existsByOauthMemberId(final Long oauthMemberId);

Oauth findFirstByOauthMemberId(final Long oauthMemberId);
}
42 changes: 42 additions & 0 deletions src/main/java/taco/klkl/domain/oauth/domain/Oauth.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package taco.klkl.domain.oauth.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import taco.klkl.domain.user.domain.User;

@Getter
@Entity(name = "oauth")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Oauth {

@Id
@Column(name = "oauth_id")
@GeneratedValue(strategy = GenerationType.AUTO)
Long id;

@OneToOne(optional = false, fetch = FetchType.EAGER)
private User user;

@Column(
name = "oauth_member_id",
nullable = false
)
private Long oauthMemberId;

private Oauth(final User user, final Long oauthMemberId) {
this.user = user;
this.oauthMemberId = oauthMemberId;
}

public static Oauth of(final User user, final Long oauth2MemberId) {
return new Oauth(user, oauth2MemberId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package taco.klkl.domain.oauth.dto.request;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public record KakaoUserInfoRequest(Long oauthMemberId, String nickname, String profileImage) {

public static KakaoUserInfoRequest of(final Long oauthMemberId, final String nickname, final String profileImage) {
return new KakaoUserInfoRequest(oauthMemberId, nickname, profileImage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package taco.klkl.domain.oauth.service;

import org.springframework.stereotype.Service;

import taco.klkl.domain.oauth.dto.request.KakaoUserInfoRequest;
import taco.klkl.domain.user.dto.response.UserDetailResponse;

@Service
public interface OauthKakaoLoginService {
UserDetailResponse loginUser(final KakaoUserInfoRequest userInfoRequest);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package taco.klkl.domain.oauth.service;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import taco.klkl.domain.oauth.dao.OauthRepository;
import taco.klkl.domain.oauth.domain.Oauth;
import taco.klkl.domain.oauth.dto.request.KakaoUserInfoRequest;
import taco.klkl.domain.user.domain.Gender;
import taco.klkl.domain.user.domain.User;
import taco.klkl.domain.user.dto.request.UserCreateRequest;
import taco.klkl.domain.user.dto.response.UserDetailResponse;
import taco.klkl.domain.user.service.UserService;
import taco.klkl.global.util.UserUtil;

@Slf4j
@Primary
@Service
@Transactional
@RequiredArgsConstructor
public class OauthKakaoLoginServiceImpl implements OauthKakaoLoginService {

private final OauthRepository oauthRepository;
private final UserService userService;
private final UserUtil userUtil;

/**
* Oauth์˜ ๊ฒฐ๊ณผ๋กœ ์‚ฌ์šฉ์ž ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
* TODO: ํ˜„์žฌ ๋”๋ฏธ์œ ์ € ๋ฐ์ดํ„ฐ๋กœ ์ธํ•ด ์ตœ์ดˆ์š”์ฒญ์€ ์—๋Ÿฌ๋ฐœ์ƒ
* @param userInfoRequest
* @return
*/
public UserDetailResponse loginUser(final KakaoUserInfoRequest userInfoRequest) {

final Long oauthMemberId = userInfoRequest.oauthMemberId();

// ์ด๋ฏธ oauth๋กœ๊ทธ์ธ ๊ธฐ๋ก์ด ์žˆ๋Š” ์œ ์ €๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
if (oauthRepository.existsByOauthMemberId(oauthMemberId)) {
final Oauth oauth = oauthRepository.findFirstByOauthMemberId(oauthMemberId);
final User user = oauth.getUser();
return UserDetailResponse.from(user);
}

final User user = registerUser(userInfoRequest);
final Oauth oauth = Oauth.of(user, userInfoRequest.oauthMemberId());
oauthRepository.save(oauth);

return UserDetailResponse.from(user);
}

private User registerUser(final KakaoUserInfoRequest userInfoRequest) {

final String name = userUtil.createUsername(userInfoRequest.nickname(), userInfoRequest.oauthMemberId());

// TODO: ์„ฑ๋ณ„, ๋‚˜์ด๋Š” ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋„ฃ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
final UserCreateRequest userCreateRequest = UserCreateRequest.of(
name,
Gender.MALE.getDescription(),
0,
userInfoRequest.profileImage(),
""
);

// ์œ ์ €๋ฅผ DB์— ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
return userService.createUser(userCreateRequest);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package taco.klkl.domain.oauth.service;

import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.JsonProcessingException;

import taco.klkl.domain.user.dto.response.UserDetailResponse;

@Service
public interface OauthKakaoService {
UserDetailResponse kakaoOauthLogin(final String code) throws JsonProcessingException;
}
Loading