본문 바로가기
Coding/전자정부프레임

OAuth 2.0 버전을 이용하여 네이버 로그인해보기

by 찡콩찡 2022. 9. 7.

지난시간 : 스크립트 이용하여 로그인을 해보았다!  오늘은 oauth 2.0 버전을 이용하여 로그인을 해보자!

네이버 로그인 흐름도

 

1. 디비접속 lettnemplyrinfo에 네이버, 카카오에서 주는 ID KEY값이 길기 때문에 varchar를  20에서 60으로 변경 

2. 키 발급 네이버 개발자센터 접속 후 로그인을 해준다. ->application 제한이 있다!

https://developers.naver.com/main/

 

NAVER Developers

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

developers.naver.com

3. 처음 들어가는 장면에는 인증을 해야한다.!

네이버 로그인 사용 API

 

+ 팀원들도 넣을 수 있음! 

+ 참고용! 

4.pom.xml에서 auto 사용하기 위해 라이브러리를 작성

<!-- 네이버 로그인 -->
	<dependency>
		<groupId>com.googlecode.json-simple</groupId>
		<artifactId>json-simple</artifactId>
		<version>1.1.1</version>
	</dependency>
	
	<!-- oauth2 login -->
	<dependency>
		<groupId>com.github.scribejava</groupId>
		<artifactId>scribejava-apis</artifactId>
		<version>5.6.0</version>
	</dependency>
	
	<dependency>
		<groupId>com.github.scribejava</groupId>
		<artifactId>scribejava-core</artifactId>
		<version>2.8.1</version>	
	</dependency>
	
	<dependency>
		 <groupId>com.squareup.okhttp3</groupId>
		 <artifactId>okhttp</artifactId>
		 <version>4.2.2</version>
	</dependency>
	<!-- //네이버 로그인 -->

5 .globals.properties 역할 :  내 로컬만을 위한 값을 넣는 곳이 Globals.properties

 참고) context-properties 썸네일 이미지 사이즈 조정할때 사용했었음

 

 context-properties는 공통적인 것 업데이트를 해도 다른 사람들이 전혀 지장이 없는 것

예) api주소 

 

공통점: 설정을 저장하는 곳

 

6. 네이버에서 발급받았던 id와 key를 넣어준다,

 

7. 주석을 해제 후 빨간 밑줄이 뜨는 부분을 import해 받아준다. 

 

8. Global.java에 아래와 같이 추가해준다.

키정보를 추가로 넣는다.

 

 

9. let 아래 api.naver.service package를 생성한다. (imple도 함께)

     9-1) NaverLoginApi.java파일을 아래와 같이 작성해준다.

package egovframework.let.api.naver.service;

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

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";
	}

}

  9-2) NaverLoginService 파일을 아래와 같이 작성해준다.

package egovframework.let.api.naver.service;

import java.io.IOException;

import javax.servlet.http.HttpSession;

import com.github.scribejava.core.model.OAuth2AccessToken;



public interface NaverLoginService {
   
   /* 네이버 아이디로 인증 URL 생성 Method */
   String getAuthorizationUrl(HttpSession session, String domain);
   
   /* 네이버 아이디로 Callback 처리 및 AccessToken 획득  Method */
   OAuth2AccessToken getAccessToken(HttpSession session, String code, String state, String domain) throws IOException;
   
   /* 세션 유효성 검증을 위한 난수 생성기 */
   String generateRandomString();
   
   /* http session에 데이터 저장 */
   void setSession(HttpSession session, String state);
   
   /* http session에서 데이터 가져오기 */
   String getSession(HttpSession session);
   
   /* Access Token을 이용하여 네이버 사용자 프로필 API를 호출 */
   String getUserProfile(OAuth2AccessToken oauthToken) throws IOException;

}

  9-3) impl package를 만들어 그안에 serviceimpl.java를 만든다.

package egovframework.let.api.naver.service.impl;

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

import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.github.scribejava.core.builder.ServiceBuilder;
import com.github.scribejava.core.model.OAuth2AccessToken;
import com.github.scribejava.core.oauth.OAuth20Service;

import egovframework.com.cmm.service.Globals;
import egovframework.let.api.naver.service.NaverLoginApi;
import egovframework.let.api.naver.service.NaverLoginService;
import egovframework.rte.fdl.cmmn.EgovAbstractServiceImpl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;



	@Service("naverLoginService")
	public class NaverLoginServiceImpl extends EgovAbstractServiceImpl implements NaverLoginService {
		/*인증 요청문을 구성하는 파라미터*/
		private final static String CLIENT_ID = Globals.NAVER_CLIENTID;
		private final static String CLIENT_SECRET = Globals.NAVER_CLIENTSECRET;
		private final static String REDIRECT_URI = Globals.NAVER_REDIRECTURI;
		private final static String SESSION_STATE = "oauth_state";
		
		/*프로필 조회 api url*/
		private final static String PROFILE_API_URL = "https://openapi.naver.com/v1/nid/me";
		
		/*네이버 아이디로 인증 url 생성 method*/
		public String getAuthorizationUrl(HttpSession session, String domain) {
			String redirectUri = "http://" +domain + REDIRECT_URI;
		/*세션 유효성 검증을 위하여 난수를 생성*/
		String state = generateRandomString();
		/*생성한 난수 값을 session에 저장*/
		setSession(session, state);
		
		/*Scribe에서 제공하는 인증 url 생성 기능을 이용하여 네아로 인증 url 생성*/
		OAuth20Service oauthService = new ServiceBuilder()
					.apiKey(CLIENT_ID)
					.apiSecret(CLIENT_SECRET)
					.callback(redirectUri)
					.state(state)
					.build(NaverLoginApi.instance());
		
			return oauthService.getAuthorizationUrl();
	}
		/*네이버 아이디로 callback 처리 및 AccessToken 획득 Method*/
		public OAuth2AccessToken getAccessToken(HttpSession session, String code, String state, String domain) throws IOException {
			
			String redirectUri = "http://" + domain + REDIRECT_URI;
			
		/*callback으로 전달받은 세션검증용 난수값과 세션에 저장되어있는 값이 일치하는지 확인*/
		String sessionState = getSession(session);
			if(StringUtils.pathEquals(sessionState, state))  {
				OAuth20Service oauthService= new ServiceBuilder()
						.apiKey(CLIENT_ID)
						.apiSecret(CLIENT_SECRET)
						.callback(redirectUri)
						.build(NaverLoginApi.instance());
				
		/*Scribe에서 제공하는 AccessToken 획득 기능으로 네아로  Access Token을 획득*/
		OAuth2AccessToken accessToken = oauthService.getAccessToken(code);
		return accessToken;
			}
			return null;
		}
		/*세션 유효성 검증을 위한 난수 생성기*/
		public String generateRandomString() {
			return UUID.randomUUID().toString();
		}
		/*http session 데이터 저장*/
		public void setSession(HttpSession session, String state) {
			session.setAttribute(SESSION_STATE, state);
			}
		/*http session에서 데이터 가져오기*/
		public String getSession(HttpSession session) {
			return (String) session.getAttribute(SESSION_STATE);
		}
		/*Access Token을 이용하여 네이버 사용자 프로필 api를 호출*/
		public String getUserProfile(OAuth2AccessToken oauthToken) throws IOException{
			OkHttpClient client = new OkHttpClient().newBuilder().build();
			Request request = new Request.Builder()
					.url(PROFILE_API_URL)
					.method("GET", null)
					.addHeader("Content-Type","application/x-www-form-urlencoded")
					.addHeader("Authorization", "Bearer" + oauthToken.getAccessToken())
					.build();
			Response response = client.newCall(request).execute();
			return response.body().string();
	} 
		}

10. joinController에 네이버 로그인서비스를 연결해준다.

 

11. MemberType.jsp에 아래 코드를 추가해준다.

  참고) 미리 네이버 로고를 다운받아 asset/front/images에 넣어준다.

<a class="btn-naver" href="${naverAuthUrl}" data-type="join">
			<img src="/asset/front/images/btn-naver.png" width="150" alt="네이버 로그인  버튼"/>
</a>

12. 네이버 로그인 창이 뜨면 전체동의 box가 뜨는데 동의하면 오류가 뜬다.

왜? controller를 작성해주지 않아서 그래!

13.return 받은거에 대한 로직을 짜야해서 LoginController에 아래와 같이 로그인 콜백 코드를 추가한다.

	//네이버 로그인 콜백
	@RequestMapping(value="/login/naverLogin.do")
	public String naverLogin(@ModelAttribute("loginVO") LoginVO loginVO, @RequestParam String code, @RequestParam String state, HttpSession session, HttpServletRequest request, HttpServletResponse response, Model model)
	throws Exception {
		
		String domain = request.getServerName();
		OAuth2AccessToken oauthToken;
		oauthToken = naverLoginService.getAccessToken(session, code, state, domain);
		
		//로그인 사용자 정보를 읽어온다
		String apiResult = naverLoginService.getUserProfile(oauthToken);
		
		JSONParser parser = new JSONParser();
		Object obj = parser.parse(apiResult);
		JSONObject jsonObj = (JSONObject) obj;
		JSONObject result=(JSONObject) jsonObj.get("response");
		
		loginVO.setId("NAVER-" + result.get("id").toString());	
		loginVO.setPassword("");
		loginVO.setUserSe("USR");
		
		LoginVO resultVO = loginService.actionLogin(loginVO);
		//로그인 값이 없으면 회원가입 처리
		if (resultVO != null && resultVO.getId() != null && !resultVO.getId().equals("")) {
			request.getSession().setAttribute("LoginVO", resultVO);
			return "forward:/index.do";			
		}else {
			//일반 가입을 제외하고는 ID 값은 SNS명 + id 값
			JoinVO joinVO = new JoinVO();
			joinVO.setEmplyrId(loginVO.getId());
			joinVO.setUserNm(result.get("name").toString());
			joinVO.setPassword("");
			joinVO.setPasswordHint("SNS가입자");
			joinVO.setPasswordCnsr("SNS가입자");
			
			joinService.insertJoin(joinVO);
			model.addAttribute("loginMessage", "회원가입이 완료되었습니다.");
			
			return "forward:/index.do";
		}