Skip to content

Commit

Permalink
Merge pull request #286 from Gongjakso/feat/naver-login
Browse files Browse the repository at this point in the history
Feat: Naver login 구현
  • Loading branch information
yumzen authored Nov 13, 2024
2 parents d473212 + f3a3c51 commit 58d9de5
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.gongjakso.server.domain.member.enumerate;

public enum LoginType {
GENERAL, KAKAO , GOOGLE
GENERAL, KAKAO , GOOGLE, NAVER
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
import com.gongjakso.server.global.security.kakao.KakaoClient;
import com.gongjakso.server.global.security.kakao.dto.KakaoProfile;
import com.gongjakso.server.global.security.kakao.dto.KakaoToken;
import com.gongjakso.server.global.security.naver.NaverClient;
import com.gongjakso.server.global.security.naver.dto.NaverProfile;
import com.gongjakso.server.global.security.naver.dto.NaverToken;
import com.gongjakso.server.global.util.redis.RedisClient;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
Expand All @@ -29,6 +32,7 @@ public class AuthService {
private final RedisClient redisClient;
private final TokenProvider tokenProvider;
private final MemberRepository memberRepository;
private final NaverClient naverClient;

@Transactional
public LoginRes signIn(String code, String redirectUri, String type) {
Expand All @@ -39,7 +43,7 @@ public LoginRes signIn(String code, String redirectUri, String type) {
Member member = switch (loginType) {
case KAKAO -> kakaoMember(code, redirectUri);
case GOOGLE -> googleMember(code, redirectUri);

case NAVER -> naverMember(code, redirectUri);
default -> null;
};

Expand Down Expand Up @@ -103,6 +107,30 @@ private Member googleMember(String code, String redirectUri) {
return member;
}

private Member naverMember(String code, String redirectUri){
// 네이버로 액세스 토큰 요청하기
NaverToken naverAccessToken = naverClient.getNaverAccessToken(code, redirectUri);

// 네이버에 있는 사용자 정보 반환
NaverProfile naverProfile = naverClient.getMemberInfo(naverAccessToken);

// 반환된 정보의 이메일 기반으로 사용자 테이블에서 계정 정보 조회 진행
// 이메일 존재 시 로그인 , 존재하지 않을 경우 회원가입 진행
Member member = memberRepository.findMemberByEmailAndDeletedAtIsNull(naverProfile.response().email()).orElse(null);

if(member == null) {
Member newMember = Member.builder()
.email(naverProfile.response().email())
.name(naverProfile.response().name())
.memberType("GENERAL")
.loginType("NAVER")
.build();

return memberRepository.save(newMember);
}
return member;
}

public void signOut(String token, Member member) {
// Validation
String accessToken = token.substring(7);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.gongjakso.server.global.security.naver;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gongjakso.server.global.security.naver.dto.NaverProfile;
import com.gongjakso.server.global.security.naver.dto.NaverToken;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;

@Slf4j
@Component
@RequiredArgsConstructor
public class NaverClient {

@Value("${spring.security.oauth2.client.provider.naver.token-uri}")
private String naverTokenUri;

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

@Value("${spring.security.oauth2.client.registration.naver.authorization-grant-type}")
private String naverGrantType;

@Value("${spring.security.oauth2.client.registration.naver.client-secret}")
private String naverClientSecret;

@Value("${spring.security.oauth2.client.provider.naver.user-info-uri}")
private String naverUserInfoUri;

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

/**
* 네이버 서버에 인가코드 기반으로 사용자의 토큰 정보를 조회하는 메소드
* @param code - 네이버에서 발급해준 인가 코드
* @return - 네이버에서 반환한 응답 토큰 객체
*/
public NaverToken getNaverAccessToken(String code, String redirectUri) {
// 요청 보낼 객체 기본 생성
WebClient webClient = WebClient.create(naverTokenUri);

//요청 본문
MultiValueMap<String , String> params = new LinkedMultiValueMap<>();
params.add("grant_type", naverGrantType);
params.add("client_id", naverClientId);
params.add("redirect_uri", redirectUri);
params.add("code", code);
params.add("client_secret", naverClientSecret);

// 요청 보내기 및 응답 수신
String response = webClient.post()
.uri(naverTokenUri)
.header("Content-type", "application/x-www-form-urlencoded")
.body(BodyInserters.fromFormData(params))
.retrieve() // 데이터 받는 방식, 스프링에서는 exchange는 메모리 누수 가능성 때문에 retrieve 권장
.bodyToMono(String.class) // (Mono는 단일 데이터, Flux는 복수 데이터)
.block();// 비동기 방식의 데이터 수신

// 수신된 응답 Mapping
ObjectMapper objectMapper = new ObjectMapper();
NaverToken naverToken;
try {
naverToken = objectMapper.readValue(response, NaverToken.class);
} catch (Exception e) {
throw new RuntimeException(e);
}

return naverToken;
}

public NaverProfile getMemberInfo(NaverToken naverToken) {
// 요청 기본 객체 생성
WebClient webClient = WebClient.create(naverUserInfoUri);

// 요청 보내서 응답 받기
String response = webClient.post()
.uri(naverUserInfoUri)
.header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
.header("Authorization", "Bearer " + naverToken.access_token())
.retrieve()
.bodyToMono(String.class)
.block();

// 수신된 응답 Mapping
ObjectMapper objectMapper = new ObjectMapper();
NaverProfile naverProfile;
try {
naverProfile = objectMapper.readValue(response, NaverProfile.class);

} catch (Exception e) {
throw new RuntimeException(e);
}

return naverProfile;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.gongjakso.server.global.security.naver;

import lombok.RequiredArgsConstructor;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class NaverUserService extends DefaultOAuth2UserService {

@Override
public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) throws OAuth2AuthenticationException {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.gongjakso.server.global.security.naver.dto;

import lombok.Builder;

@Builder
public record NaverProfile(
String resultcode,
String message,
NaverProfileResponse response
) {
public record NaverProfileResponse(
String id,
String name,
String nickname,
String profile_image,
String email,
String age,
String birthday,
String gender,
String mobile,
String mobile_e164,
String birthyear
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.gongjakso.server.global.security.naver.dto;

import jakarta.validation.constraints.Null;
import lombok.Builder;

@Builder
public record NaverToken(
String access_token,
@Null
String refresh_token,
int expires_in,
String token_type,
String error,
String error_description
) {
}

0 comments on commit 58d9de5

Please sign in to comment.