Skip to content

Commit

Permalink
[#4] ✍ 카카오 로그인 로직 분리
Browse files Browse the repository at this point in the history
  • Loading branch information
jun108059 committed Nov 14, 2021
1 parent e073796 commit b51c5b1
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
package com.teamnexters.lazy.api.controller;

import com.teamnexters.lazy.api.config.auth.jwt.Token;
import com.teamnexters.lazy.api.service.AuthService;
import com.teamnexters.lazy.common.error.ErrorResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@Tag(name = "Auth Controller", description = "소셜 로그인 관련 컨트롤러")
@Slf4j
@RequiredArgsConstructor
@Controller
public class AuthController {

@Value("${spring.security.oauth2.client.registration.kakao.auth-url}")
private String kakaoAuthUrl; // Auth URL

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

@Value("${spring.security.oauth2.client.registration.kakao.client-id}")
private String kakaoRestApiKey; // REST API Key

@Value("${spring.security.oauth2.client.registration.kakao.scope}")
private String kakaoScope; // 추가 동의 항목
private final AuthService authService;

@Operation(summary = "✅ 카카오 OAuth 랜딩 API",
description = "카카오 OAuth 페이지를 랜딩해요.",
Expand All @@ -32,21 +30,22 @@ public class AuthController {
responseCode = "200", description = "[Ok] Kakao Login Page Landing")})
@GetMapping("api/v1/oauth/kakao")
public String kakaoLoginLanding() {
StringBuffer url = new StringBuffer();
url.append(kakaoAuthUrl)
.append("/oauth/authorize?")
.append("client_id=")
.append(kakaoRestApiKey)
.append("&redirect_uri=")
.append(kakaoRedirectUri)
.append("&scope=")
.append(kakaoScope)
.append("&response_type=")
.append("code"); // code 고정

log.info("Kakao Login Landing URL : {}", url);
String kakaoLandingUrl = authService.makeKakaoLandingUrl();
return "redirect:" + kakaoLandingUrl;
}

return "redirect:" + url;
@Operation(summary = "✅ 카카오 액세스 토큰으로 App JWT 생성",
description = "카카오 액세스 토큰으로 App Server JWT 얻어와요.",
responses = {
@ApiResponse(
responseCode = "200", description = "[Ok] JWT Create By Kakao Token",
content = @Content(schema = @Schema(implementation = Token.class))),
@ApiResponse(
responseCode = "408", description = "[Error] Token is not valid",
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))})
@GetMapping(value = "api/v1/token/{kakao-token}")
public ResponseEntity<Token> kakaoAuthRequest(@PathVariable(name="kakao-token") String kakaoToken) {
return ResponseEntity.ok().body(authService.getTokenAndSaveMemberAfterKakaoUserApi(kakaoToken));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,29 @@ public class KakaoProfileDto {
private Long id;
private Properties properties;

@JsonProperty("kakao_account")
private Account account;

@Getter
@Setter
@ToString
private static class Properties {
public static class Properties {
@JsonProperty("nickname")
private String nickname;
public String nickname;
@JsonProperty("thumbnail_image")
private String thumbnailImage;
public String thumbnailImage;
@JsonProperty("profile_image")
private String profileImage;
public String profileImage;
}

/**
* https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#req-user-info
*/
@Getter
@Setter
@ToString
public static class Account {
@JsonProperty("email")
public String email;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.teamnexters.lazy.api.external.kakao;

import com.teamnexters.lazy.api.domain.oauth.kakao.KakaoProfileDto;
import com.teamnexters.lazy.api.exception.ExternalApiException;
import com.teamnexters.lazy.api.exception.TokenValidException;
import com.teamnexters.lazy.common.domain.member.Member;
import com.teamnexters.lazy.common.domain.member.Provider;
import com.teamnexters.lazy.common.domain.member.Role;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@Component
@RequiredArgsConstructor
public class KakaoClient {

private final WebClient webClient;

// Kakao User 정보 받아오는 URI
private static final String KAKAO_USER_INFO_URI = "https://kapi.kakao.com/v2/user/me";

public Member getUserInfoByKakaoApi(String accessToken) {
KakaoProfileDto kakaoUserResponse = webClient.get()
.uri(KAKAO_USER_INFO_URI)
.headers(h -> h.setBearerAuth(accessToken)) // Bearer 설정
.retrieve()
.onStatus(HttpStatus::is4xxClientError, response -> Mono.error(new TokenValidException()))
.onStatus(HttpStatus::is5xxServerError, response -> Mono.error(new ExternalApiException("GET : " + KAKAO_USER_INFO_URI)))
.bodyToMono(KakaoProfileDto.class)
.block();

return Member.builder()
.oauthId(String.valueOf(kakaoUserResponse.getId()))
.name(kakaoUserResponse.getProperties().getNickname())
.email(kakaoUserResponse.getAccount().getEmail())
.provider(Provider.KAKAO)
.role(Role.USER)
.picture(kakaoUserResponse.getProperties().getProfileImage())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.teamnexters.lazy.api.service;

import com.teamnexters.lazy.api.config.auth.jwt.JwtTokenProvider;
import com.teamnexters.lazy.api.config.auth.jwt.Token;
import com.teamnexters.lazy.api.external.kakao.KakaoClient;
import com.teamnexters.lazy.common.domain.member.Member;
import com.teamnexters.lazy.common.domain.member.MemberRepository;
import com.teamnexters.lazy.common.domain.member.Provider;
import com.teamnexters.lazy.common.domain.member.Role;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
@RequiredArgsConstructor
@PropertySource("classpath:application-oauth.yml")
public class AuthService {

@Value("${spring.security.oauth2.client.registration.kakao.auth-url}")
private String kakaoAuthUrl; // Auth URL

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

@Value("${spring.security.oauth2.client.registration.kakao.client-id}")
private String kakaoRestApiKey; // REST API Key

@Value("${spring.security.oauth2.client.registration.kakao.scope}")
private String kakaoScope; // 추가 동의 항목

private final KakaoClient kakaoClient;
private final MemberRepository memberRepository;
private final JwtTokenProvider jwtTokenProvider;

/**
* Kakao landing URL 생성
* @return Kakao landing URL
*/
public String makeKakaoLandingUrl() {
StringBuffer url = new StringBuffer();
url.append(kakaoAuthUrl)
.append("/oauth/authorize?")
.append("client_id=")
.append(kakaoRestApiKey)
.append("&redirect_uri=")
.append(kakaoRedirectUri)
.append("&scope=")
.append(kakaoScope)
.append("&response_type=")
.append("code"); // code 고정

log.info("Kakao Login Landing URL : {}", url);

return url.toString();
}

/**
* 카카오 API 통해 회원 정보 가져와서 생성 후 Token 발행
*
* @param kakaoToken Kakao Token
* @return App Server Token
*/
@Transactional
public Token getTokenAndSaveMemberAfterKakaoUserApi(String kakaoToken) {
// Kakao External Client API 호출
Member kakaoUserInfo = kakaoClient.getUserInfoByKakaoApi(kakaoToken);
String oauthId = kakaoUserInfo.getOauthId();
// Member
Member member = memberRepository.findByOauthIdAndProvider(oauthId, Provider.KAKAO).orElseGet(
() -> memberRepository.save(kakaoUserInfo)
);

// Token 생성
Token token = jwtTokenProvider.createToken(member.getEmail(), Role.USER);
log.info("### Token : [ {} ]", token);
return token;
}

}

0 comments on commit b51c5b1

Please sign in to comment.