반응형
  • [Spring Boot] OAuth 2.0 로그인 (구글 로그인)에서 구글 로그인과 회원가입에 대해 정리했었음
  • 구글 뿐 아니라 카카오, 네이버, 페이스북 로그인에 대해서 정리
  • 과정이 거의 비슷하긴 하지만 조금씩 다르기 때문에 한 번씩 정리해 봄

카카오 개발자 페이지에서 애플리케이션 등록

  1. 카카오 개발자 페이지 접속 후 로그인
  2. 내 어플리케이션 클릭
  3. 애플리케이션 추가하기
  4. 앱 아이콘, 앱 이름, 사업자명 작성
  5. 앱 키 -> REST API 키 저장
  6. 보안 -> Client Secret 생성 후 코드도 저장 + 활성화
  7. 카카오 로그인 -> 활성화 설정 둘 다 ON으로 변경 + Redirect URI 설정
  8. 플랫폼 -> Web 플랫폼 등록 -> http://localhost:8080 설정
  9. 동의항목 -> 가져올 정보값 선택 (일부 항목은 검수를 해야 필수 동의 설정 가능)
    • 닉네임, 이메일만 선택하고 진행해 봄

네이버 개발자 센터에서 애플리케이션 등록

  1. 네이버 개발자 센터 접속 후 로그인
  2. Application -> 애플리케이션 등록
  3. 애플리케이션 이름 지정 후 사용 API는 네이버 로그인 선택
  4. 필요한 정보 선택
  5. 로그인 오픈 API 서비스 환경에서 PC 웹 선택 후 URL 지정
  6. Client ID와 Client Secret을 저장

페이스북 API 콘솔에서 애플리케이션 등록

  1. 페이스북 개발자 센터 접속 후 로그인
  2. My Apps -> 앱 만들기
  3. 유형 : 소비자 선택
  4. 앱 이름, 이메일, 비지니스 계정 설정 후 앱 만들기
  5. 방금 생성한 앱 대시보드에 접속 후 앱에 제품 추가하기에서 Facebook 로그인 선택
  6. 플랫폼 : 웹 선택
  7. 사이트 URL에 http://localhost:8080 넣어주고 save
  8. 설정 -> 기본설정에서 앱 ID와 앱 시크릿 코드를 저장

각각의 사이트에서 받아온 값 정리

  • 전에 했던 구글 + 카카오, 네이버, 페이스북 OAuth 로그인 시 받아오는 정보를 getAttributes를 통해 출력해 봄
    • 추가 항목 선택 시 값을 더 받아올 수도 있음
<google>
{
   sub=103058387739722400130, 
   name=안창범, 
   given_name=창범, 
   family_name=안, 
   picture=https://lh3.googleusercontent.com/a/AEdFTp5SiCyTaOLog9sDPN6QhWwsUj7xPbfj4HQF0fdC=s96-c, email=chb20050@gmail.com, 
   email_verified=true, 
   locale=ko
}

<kakao>
{
    id=2632890179, 
    connected_at=2023-01-22T08:17:54Z, 
    properties = {nickname=안창범}, 
    kakao_account = {
        profile_nickname_needs_agreement=false, 
        profile={nickname=안창범}, 
        has_email=true, 
        email_needs_agreement=false, 
        is_email_valid=true, 
        is_email_verified=true, 
        email=chb2005@naver.com
    }
}

<naver>
{
    resultcode=00, 
    message=success, 
    response = {
        id=pvdq1FSG3VZlD7Cp3JuWfAFi-3xir6A-WPlP5f8kXIo, email=chb20050@gmail.com, 
        name=안창범
    }
}

<facebook>
{
    id=5483543425087412, 
    name=안창범, 
    email=chb2005@naver.com
}
  • google과 facebook과는 달리 kakao와 naver에서 받아온 정보는 객체 안에 객체가 있는 형식이라 추출할 때, 다른 처리 방식이 필요함
  • 또한 kakao의 ID는 String 타입이 아닌 Long 타입이기 때문에 추출할 때, 다른 처리 방식이 필요함

받아온 데이터 변환

  • 위에서 확인한 값들을 우리가 만든 User 엔티티에 맞게끔 변환해서 가입을 시켜줘야 함
  • provider = "google", "kakao", "naver", "facebook"
  • providerId = google의 sub값, kakao와 facebook의 id값, naver의 response의 id 값
  • loginId = provider_providerId 로 설정
  • nickname = 각 사이트에 등록한 이름으로 설정
  • email = google과 facebook의 email값, kakao의 kakao_account의 email값, naver의 response의 email값

코드 구현

application.yml

  • 카카오와 네이버는 provider도 작성해 줘야 함
  • 각각의 사이트에서 발급받아 저장해 놓았던 client-id, client-secret 값을 알맞게 넣어주면 됨
spring:
  # OAuth 로그인
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: 발급받은 클라이언트 ID
            client-secret: 발급받은 클라이언트 보안 비밀번호
            scope:
              - email
              - profile

          kakao:
            client-id: 발급받은 REST API 키
            client-secret: 발급받은 Client Secret 코드
            scope:
              - account_email
              - profile_nickname
            authorization-grant-type: authorization_code
            redirect-uri: http://localhost:8080/login/oauth2/code/kakao
            client-name: Kakao
            client-authentication-method: POST

          naver:
            client-id: 발급받은 Client ID
            client-secret: 발급받은 Client Secret
            scope:
              - name
              - email
            client-name: Naver
            authorization-grant-type: authorization_code
            redirect-uri: http://localhost:8080/login/oauth2/code/naver

          facebook:
            client-id: 발급받은 앱 ID
            client-secret: 발급받은 앱 시크릿 코드
            scope:
              - email
              - public_profile

        provider:
          kakao:
            authorization-uri: https://kauth.kakao.com/oauth/authorize
            token-uri: https://kauth.kakao.com/oauth/token
            user-info-uri: https://kapi.kakao.com/v2/user/me
            user-name-attribute: id

          naver:
            authorization-uri: https://nid.naver.com/oauth2.0/authorize
            token-uri: https://nid.naver.com/oauth2.0/token
            user-info-uri: https://openapi.naver.com/v1/nid/me
            user-name-attribute: response

OAuth2UserInfo (interface)

  • 사이트별로 값을 추출하는 방식은 다르지만 추출해야 하는 값은 같기 때문에 통일성을 위해 interface 생성
  • 생성한 interface를 상속받아 사용
public interface OAuth2UserInfo {
    String getProviderId();
    String getProvider();
    String getEmail();
    String getName();
}

GoogleUserInfo

@AllArgsConstructor
public class GoogleUserInfo implements OAuth2UserInfo{

    private Map<String, Object> attributes;

    @Override
    public String getProviderId() {
        return (String) attributes.get("sub");
    }

    @Override
    public String getProvider() {
        return "google";
    }

    @Override
    public String getEmail() {
        return (String) attributes.get("email");
    }

    @Override
    public String getName() {
        return (String) attributes.get("name");
    }
}

KakaoUserInfo

@AllArgsConstructor
public class KakaoUserInfo implements OAuth2UserInfo{

    private Map<String, Object> attributes;

    @Override
    public String getProviderId() {
        // Long 타입이기 때문에 toString으로 변호나
        return attributes.get("id").toString();
    }

    @Override
    public String getProvider() {
        return "kakao";
    }

    @Override
    public String getEmail() {
        // kakao_account라는 Map에서 추출
        return (String) ((Map) attributes.get("kakao_account")).get("email");
    }

    @Override
    public String getName() {
        // kakao_account라는 Map에서 추출
        return (String) ((Map) attributes.get("properties")).get("nickname");
    }
}

NaverUserInfo

@AllArgsConstructor
public class NaverUserInfo implements OAuth2UserInfo{

    private Map<String, Object> attributes;

    @Override
    public String getProviderId() {
        return (String) attributes.get("id");
    }

    @Override
    public String getProvider() {
        return "naver";
    }

    @Override
    public String getEmail() {
        return (String) attributes.get("email");
    }

    @Override
    public String getName() {
        return (String) attributes.get("name");
    }
}

FacebookUserInfo

@AllArgsConstructor
public class FacebookUserInfo implements OAuth2UserInfo{

    private Map<String, Object> attributes;

    @Override
    public String getProviderId() {
        return (String) attributes.get("id");
    }

    @Override
    public String getProvider() {
        return "facebook";
    }

    @Override
    public String getEmail() {
        return (String) attributes.get("email");
    }

    @Override
    public String getName() {
        return (String) attributes.get("name");
    }
}

PrincipalOauth2UserService

  • OAuth2UserRequest에서 provider 추출
  • 추출한 provider에 따라 각각 다른 방식으로 값 추출 후 User Entity로 변환
@Service
@RequiredArgsConstructor
@Slf4j
public class PrincipalOauth2UserService extends DefaultOAuth2UserService {

    private final UserRepository userRepository;
    private final BCryptPasswordEncoder encoder;

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2User oAuth2User = super.loadUser(userRequest);
        log.info("getAttributes : {}", oAuth2User.getAttributes());

        OAuth2UserInfo oAuth2UserInfo = null;

        String provider = userRequest.getClientRegistration().getRegistrationId();

        if(provider.equals("google")) {
            log.info("구글 로그인 요청");
            oAuth2UserInfo = new GoogleUserInfo( oAuth2User.getAttributes() );
        } else if(provider.equals("kakao")) {
            log.info("카카오 로그인 요청");
            oAuth2UserInfo = new KakaoUserInfo( (Map)oAuth2User.getAttributes() );
        } else if(provider.equals("naver")) {
            log.info("네이버 로그인 요청");
            oAuth2UserInfo = new NaverUserInfo( (Map)oAuth2User.getAttributes().get("response") );
        } else if(provider.equals("facebook")) {
            log.info("페이스북 로그인 요청");
            oAuth2UserInfo = new FacebookUserInfo( oAuth2User.getAttributes() );
        }

        String providerId = oAuth2UserInfo.getProviderId();
        String email = oAuth2UserInfo.getEmail();
        String loginId = provider + "_" + providerId;
        String nickname = oAuth2UserInfo.getName();


        Optional<User> optionalUser = userRepository.findByLoginId(loginId);
        User user = null;

        if(optionalUser.isEmpty()) {
            user = User.builder()
                    .loginId(loginId)
                    .nickname(nickname)
                    .provider(provider)
                    .providerId(providerId)
                    .role(UserRole.USER)
                    .build();
            userRepository.save(user);
        } else {
            user = optionalUser.get();
        }

        return new PrincipalDetails(user, oAuth2User.getAttributes());
    }
}

login.html 추가 사항

  • 해당 a 태그 클릭 시 각각의 사이트 로그인 진행
<a href="/oauth2/authorization/google">구글 로그인</a>
<a href="/oauth2/authorization/kakao">카카오 로그인</a>
<a href="/oauth2/authorization/naver">네이버 로그인</a>
<a href="/oauth2/authorization/facebook">페이스북 로그인</a>

결과

  • 4번 User : 직접 회원가입한 유저
  • 5번 User : 구글 로그인으로 가입한 유저
  • 6번 User : 카카오 로그인으로 가입한 유저
  • 7번 User : 네이버 로그인으로 가입한 유저
  • 8번 User : 페이스북 로그인으로 가입한 유저
반응형

↓ 클릭시 이동

복사했습니다!