[java/jsp] 네이버 아이디로 로그인 구현하기
1. 네이버 아이디로 로그인 링크만들기
- 먼저
http://localhost:9999/HN-OpenBanking/auth/naver.do
을 클릭하면 팝업창으로 넘어가게 만들었다.
<div class="col-6 col-12-small">
<h3>다른 서비스로 로그인</h3>
<a href="http://localhost:9999/HN-OpenBanking/auth/naver.do"
onclick="window.open(this.href, '_blank', 'width=500px,height=800px,toolbars=no,scrollbars=no'); return false;">
<img src="images/naver_login.png" style="width: 50%">
</a>
<br>
<a href="#"><img src="images/naver_login.png" style="width: 50%"></a>
<br>
</div>
- 실행 화면
- 코드 원문 : https://github.com/hennylee/kopo-05-web/blob/main/code/HN-OpenBanking/WebContent/login/login.jsp
2. beans.properties 등록
/auth/naver.do=kr.co.hn.controller.NaverLoginController
3. NaverLoginController 생성
package kr.co.hn.controller;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.security.SecureRandom;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
// api명세 : https://developers.naver.com/docs/login/api/api.md
// 코드 출처 : https://devtansan-s-tocking.tistory.com/16
public class NaverLoginController implements Controller{
public final static String CLIENT_ID = "h5b3O54T4arCVZ7EKFSR";
public final static String CLIENT_SECRET = "itSPnyhzQ6";
public final static String CALLBACK_URL = "http://localhost:9999/HN-OpenBanking/auth/naver/callback.do";
@Override
public String handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpSession session = request.getSession();
String clientId = CLIENT_ID;//애플리케이션 클라이언트 아이디값";
String redirectURI = URLEncoder.encode(CALLBACK_URL, "UTF-8");
// CSRF 방지를 위한 상태 토큰 생성 코드
SecureRandom random = new SecureRandom();
String state = new BigInteger(130, random).toString();
String apiURL = "https://nid.naver.com/oauth2.0/authorize?response_type=code";
apiURL += "&client_id=" + clientId;
apiURL += "&redirect_uri=" + redirectURI;
apiURL += "&state=" + state;
// 상태 토큰은 추후 검증을 위해 세션에 저장되어야 한다.
session.setAttribute("state", state);
return "sendRedirect:" + apiURL;
}
}
-
가장 먼저 개발자 사이트에서 등록한 client_id, redirect_uri와 생성한 state값(상태토큰)을 가지고 아래 URL로 넘어가도록 해준다.
-
이때, 반환되는 String이
sendRedirect:
로 시작하는데, FrontController 부분에서sendRedirect:
가 붙으면 앞에 reqeust.getContextPath를 붙이지 않고 바로 해당 url로 이동되도록 설정했다. -
FrontController 코드 : https://github.com/hennylee/kopo-05-web/blob/main/code/HN-OpenBanking/src/kr/co/hn/FrontControllerServlet.java
-
상태 토큰은 다음 단계에서도 사용하기 위해 세션에 저장해둔다.
-
이동되는 apiURL :
apiURL : https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=h5b3O54T4arCVZ7EKFSR&redirect_uri=http%3A%2F%2Flocalhost%3A9999%2FHN-OpenBanking%2Fauth%2Fnaver%2Fcallback.do&state=428633826614157219736944190684946690114
- 이 URL로 접속해보면 아래 화면이 뜬다.
-
이 단계에서 로그인을 해주면,
http://localhost:9999/HN-OpenBanking/auth/naver/callback.do?code=1pVegeEhXTq4zjDp4856&state=42863382661415721973694419068494669011434
와 같은 URL로 이동된다. -
개발자 사이트에 등록해둔 callback URL임을 알 수 있다.
-
그리고 파라미터로 code, state 가 넘어온다. 이 url을 컨트롤러에서 처리하기 위해서 bean.properties에 해당 url을 등록한다.
4. beans.properties 등록
/auth/naver/callback.do=kr.co.hn.controller.NaverCallbackController
5. AccessToken토큰을 받아서 네이버 사용자 프로필을 조회하는 컨트롤러
-
이 단계에서는 토큰을 받아서 사용자 프로필을 조회한 후, DB에 저장하는 단계이다.
-
사용자 프로필은 JSON 형태로 반환되기 때문에 JSON을 파싱해줄 SimplyJSON 라이브러리를 활용했다.
-
네이버로부터 받아온 id를 기반으로 이미 존재하는 회원이면 로그인 페이지로 전환시키고 존재하지 않는 회원이라면 회원가입 페이지로 넘겨줄 수 있도록 url을 달리 저장했다.
-
url전환은 현재 열려있는 팝업창을 닫고 진행해야 하기 때문에 script만 존재하는 naverJoin.jsp 로 페이지를 먼저 foward시켰다.
package kr.co.hn.controller;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import kr.co.hn.dao.LoginDAO;
import kr.co.hn.dao.MemberDAO;
import kr.co.hn.vo.MemberVO;
// api 명세 : https://developers.naver.com/docs/login/profile/profile.md
// 코드 출처 : https://devtansan-s-tocking.tistory.com/17?category=811719
public class NaverCallbackController implements Controller {
@Override
public String handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
String msg = "";
String url = "";
HttpSession session = request.getSession();
String code = request.getParameter("code");
String state = (String) session.getAttribute("state");
// 1. 아래 만들어둔 getAccessToken함수를 통해 AccessToken 받기
JSONObject jsonObj = getAccessToken(code, state);
// 2. 받아온 AccessToken이 있다면?
if (jsonObj != null) {
// 3. 아래 만들어둔 getAuthInfo 메소드를 사용해서 사용자 프로필 정보를 받아온다.
JSONObject resObj = getAuthInfo(jsonObj);
// 4. 받아온 정보가 있다면?
if (resObj != null) {
// 5-1. 사용자 프로필 정보를 변수에 저장한다.
String naverCode = (String)resObj.get("id");
//String nickname = (String)resObj.get("nickname");
String name = (String)resObj.get("name");
String email = (String)resObj.get("email");
String tel = (String)resObj.get("mobile");
String[] emails = email.split("@");
String[] tels = (tel.split("-"));
// 5-2. vo객체에 받아온 정보를 담는다.
MemberVO member = new MemberVO();
member.setId(naverCode);
member.setPassword(naverCode);
member.setEmailId(emails[0]);
member.setEmailDomain(emails[1]);
member.setName(name);
member.setTel1(tels[0]);
member.setTel2(tels[1]);
member.setTel3(tels[2]);
member.setType("U");
// 5-3. 받아온 객체가 회원에 존재하는지 확인한다.
LoginDAO dao = new LoginDAO();
MemberVO user = dao.login(member);
// 5-4. 존재하는 회원이 없으면 => 회원가입으로 진입
if(user == null) {
url = request.getContextPath() + "/naverJoinForm.do";
msg = "join";
}
// 5.5 존재하는 회원이 있으면 => 로그인으로 진입
else {
url = request.getContextPath() + "/loginProcess.do";
msg = "login";
}
// 6. 필요한 정보 공유영역에 등록하기
session.setAttribute("member", member);
request.setAttribute("msg", msg);
request.setAttribute("url", url);
}
}
// 7. 페이지 이동시키기
return "/member/naverJoin.jsp";
}
// (1) handleRequest에서 사용할 AccessToken을 JSONObject로 받아오는 메소드
private JSONObject getAccessToken(String code, String state) {
StringBuilder apiURL = new StringBuilder("https://nid.naver.com/oauth2.0/token?grant_type=authorization_code");
apiURL.append("&client_id=" + NaverLoginController.CLIENT_ID);
apiURL.append("&client_secret=" + NaverLoginController.CLIENT_SECRET);
apiURL.append("&code=" + code);
apiURL.append("&state=" + state);
try {
URL url = new URL(apiURL.toString());
HttpURLConnection http = (HttpURLConnection) url.openConnection();
http.setRequestMethod("GET");
int responseCode = http.getResponseCode();
BufferedReader br;
// 정상 호출이라면?
if (responseCode == 200) {
br = new BufferedReader(new InputStreamReader(http.getInputStream()));
}
// 에러가 발생했다면?
else {
br = new BufferedReader(new InputStreamReader(http.getErrorStream()));
}
String inputLine;
StringBuffer res = new StringBuffer();
while ((inputLine = br.readLine()) != null) {
res.append(inputLine);
}
br.close();
JSONParser parsing = new JSONParser();
Object obj = parsing.parse(res.toString());
JSONObject jsonObj = (JSONObject) obj;
return jsonObj;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// (2) 사용자 프로필정보를 JSONObject로 받아오는 메소드
private JSONObject getAuthInfo(JSONObject jsonObj) throws IOException, ParseException {
String access_token = (String) jsonObj.get("access_token");
String refresh_token = (String) jsonObj.get("refresh_token");
String apiURL = "https://openapi.naver.com/v1/nid/me";
String header = "Bearer " + access_token; // Bearer 다음에 공백 추가
try {
URL url = new URL(apiURL);
HttpURLConnection http = (HttpURLConnection) url.openConnection();
http.setRequestMethod("GET");
http.setRequestProperty("Authorization", header);
int responseCode = http.getResponseCode();
BufferedReader br;
// 정상호출
if (responseCode == 200) {
br = new BufferedReader(new InputStreamReader(http.getInputStream()));
} else {
br = new BufferedReader(new InputStreamReader(http.getErrorStream()));
}
String inputLine;
StringBuffer res = new StringBuffer();
while ((inputLine = br.readLine()) != null) {
res.append(inputLine);
}
br.close();
JSONParser parsing = new JSONParser();
Object obj = parsing.parse(res.toString());
JSONObject jsonObj2 = (JSONObject)obj;
JSONObject resObj = (JSONObject)jsonObj2.get("response");
return resObj;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
- 코드 원문 : https://github.com/hennylee/kopo-05-web/blob/main/code/HN-OpenBanking/src/kr/co/hn/controller/NaverCallbackController.java
6. /member/naverJoin.jsp
- 열려있던 팝업창을 닫고, 이미 존재하는 회원이면 로그인 페이지로 전환시키고 존재하지 않는 회원이라면 회원가입 페이지로 넘겨주도록 수행시켰다.
<%@page import="kr.co.hn.vo.MemberVO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<script>
self.close();
opener.document.location.href="${url}"
</script>
- 코드원문 : https://github.com/hennylee/kopo-05-web/blob/main/code/HN-OpenBanking/WebContent/member/naverJoin.jsp