우당탕탕

[앱출시 - 2] REST API 소셜로그인 (네이버,카카오,구글,페이스북) - 네이버 본문

앱출시관련

[앱출시 - 2] REST API 소셜로그인 (네이버,카카오,구글,페이스북) - 네이버

모찌모찝 2022. 8. 26. 07:00

오늘은 이전에 작성한 REST API 소셜 로그인 2편-네이버를 작성한다.
카카오 버전[앱출시 - 1] REST API 소셜로그인 (네이버,카카오,구글,페이스북) - 카카오 를 참고하면 된다.

개발 ( Springboot )

일단 개발에 앞서 네이버의 경우에도 카카오와 같이 개발자 사이트에 접속해서 개발 준비를 진행해야 한다.
네이버 개발자 -https://developers.naver.com/

네이버 개발자 사이트에 접속하면 상단에 Application을 클릭 후 애플리케이션 등록을 눌러 새로운 애플리케이션을 등록해준다. 

새 애플리케이션 등록

앱의 이름을 적고 사용 API로는 네아로(네이버 아이디로 로그인)를 선택한다.
정보제공의 경우 운영서버 심사 시에 실제 사용하는 내용만 넣어야 하므로 적당히 필요한 값만 선택해야 한다.

산타의 경우 아래의 서비스 환경은 Mobile웹으로 개발을 진행하였다. 
카카오 버전과 독같이 Callback URL을 적어줘야 하는데 나의 경우에는 EC2서버 URL과 로컬 URL을 작성하여 진행했다. 
네이버 콜백 URL의 경우 카카오와 비슷한 스타일로 로컬 기준 ex) http://localhost:8080/auth/naver/callback 으로 작성하였다. 

오른쪽의 + 버튼을 눌러야 적용된다

 

내 애플리케이션을 생성 완료하면 앱 개요 화면에서 애플리케이션 정보Client ID와 Client Secret정보가 있다.
카카오 로그인과 동일하게 Client ID와 Client Secret키의 경우 프로퍼티에 저장시키고 불러와서 사용했다.

순서

1. 네이버 로그인을 클릭 시 아래의 URL로 접속하게 만들면 설정한 redirect_url로 네이버 측에서 code값과 함께 리턴해준다.
2. code값을 가지고 access_token을 받는다.
3. 해당 access_token으로 네이버 회원정보 API를 요청하여 토큰 유저 정보를 받아온다.

https://nid.naver.com/oauth2.0/authorize?client_id=[네이버 클라이언트 ID]&response_type=code&redirect_uri=[설정 Redirect URL]&state=[난수 값]


네이버의 경우 난수 값을 추가할 수 있는데 예시로 이런 식으로 함수를 작성하여 불러오면 된다.

    public String generateState()
    {
        SecureRandom random = new SecureRandom();
        return new BigInteger(130, random).toString(32);
    }


카카오 로그인 구현과 동일하게 JSONObject를 사용하여 build.gradle에 아래를 추가한다.

implementation 'org.json:json:20171018'


이제 회원가입 및 로그인 컨트롤러를 살펴보겠다.  (전체 코드는 아래 첨부하겠다)
처음으로 access_token과 refresh_token을 받는 API이다. 

@RequestMapping(value = "auth/naver/callback")
    public String NaverLogin(@RequestParam("code") String code, @RequestParam("state") String state, HttpServletRequest request, Model model){
        RestTemplate rt = new RestTemplate();
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

        MultiValueMap<String,String> params = new LinkedMultiValueMap<>();
        params.add("client_id", naver_client);
        params.add("client_secret", naver_secret);
        params.add("grant_type", "authorization_code");
        params.add("state", state);  // state 일치를 확인
        params.add("code", code);

        HttpEntity<MultiValueMap<String,String>> naverTokenRequest = new HttpEntity<>(params,httpHeaders);

        ResponseEntity<String> response = rt.exchange(
                "https://nid.naver.com/oauth2.0/token",
                HttpMethod.POST,
                naverTokenRequest,
                String.class
        );

        // 토큰값 Json 형식으로 가져오기위해 생성
        JSONObject jo = new JSONObject(response.getBody());

        // 토큰결과값
        log.debug("naver Id token result = {} " ,  response);
        
    	return "";
    }

카카오와 다른 점으로 state가 추가되었고, naver_client에는 Client ID, naver_secret에는 Client secret 값이 들어가 있다.

컨트롤러 클래스 상단의 @Value 어노테이션으로 프로퍼티에 담겨있는 데이터를 불러온다.

    @Value("${naver.client}")
    private String naver_client;
    
    @Value("${naver.secret}")
    private String naver_secret;


상단의 코드로 네이버에 토큰을 요청하면 동일하게 access_token과 refresh_token이 반환되는데, 해당 토큰으로 아래 코드와 같이 회원을 조회 시킨다.

RestTemplate rt2 = new RestTemplate();
HttpHeaders headers2 = new HttpHeaders();

headers2.add("Authorization", "Bearer "+ jo.get("access_token"));
headers2.add("Content-type","application/x-www-form-urlencoded;charset=utf-8");

HttpEntity<MultiValueMap<String,String >> naverProfileRequest2= new HttpEntity<>(headers2);

ResponseEntity<String> response2 = rt2.exchange(
        "https://openapi.naver.com/v1/nid/me",
        HttpMethod.POST,
        naverProfileRequest2,
        String.class
);

// 토큰을 사용하여 사용자 정보 추출
JSONObject jo2 = new JSONObject(response2.getBody());

log.debug("##### naver login = {}" , jo2);

조회시 jo2에는 사용자 정보가 담겨 나온다. 이후 로그인 및 회원가입 로직을 작성하면 소셜 로그인 같은 경우는 끝이 난다.
예시로 소셜 로그인을 카카오와 네이버를 사용한다면, 해당 데이터를 비교해서 필수 값을 정하여 개발을 진행하면 좋을 것 같다. 

구글과 페이스북의 경우도 위와 동일하게 작성이 가능한데 해당 내용은 중복되는 내용이 많아 비슷한 코드가 존재하는 깃허브 주소를 첨부한다.
https://github.com/Mozzirt/E-Mozzi/blob/main/backend/src/main/java/game/mozzi/controller/LoginController.java

아래에는 전체 코드를 첨부한다.

@RequestMapping(value = "auth/naver/callback")
    public String NaverLogin(@RequestParam("code") String code, @RequestParam("state") String state, HttpServletRequest request, Model model){

        RestTemplate rt = new RestTemplate();
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

        MultiValueMap<String,String> params = new LinkedMultiValueMap<>();
        params.add("client_id", naver_client);
        params.add("client_secret", naver_secret);
        params.add("grant_type", "authorization_code");
        params.add("state", state);  // state 일치를 확인
        params.add("code", code);

        HttpEntity<MultiValueMap<String,String>> kakaoTokenRequest = new HttpEntity<>(params,httpHeaders);

        ResponseEntity<String> response = rt.exchange(
                "https://nid.naver.com/oauth2.0/token",
                HttpMethod.POST,
                kakaoTokenRequest,
                String.class
        );

        // 토큰값 Json 형식으로 가져오기위해 생성
        JSONObject jo = new JSONObject(response.getBody());

        // 토큰결과값
        log.debug("naver Id token result = {} " ,  response);


        RestTemplate rt2 = new RestTemplate();
        HttpHeaders headers2 = new HttpHeaders();

        headers2.add("Authorization", "Bearer "+ jo.get("access_token"));
        headers2.add("Content-type","application/x-www-form-urlencoded;charset=utf-8");

        HttpEntity<MultiValueMap<String,String >> kakaoProfileRequest2= new HttpEntity<>(headers2);

        ResponseEntity<String> response2 = rt2.exchange(
                "https://openapi.naver.com/v1/nid/me",
                HttpMethod.POST,
                kakaoProfileRequest2,
                String.class
        );

        // 토큰을 사용하여 사용자 정보 추출
        JSONObject jo2 = new JSONObject(response2.getBody());

        log.debug("##### naver login = {}" , jo2);
	
    	// todo 로그인 및 회원가입 
    	return ""; 
    }

 

다음 포스팅에는 필터를 사용하여 로그인 및 로그를 찍는 작업에 대해 작성하도록 하겠다.
필터나 인터셉터에 대해 모르거나 이해가 가지 않을 경우 [Spring] 필터(Filter)와 인터셉터(Interceptor) 차이를 보고 오면 도움이 될 것 같다.

Comments