개발공부/spring security & JWT 강의

[11강] 네이버 로그인 완료

기억지기 개발자 2023. 11. 14. 13:52

🤍[네이버 개발자 사이트]에 들어가서 다음과 같이 진행하여 id와 비밀번호를 발급받아 준다.

 

 

🤍앞서 진행한 것과 마찬가지로 해당 값을 각각 코드에 넣어준다.

# naver OAuth2 설정
spring.security.oauth2.client.registration.naver.client-id=vFzQdx0GIL_gWvfx9i1k
spring.security.oauth2.client.registration.naver.client-secret=[비밀번호 값]
spring.security.oauth2.client.registration.naver.scope=email,profile
spring.security.oauth2.client.registration.naver.client-name=Naver
spring.security.oauth2.client.registration.naver.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.naver.redirect-uri=http://localhost:8080/login/oauth2/code/naver

 

 

❌❌그러고 나서 실행시키면 오류 발생!!!❌❌

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Unsatisfied dependency expressed through method 'setFilterChains' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'filterChain' defined in class path resource [com/example/reuseit_project/config/SecurityConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'filterChain' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'clientRegistrationRepository' defined in class path resource [org/springframework/boot/autoconfigure/security/oauth2/client/servlet/OAuth2ClientRegistrationRepositoryConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository]: Factory method 'clientRegistrationRepository' threw exception; nested exception is java.lang.IllegalStateException: Provider ID must be specified for client registration 'naver' at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.resolveMethodArguments(AutowiredAnnotationBeanPostProcessor.java:768) ~[spring-beans-5.3.26.jar:5.3.26] a

원인은 다음과 같다. 

네이버와 같은 OAuth2 공급자는 일반적으로 Spring Security OAuth2에서 기본으로 제공되지 않습니다.

따라서 Spring Security OAuth2는 기본적으로 Facebook, Twitter, Google과 같은 일부 OAuth2 공급자에 대한 지원을 제공하며, 다른 공급자(예: 네이버, 카카오)에 대한 설정은 애플리케이션 개발자가 직접 해주어야 합니다.

 

🤍위에서 발생한 오류를 naver를 provider로 등록을 하며 해결하는 과정을 진행한다.

# 네이버 OAuth2 공급자의 인증 및 토큰 URI 설정
spring.security.oauth2.client.provider.naver.authorization-uri=https://nid.naver.com/oauth2.0/authorize
spring.security.oauth2.client.provider.naver.token-uri=https://nid.naver.com/oauth2.0/token
spring.security.oauth2.client.provider.naver.user-info-uri=https://openapi.naver.com/v1/nid/me
spring.security.oauth2.client.provider.naver.user-name-attribute=response

 

<각 코드의 역할과 필요성에 대해서 설명.. >

spring.security.oauth2.client.provider.naver.authorization-uri=https://nid.naver.com/oauth2.0/authorize

  • 사용자 인증 및 권한 부여: OAuth2 프로토콜에서 사용자가 클라이언트 애플리케이션(여기서는 Spring 애플리케이션)에 대한 권한을 부여하려면 공급자의 인가 엔드포인트로 이동합니다. 이 엔드포인트는 사용자를 공급자의 로그인 페이지로 리디렉션하고, 사용자가 로그인한 후 권한을 부여하도록 요청합니다.
  • 액세스 토큰 획득: 사용자가 권한을 부여하면 공급자는 클라이언트 애플리케이션에 액세스 토큰을 제공합니다. 이 액세스 토큰은 클라이언트가 사용자의 데이터에 접근할 수 있는 권한을 나타내며, 이 토큰을 사용하여 API 엔드포인트에 요청을 보낼 수 있습니다.

네이버 - 개발 가이드에서 확인할 수 있다.

 

 

spring.security.oauth2.client.provider.naver.token-uri=https://nid.naver.com/oauth2.0/token

  • 액세스 토큰 요청: OAuth2 프로토콜에서 클라이언트 애플리케이션이 사용자의 데이터에 접근하기 위해 액세스 토큰이 필요합니다. 클라이언트 애플리케이션은 이 액세스 토큰을 요청하기 위해 공급자의 토큰 엔드포인트로 POST 요청을 보냅니다.
  • 토큰 엔드포인트: token-uri는 공급자(Naver)에서 액세스 토큰을 발급하고 관리하는 엔드포인트(URI)를 가리킵니다. 클라이언트 애플리케이션은 이 엔드포인트로 사용자 인증 정보와 권한 코드(또는 리프레시 토큰)를 제출하여 액세스 토큰을 요청하며, 공급자는 유효한 경우 액세스 토큰을 발급합니다.
  • 액세스 토큰 사용: 발급된 액세스 토큰은 클라이언트 애플리케이션이 공급자의 API 엔드포인트로 요청을 보내고 사용자의 데이터에 접근하는 데 사용됩니다. 이 토큰은 클라이언트 애플리케이션과 공급자 간에 사용자 데이터의 보안 접근을 보장하며, 클라이언트가 공급자의 서비스를 대신 사용할 수 있도록 허용합니다.

네이버 - 개발 가이드에서 직접 확인할 수 있다.

 


spring.security.oauth2.client.provider.naver.user-info-uri=https://openapi.naver.com/v1/nid/me

  • 사용자 정보 요청: OAuth2 프로토콜에서 클라이언트 애플리케이션이 액세스 토큰을 획득하면, 이 토큰을 사용하여 사용자 정보를 가져올 수 있습니다. 사용자 정보에는 사용자의 프로필 정보 및 기타 관련 정보가 포함됩니다.
  • 사용자 정보 엔드포인트: user-info-uri는 공급자(Naver)에서 사용자 정보를 제공하는 엔드포인트(URI)를 가리킵니다. 클라이언트 애플리케이션은 이 엔드포인트로 액세스 토큰을 사용하여 사용자 정보를 요청하고, 공급자는 해당 사용자에 대한 정보를 제공합니다.
  • 사용자 정보 활용: 사용자 정보는 주로 애플리케이션에서 사용자를 식별하거나 사용자에 맞게 맞춤화된 서비스를 제공하기 위해 사용됩니다. 이 정보는 사용자의 프로필, 이메일 주소, 닉네임, 프로필 사진 및 기타 필요한 정보를 포함할 수 있으며, 클라이언트 애플리케이션은 이 정보를 활용하여 사용자 경험을 향상합니다.

네이버 - 개발 가이드에서 직접 확인할 수 있다.

 

 

spring.security.oauth2.client.provider.naver.user-name-attribute=response 

  • OAuth2 클라이언트가 Naver로부터 사용자 정보를 가져올 때, 사용자의 이름을 어떤 속성으로 식별할지를 지정하는 것입니다.
  • 그래서 response가 아닌 goodResponse로 바꾸고 싶다면 바꿀 수 있습니다.

 


⭐아래의 링크들을 클릭하게 되면 어떻게 작동하는 것인지 원리 설명...

지금까지는 그냥 저 링크를 누르면 spring security 측에서 알아서 내부적으로 어떻게든(?) 작동할 것이라고 생각했는데 그게 아니었다!!

 

/oauth2/authorization 부분은 OAuth 2.0 인증 프로세스를 시작하는 엔드포인트를 가리킵니다. 

이 엔드포인트를 통해 사용자는 클라이언트 애플리케이션과 OAuth 2.0 프로바이더(또는 인증 공급자) 간의 권한 부여 프로세스를 시작할 수 있다!! 

 

일반적으로 /oauth2/authorization/provider 형식의 URL을 사용하여 특정 OAuth 프로바이더에 대한 권한 부여를 시작합니다. 여기서 "provider"는 OAuth 인증 서비스(예: 네이버)를 가리키며, 해당 프로바이더의 권한 부여 프로세스가 시작된다.

 

[네이버 로그인]을 누르면 https://nid.naver.com/oauth2.0/authorize 이 사용자가 직접 지정한 (네이버에서 지정해 준) 주소로 이동하게 된다. 반면에 페이스북, 구글은 리다이렉션 주소가 OAuth 라이브러리에 미리 지정이 되어있기 때문에 작성할 필요가 없다.

 

🤍네이버에 관련된 코드도  service에 추가 & 클래스 생성

// 네이버에서 받아온 데이터를 처리해줄 클래스를 새로 하나 정의
public class NaverUserInfo implements OAuth2UserInfo{
    private Map<String, Object> attributes;

    public NaverUserInfo(Map<String, Object> attributes) {
        this.attributes = attributes;
    }

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

    @Override
    public String getProvider() {
        return "naver"; // 원래 Provider이라는 필드에는 로르인하는 주체, 대상이 오는 거임.
    }

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

    @Override
    public String getName() {
        return (String) attributes.get("name");
    }
}
OAuth2UserInfo oAuth2UserInfo = null;
        if(userRequest.getClientRegistration().getClientId().equals("google")) {
            System.out.println("--- 구글 로그인 요청 ---");
            oAuth2UserInfo = new GoogleUserInfo(oAuth2User.getAttributes());
        } else if(userRequest.getClientRegistration().getClientId().equals("facebook")) {
            System.out.println("--- 페이스북 로그인 요청 ---");
            oAuth2UserInfo = new GoogleUserInfo(oAuth2User.getAttributes());
        } 
        ////  바로 아래 부분을 새로 추가
        else if(userRequest.getClientRegistration().getClientId().equals("naver")) {
            System.out.println("--- 네이버 로그인 요청 ---");
            oAuth2UserInfo = new NaverUserInfo((Map)oAuth2User.getAttributes().get("response"));
        } else {
            System.out.println("우리는 구글, 페이스북, 네이버만 지원합니다.");
        }

그런데 받은 데이터를 해당 클래스로 전달하는 코드가 구글, 페이스북과 네이버는 살짝 다른데 그 이유를 설명하자면...

 

oAuth2UserInfo = new NaverUserInfo((Map)oAuth2User.getAttributes().get("response"));

getAttribute(response):{resultcode==00, message=success, response={id=1235344, email=사용자 이메일, name=사용자 이름}}
  • 위처럼 네이버는 바로 getAttribute 뒤에 바로 데이터들이 넘어오는 것이 아니라 중간에 보면 response라는 객체로 한 번 더 감싸져서 데이터가 넘어온다. 그래서 .get("response")라는 코드를 추가해서 response라는 이름을 가진 객체를 꺼내서 전달해야 하기 때문에 저렇게 코드가 작성된 것이다. 

 


결과

성공 화면~~
코드를 작성한 대로 잘 DB에 회원 정보가 저장되었음.