스프링 - 네아 로그인 구현

jinvicky·2023년 2월 8일
0

2023 Spring Project TIL

목록 보기
12/32

참고사이트
https://cobook.tistory.com/31
https://m.blog.naver.com/sam_sist/220969407741

새벽에 따라치고 분석하고 하느라 정신이 몽롱하다...
자고 일어나서 까먹기 전에 한 자라도 적어본다.

  1. 로그인 화면 경로로 get요청이 가면 이케저케해서 네이버 소셜 로그인 경로를 controller에서 받아와서 model인 urlNaver에 담는다.
    jsp에서 링크 경로를 ${urlNaver}로 하면 버튼을 클릭했을 때
    네이버의 창이 뜬다(처음에 urlNaver의 의미를 이해 못했었다.)

  2. scribejava-core 라이브러리라는 것을 사용한다.
    scribe에서 제공하는 인증 url 생성 기능이라는 것을 이용한다고 한다.
    또한 accessToken 획득 기능을 제공해서 이것으로 네이버 아이디로 Access Token을 획득할 수 있다고 한다.

세션 유효성 검증을 위한 난수 생성 부분이 잘 이해가 안 갔다.
저런 식인가보다 하긴 했는데 세션 검증용 난수값과 세션에
저장되어 있는 값이 일치하는 지 확인하는 것이라는 데 어렵다. ㅜㅜ

  1. JSONParser import 잘해야 한다. 따라치는데
    코드에서 에러가 JsonParser에서 났는데 보니 import의 문제였다.
    아래 둘을 import해서 쓰도록 하자.
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
  1. 그리고 scribejaba-core 라이브러리를 사용하는데
    버전이 생각보다 중요하다. 버전이 최신이면 ServiceBuilder() 객체를 생성할 때 ()안이 비었다고 에러가 난다.
    여러 사이트를 참고하는데 코드가 동일하길래 한 사이트에서 나온
    버전인 2.8로 낮추었더니 에러가 사라졌다.
<dependency>
	<groupId>com.github.scribejava</groupId>
	<artifactId>scribejava-core</artifactId>
	<version>2.8.1</version>
</dependency>
  1. 해보지는 않았지만 아래 사이트에서 보니 여러 계정으로 테스트를 하려면 테스트 계정을 등록해야 에러가 나지 않는다.

  2. jsp 화면에서 꼭 아래 네이버 관련 태그를 추가해야 한다.

    <script type="text/javascript" src="https://static.nid.naver.com/js/naverLogin_implicit-1.0.3.js" charset="utf-8"></script>
  1. 코딩 전 애플리케이션 등록
    사이트 참고하다보면 셋팅은 다 나와있다.
    코딩할 때 중요한 것은 대략 4가지다. 메모장에 옮기고 참고하는게 덜 헷갈리고 좋았다.
  2. 클라이언트 아이디
  3. 클라이언트 시크릿
  4. 로그인 후 redirect할 콜백 uri
  5. 서비스 url

복붙한 코드지만 백업용으로 올려본다.

package com.fastcampus.ch4.domain;

import com.github.scribejava.core.builder.api.DefaultApi20;

public class NaverOAuthApi extends DefaultApi20 {

    protected NaverOAuthApi() {}

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

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

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

    @Override
    public String getAuthorizationBaseUrl() {
        return "https://nid.naver.com/oauth2.0/authorize";
    }
}
package com.fastcampus.ch4.domain;

import javax.servlet.http.HttpSession;

import com.github.scribejava.core.builder.ServiceBuilder;
import com.github.scribejava.core.model.OAuth2AccessToken;
import com.github.scribejava.core.model.OAuthRequest;
import com.github.scribejava.core.model.Response;
import com.github.scribejava.core.model.Verb;
import com.github.scribejava.core.oauth.OAuth20Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.UUID;

public class NaverLoginBo {

    private static final String CLIENT_ID = "클라이언트 아이디 적으면 된다.";
    private static final String CLIENT_SECRET = "클라이언트 시크릿 적으면 된다.";
    private static final String REDIRECT_URI = "http://localhost:8080/login/oauth2/code/naver";
    private static final String SESSION_STATE = "oauth_state";

    private static final String PROFILE_API_URL = "https://openapi.naver.com/v1/nid/me";


    public String getAuthorizationUrl(HttpSession session) {
        String state = generateRandomString();
        setSession(session, state);

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

        return oauthService.getAuthorizationUrl();
    }

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

        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(NaverOAuthApi.instance());

            OAuth2AccessToken accessToken = oauthService.getAccessToken(code);
            return accessToken;
        }
        return null;
    }

    private String generateRandomString() {
        return UUID.randomUUID().toString();
    }

    private void setSession(HttpSession session, String state) {
        session.setAttribute(SESSION_STATE, state);
    }

    private String getSession(HttpSession session) {
        return (String)session.getAttribute(SESSION_STATE);
    }

    public String getUserProfile (OAuth2AccessToken oauthToken) throws Exception {
        OAuth20Service oauthService = new ServiceBuilder()
                .apiKey(CLIENT_ID)
                .apiSecret(CLIENT_SECRET)
                .callback(REDIRECT_URI).build(NaverOAuthApi.instance());

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


import com.fastcampus.ch4.domain.NaverLoginBo;
import com.github.scribejava.core.model.OAuth2AccessToken;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpSession;

@Controller
public class CityTurtleController {

    private NaverLoginBo naverLoginBo;
    private String apiResult = null;

    @Autowired
    private void setNaverLoginBo(NaverLoginBo naverLoginBo) {
        this.naverLoginBo = naverLoginBo;
    }

    @RequestMapping(value = "/login.do", method = {RequestMethod.GET, RequestMethod.POST})
    public String login(Model model, HttpSession session) {

        String naverAuthUrl = naverLoginBo.getAuthorizationUrl(session);
        System.out.println("네이버~~:: " + naverAuthUrl);
        model.addAttribute("urlNaver", naverAuthUrl);

        return "login";
    }

    //네이버 로그인 성공시 콜백
    @RequestMapping(value = "/login/oauth2/code/naver", method = {RequestMethod.GET, RequestMethod.POST})
    public String callbackNaver(Model model, @RequestParam String code, @RequestParam String state, HttpSession session) throws Exception {
        System.out.println("시스템 성공 콜백 ");
        OAuth2AccessToken oauthToken;
        oauthToken = naverLoginBo.getAccessToken(session, code, state);
        apiResult = naverLoginBo.getUserProfile(oauthToken);

        JSONParser jsonParser = new JSONParser();
        JSONObject jsonObj;

        jsonObj = (JSONObject) jsonParser.parse(apiResult);
        JSONObject response_obj = (JSONObject) jsonObj.get("response");

        String email = (String) response_obj.get("email");
        String name = (String) response_obj.get("name");

        session.setAttribute("signin", apiResult);
        session.setAttribute("email", email);
        session.setAttribute("name", name);

        return "redirect:/loginSuccess.do";

    }

    @RequestMapping("/loginSuccess.do")
    public String loginSuccess() {
        return "loginSuccess";
    }
}
profile
Front-End와 Back-End 경험, 지식을 공유합니다.

0개의 댓글

관련 채용 정보