본문 바로가기
기술/Spring-Boot

[SpringBoot/OAuth2] WebSecurity 없이 REST API 환경에서 OAuth2 인증 구현하기 - 2. 네이버 로그인

by Zabee52 2022. 1. 2.

OAuth2

 

 

[SpringBoot/OAuth2] WebSecurity 없이 REST API 환경에서 OAuth2 인증 구현하기 - 1. 카카오 로그인

OAuth2 참고로 이건 내가 WebSecurity를 쓰기 싫어서 이러는게 아니라 쓸 줄을 몰라서 방법을 찾은 것이다..... WebSecurity 쓰는게 더 편해보이니 아는사람은 순순히 쓰도록 하는것이 좋을지도 모르고 아

dazbee.tistory.com

이전 시간을 통해 카카오 로그인의 흐름과 그 코드를 알아봤다. 그러면 이 이해를 바탕으로 횡이동 하면 된다. 어떻게? 비교하면서.

근데 몰?루 이거 좀 귀엽다. 그래서 한 번 더 써봤다.

 

어차피 OAuth2는 표준 프로토콜이다. 구현 방식은 크게 다르지 않을 것이다. 비교하면서 가자. 이번엔 코드스니펫이 아닌 직접 작성하는 코드기 때문에 완성본 예시는 없다. 그럼지금바로당장출발렛츠고

 

네이버 공식문서 링크는 여기 있다.

 

네이버 로그인 개발가이드 - LOGIN

네이버 로그인 개발가이드 1. 개요 4,200만 네이버 회원을 여러분의 사용자로! 네이버 회원이라면, 여러분의 사이트를 간편하게 이용할 수 있습니다. 전 국민 모두가 가지고 있는 네이버 아이디

developers.naver.com

 

1. 인가 코드로 액세스 요청

전체적인 코드의 흐름은 비슷할 것으로 생각된다. 대신 파라미터들은 다를테니, 이번엔 네이버 공식문서 친구와 함께 해보겠다.

먼저 필요한 것은 body에 필요한 정보들이다. 찾아오자.

 

 

네이버 로그인 개발가이드 - LOGIN

네이버 로그인 개발가이드 1. 개요 4,200만 네이버 회원을 여러분의 사용자로! 네이버 회원이라면, 여러분의 사이트를 간편하게 이용할 수 있습니다. 전 국민 모두가 가지고 있는 네이버 아이디

developers.naver.com

필요한 정보는 이 파트에 있다.

오.....

여기는 어디로 요청해야 하는지 최상단에 명시해주네.. 이전 카카오 문서에서 아쉬웠던 점이 잘 충족이 된 것 같다. 그러면 이걸 보고 이제 코드를 써보자. 참고로 나는 기존 코드스니펫 방식을 따라 POST 방식으로 요청했지만, GET방식도 가능하다.

 

카카오와 다른 점은, client_secret이 선택사항이었던 카카오와는 달리 네이버는 반드시 넣어줘야 한다는 점이다. 이 점 유의하자.

 

아.. 근데 반드시 필요한 정보 중 state에 대한 부분. 이게 뭘까. 나는 지금 이 정보는 제공받지 못 했는데. 흠.....

공식문서를 조금 더 찾아보자.

 

 

 

 

네이버 로그인 개발가이드 - LOGIN

네이버 로그인 개발가이드 1. 개요 4,200만 네이버 회원을 여러분의 사용자로! 네이버 회원이라면, 여러분의 사이트를 간편하게 이용할 수 있습니다. 전 국민 모두가 가지고 있는 네이버 아이디

developers.naver.com

찾아보니, 애초에 프론트엔드에서 요청을 할 때부터 state 값을 파라미터로 작성하게 되어있었다. 맥락을 보니 임의의 값을 넣으면 되는 것 같다. 일단 아무 값이나 넣어서 인증을 시도해봐야겠다.

 

프론트엔드 단에서 인증을 시도할 때 사용한 URL은 다음과 같다.

https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id={YOUR_CLIENT_ID}&state=randomtext&redirect_uri={YOUR_CALLBACK_URL}

 

 

// HTTP Header 생성
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

        // HTTP Body 생성
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "authorization_code");
        body.add("client_id", clientId);
        body.add("client_secret", secretKey);
        body.add("code", code);
        body.add("state", state);

        // HTTP 요청 보내기
        HttpEntity<MultiValueMap<String, String>> naverTokenRequest =
                new HttpEntity<>(body, headers);
        RestTemplate rt = new RestTemplate();
        ResponseEntity<String> response = rt.exchange(
                "https://nid.naver.com/oauth2.0/token",
                HttpMethod.POST,
                naverTokenRequest,
                String.class
        );

 

그렇게 받아온 state값까지 포함해서 파라미터에 넣고 요청까지 보내줬다. 그러면 이제 응답 받아올 시간.

 

응답 정보 역시 친절하게 잘 나와있다.

내가 찾던 정보 여기있네요!

코드 나와라 뿅.

// HTTP 응답 (JSON) -> 액세스 토큰 파싱
String responseBody = response.getBody();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseBody);
return jsonNode.get("access_token").asText();

 

이렇게 해서 만들어진 메소드 완성본은 다음과 같다.

private String getAccessToken(String code, String state) throws JsonProcessingException {
        // HTTP Header 생성
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

        // HTTP Body 생성
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "authorization_code");
        body.add("client_id", clientId);
        body.add("client_secret", secretKey);
        body.add("code", code);
        body.add("state", state);

        // HTTP 요청 보내기
        HttpEntity<MultiValueMap<String, String>> naverTokenRequest =
                new HttpEntity<>(body, headers);
        RestTemplate rt = new RestTemplate();
        ResponseEntity<String> response = rt.exchange(
                "https://nid.naver.com/oauth2.0/token",
                HttpMethod.POST,
                naverTokenRequest,
                String.class
        );

        // HTTP 응답 (JSON) -> 액세스 토큰 파싱
        String responseBody = response.getBody();
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode jsonNode = objectMapper.readTree(responseBody);
        return jsonNode.get("access_token").asText();
    }

 

START: execution(ResponseEntity com.teamproj.backend.service.NaverUserService.naverLogin(String,String))

AAAAOjsnCUOZnyn6yRdazArijMkFkQ5PLT_OrdzYRBoxCqQMN3iLH-jUwE9jlf1QJKT-hXeaBmt8tb0yJkWw8uH4TdI

END: execution(ResponseEntity com.teamproj.backend.service.NaverUserService.naverLogin(String,String)) 340ms

sout 찍어보니 코드가 잘 나오는 모습. 옼ㅋㅋㅋㅋㅋ 나는 틀리지 않았다.

근데 여기서 느낀 점은, state 값이 변하지 않으면 이 코드도 언제나 동일하게 나온다는 점이다. 이런 점을 고려해서 state는 난수를 사용하는 것이 좋을 것 같다는 생각이 들었다.

 

 

2. 액세스 토큰으로 사용자 정보 가져오기

이 부분은 생각해보면 양식은 크게 다르지 않을 것 같다. 왜냐면 OAuth2는 표준이니까. 그래도 한 번 문서를 확인해본다.

 

 

네이버 로그인 개발가이드 - LOGIN

네이버 로그인 개발가이드 1. 개요 4,200만 네이버 회원을 여러분의 사용자로! 네이버 회원이라면, 여러분의 사이트를 간편하게 이용할 수 있습니다. 전 국민 모두가 가지고 있는 네이버 아이디

developers.naver.com

흐름이 점점 손에 잡혀가기 시작한다

이봐 OAuth2 ww 오마에 이렇게 응용하기 편해도 되는거냐구?(草)

 

 주소도 이렇게 잘 알려줬겠다. 이걸 그대로 코드에 붙이기만 하면 될 것이다.

// HTTP Header 생성
        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "Bearer " + accessToken);
        headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

        // HTTP 요청 보내기
        HttpEntity<MultiValueMap<String, String>> naverUserInfoRequest = new HttpEntity<>(headers);
        RestTemplate rt = new RestTemplate();
        ResponseEntity<String> response = rt.exchange(
                "https://openapi.naver.com/v1/nid/me",
                HttpMethod.POST,
                naverUserInfoRequest,
                String.class
        );

 

이번엔 필요한 정보값을 가져와야 한다. 나에게 필요한 정보는 현재 

 

1. 사용자의 고유 정보를 인식하기 위한 id값

2. 사용자의 닉네임

3. 사용자의 프로필사진

 

세 가지이다. id값을 가져오는 이유는 DB에 정보를 저장할 때 회원 정보가 이미 존재하는지 알아내고, 존재한다면 로그인처리, 없다면 회원가입 처리를 하기 위해서이다. 그러면 이젠 정보를 어떻게 가져오는지 또 살펴보자.

잉?

 

이렇게 보니 양식이 어떻게 되는 건지 좀 헷갈린다. "response/id"로 가져와야하나? 아니면 그냥 "id"로 가져와야하나? json 형식으로 보여줬다면 조금 더 직관적인 느낌이었을지도 모르겠다. 대강 감은 오지만 일단 확실한 정보를 얻기 위해 sout을 찍어봤다.

{
  "resultcode": "00",
  "message": "success",
  "response":
  {
    "id": "GrzApdKssmguX3FcB8BG8QDUh9tKx45bXhEnOBxq_IU",
    "nickname": "자비",
    "profile_image": "https://ssl.pstatic.net/static/pwe/address/img_profile.png"
  }
}

오케이. 이런식으로 가져오는 거였군. "바로 적용"

 

private NaverUserInfoDto getNaverUserInfo(String accessToken) throws JsonProcessingException {
        // HTTP Header 생성
        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "Bearer " + accessToken);
        headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

        // HTTP 요청 보내기
        HttpEntity<MultiValueMap<String, String>> naverUserInfoRequest = new HttpEntity<>(headers);
        RestTemplate rt = new RestTemplate();
        ResponseEntity<String> response = rt.exchange(
                "https://openapi.naver.com/v1/nid/me",
                HttpMethod.POST,
                naverUserInfoRequest,
                String.class
        );

        // HTTP 응답 받아오기
        String responseBody = response.getBody();
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode jsonNode = objectMapper.readTree(responseBody);
        
        Long id = jsonNode.get("response").get("id").asLong();
        String nickname = jsonNode.get("response").get("nickname").asText();
        String profileImage = jsonNode.get("response").get("profile_image").asText();

        return NaverUserInfoDto.builder()
                .id(id)
                .nickname(nickname)
                .profileImage(profileImage)
                .build();
    }

정보는 받아왔다. sout 찍어보자.

id : 0
nickname : 자비
profileImage : https://ssl.pstatic.net/static/pwe/address/img_profile.png

잘 됨 ㅋㅋ

 

와 ㅋㅋㅋㅋㅋㅋㅋ

 

이제 나머지는 백엔드에서 알아서 잘 해주면 되는 영역이다. 와하하~

 

이로써 나는 OAuth2에 대해 알 수 있었고, 실제로 구현도 해봤다. 혹시 WebSecurity를 이용하지 않고 회원가입 절차를 진행하고 싶은 사람들은 이렇게 해보면 좋을 것 같다. 아이좋아.

 

공식문서는 언제나 사람을 강하게 만든다. 이 문서를 해석해낸 순간 모든 것을 해낼 수 있을 것만 같은 기분이 들게 한다. 이게... 더닝크루거 효과?

어이- OAuth2. 부끄러워할 것 없다. 강자에게 굴복하는 것은 당연한 것이니.

댓글