본문 바로가기
Spring

[Spring] REST 방식으로 네이버 로그인 구현하기

by 태진아밴드 2020. 10. 7.

카카오 로그인에 이어서 이번엔 네이버 로그인을 구현해보자.

 

일단 네이버 개발자 센터로 들어가서 애플리케이션 등록을 해주자.

 

developers.naver.com/

 

NAVER Developers

네이버 오픈 API들을 활용해 개발자들이 다양한 애플리케이션을 개발할 수 있도록 API 가이드와 SDK를 제공합니다. 제공중인 오픈 API에는 네이버 로그인, 검색, 단축URL, 캡차를 비롯 기계번역, 음��

developers.naver.com

 

애플리케이션 등록에서 사용 API는 네아로(네이버 아이디로 로그인)을 선택해주면 된다.

 

그 다음 서비스 URL, Callback URL을 설정해주면 되는데,

 

로컬 테스트 이후엔 실제 서비스 URL로 변경해주어야 동작하므로 주의해야 한다.

 

등록을 완료하고 나면 Client ID와 Client Secret이 발급되는데 카카오 로그인할때와 마찬가지로

 

Access Token을 발급받기 위한 키이므로 따로 보관해두자.

 

이제 프로젝트에 적용을 하기위해서 먼저 pom.xml에 라이브러리를 하나 추가해주자

* pom.xml

<!-- naver -->
<dependency>
	<groupId>com.github.scribejava</groupId>
	<artifactId>scribejava-core</artifactId>
	<version>2.8.1</version>
</dependency>

추가가 완료되었다면 로그인용 구현체와 URL을 만들어주는 소스를 작성하자.

*java

NaverLoginApi

public class NaverLoginApi extends DefaultApi20 {
     protected NaverLoginApi(){
    }

    private static class InstanceHolder{
        private static final NaverLoginApi INSTANCE = new NaverLoginApi();
    }


    public static NaverLoginApi instance(){
        return InstanceHolder.INSTANCE;
    }

    @Override
    public String getAccessTokenEndpoint() {
        return "https://nid.naver.com/oauth2.0/token?grant_type=authorization_code";
    }

    @Override
    protected String getAuthorizationBaseUrl() {
        return "https://nid.naver.com/oauth2.0/authorize";
    }
}

 

NaverLoginBO

public class NaverLoginBO {

    // 발급받은 client id;
    @Value("#{config['naver.client.id']}")
    private String CLIENT_ID;

    // 발급받은 client Secret
    @Value("#{config['naver.client.id']}")
    private String CLIENT_SECRET;

    // 로그인성공시 리턴될 URL
    @Value("#{config['naver.redirect.url']}")
    private String REDIRECT_URI;
    
    // 세션 유효값검증용 체크용
    private String SESSION_STATE = "oauth_state"; 

    // 프로필 조회 API URL
    private final static String PROFILE_API_URL = "https://openapi.naver.com/v1/nid/me";

    public String getAuthorizationUrl(HttpSession session) {
        // 세션 유효성 검증을 위하여 난수를 생성
        String state = generateRandomString();
        // 생성한 난수 값을 session에 저장
        setSession(session, state);

        // Scribe에서 제공하는 인증 URL 생성 기능을 이용하여 네아로 인증 URL 생성
        OAuth20Service oauthService = new ServiceBuilder()
                .apiKey(CLIENT_ID)
                .apiSecret(CLIENT_SECRET)
                .callback(REDIRECT_URI)
                .state(state) //앞서 생성한 난수값을 인증 URL생성시 사용함
                .build(NaverLoginApi.instance());
        return oauthService.getAuthorizationUrl();
    }

    public OAuth2AccessToken getAccessToken(HttpSession session, String code, String state) throws IOException {

        // Callback으로 전달받은 세선검증용 난수값과 세션에 저장되어있는 값이 일치하는지 확인
        String sessionState = getSession(session);
        if (StringUtils.pathEquals(sessionState, state)) {

            OAuth20Service oauthService = new ServiceBuilder()
                    .apiKey(CLIENT_ID)
                    .apiSecret(CLIENT_SECRET)
                    .callback(REDIRECT_URI)
                    .state(state)
                    .build(NaverLoginApi.instance());

            // Scribe에서 제공하는 AccessToken 획득 기능으로 네아로 Access Token을 획득
            OAuth2AccessToken accessToken = oauthService.getAccessToken(code);
            return accessToken;
        }
        return null;
    }

    // 세션 유효성 검증을 위한 난수 생성기
    private String generateRandomString() {
        return UUID.randomUUID().toString();
    }

    // http session에 데이터 저장
    private void setSession(HttpSession session, String state) {
        session.setAttribute(SESSION_STATE, state);
    }

    // http session에서 데이터 가져오기
    private String getSession(HttpSession session) {
        return (String) session.getAttribute(SESSION_STATE);
    }

    // Access Token을 이용하여 네이버 사용자 프로필 API를 호출
    public String getUserProfile(HttpSession session, OAuth2AccessToken oauthToken) throws IOException {

        OAuth20Service oauthService = new ServiceBuilder()
                .apiKey(CLIENT_ID)
                .apiSecret(CLIENT_SECRET)
                .callback(REDIRECT_URI).build(NaverLoginApi.instance());

        OAuthRequest request = new OAuthRequest(Verb.GET, PROFILE_API_URL, oauthService);
        oauthService.signRequest(oauthToken, request);
        Response response = request.send();
        return response.getBody();
    }
}

카카오 로그인과 마찬가지로 properties에 별도로 변수로 잡아둔 CLIENT_ID, CLIENT_SECRET, REDIRECT_URI를 넣어서 url을 리턴해주는 형태이다.

 

인증 전 후 세션 유효성 체크를 위한 난수 생성부분도 들어있다.

 

servlet-contex.xml

<beans:bean id="naverLoginBO" class="test.domain.NaverLoginBO" />

다 만들었다면 naverLoginBO라는 id로 사용하기위해 bean 등록을 해준다.

 

이제 해당 구현체를 토대로 컨트롤러 부분을 작성해주자.

 

getNaverAuthUrl

// 네이버 로그인창 호출
@RequestMapping(value = "/getNaverAuthUrl")
public @ResponseBody String getNaverAuthUrl(HttpSession session) throws Exception {
    String reqUrl = naverLoginBO.getAuthorizationUrl(session);
    return reqUrl;
}

 

카카오 로그인과  마찬가지로 스크립트 단에서 컨트롤러 부분을 호출해주자.

* javascript

// 네이버 로그인 버튼 클릭
function loginWithNaver() {
    $.ajax({
        url: '/login/getNaverAuthUrl',
        type: 'get',
    }).done(function (res) {
        location.href = res;
    });
}

 

여기까지 하면 로그인 팝업이 뜨게된다. 이제 마지막으로 로그인 이후 과정을 살펴보자.

 

// 네이버 연동정보 조회
@RequestMapping(value = "/login/oauth_naver")
public String oauthNaver(HttpServletRequest request, HttpServletResponse response) throws Exception {

    JSONParser parser = new JSONParser();
    Gson gson = new Gson();

    HttpSession session = request.getSession();
    String code = request.getParameter("code");
    String state = request.getParameter("state");
    String error = request.getParameter("error");

    // 로그인 팝업창에서 취소버튼 눌렀을경우
    if ( error != null ){
        if(error.equals("access_denied")){
            return "redirect:/login";
        }
    }

    OAuth2AccessToken oauthToken;
    oauthToken = naverLoginBO.getAccessToken(session, code, state);
    //로그인 사용자 정보를 읽어온다.
    String loginInfo = naverLoginBO.getUserProfile(session, oauthToken);

    // JSON 형태로 변환
    Object obj = parser.parse(loginInfo);
    JSONObject jsonObj = JSONObject.fromObject(gson.toJson(obj));
    JSONObject callbackResponse = (JSONObject) jsonObj.get("response");
    String naverUniqueNo = callbackResponse.get("id").toString();

    if (naverUniqueNo != null && !naverUniqueNo.equals("")) {

        /** 
        
            TO DO : 리턴받은 naverUniqueNo 해당하는 회원정보 조회 후 로그인 처리 후 메인으로 이동
        
        */

    // 네이버 정보조회 실패
    } else {
        throw new ErrorMessage("네이버 정보조회에 실패했습니다.");
    }

}

네이버 로그인도 Access Token을 통해 사용자 정보를 가져 온 뒤 naverUniqueNo로 회원을 조회하면 되겠다.

 

이렇게 카카오 로그인과 네이버 아이디로 로그인 개발을 마쳤다..!🤚

 

이제 실서비스에 반영하면되는데 문제가 생겼다.

 

서비스 하는페이지가 웹이아닌 앱으로 감싸서 웹뷰로 사용할 페이지였는데 IOS가 문제였다..!

 

애플 정책이 올해부로 바뀌어서 sns로그인 기능이 있는 경우 apple 로그인이 같이 있어야지 심사에 통과된다고 IOS앱은 리젝당했다..!😂

 

서비스오픈 예정일도 빠듯하고 미뤄진 다른일들이 많아서 결국은 sns 로그인 부분은 주석처리하고 서비스를 올리기로했다..

 

아마 이후에 apple 로그인과 google 로그인 부분까지 붙이고 나서 실서비스에 적용이 될거 같다.

 

만들고나면 그 친구들도 포스팅해야겠다!