초코딩(chocoding)

[Project - Spring Boot] 소셜 로그인/API - 네이버 : 기본 초기 세팅 / 로그인 정보 db에 담기 / 로그인정보 session에 담기 / 로그아웃 구현 본문

Project

[Project - Spring Boot] 소셜 로그인/API - 네이버 : 기본 초기 세팅 / 로그인 정보 db에 담기 / 로그인정보 session에 담기 / 로그아웃 구현

sweetychocoding 2024. 1. 24. 14:54
728x90

 

먼저 네이버 소셜 로그인 기초 세팅은 구글링하여 다른 블로그를 참고했다.

 

https://velog.io/@tjddus0302/Spring-Boot-OAuth-2-%EC%86%8C%EC%85%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84-Naver

 

Spring Boot | OAuth 2 소셜 로그인 구현 (Naver)

네이버 소셜 로그인 구현하기

velog.io

 

 

.

.

.

 

 

나는 카카오, 네이버, 구글 이 세 개의 소셜 로그인을 구현할 예정인데

현재로는 카카오, 네이버만 구현한 상태이다.

 

처음 controller를 설계할 때 너무나도 무지해서 (.... ㅠㅠ)

카카오에서 넘어오는 값과 네이버에서 넘어오는 값을... 뭘로 구별해야 할까.. 하다가

모르겠어서 일단 controller를 따로따로 만들었다...

 

지금은 어느정도 .. (?) 감을 잡아서

구글 로그인 구현을 하기 전에 controller를 하나로 합칠 생각이다.

 

어쨌든.. 무ㅝ... 이런 말을 하는 이유는

 

지금은 컨트롤러가 소셜당 하나씩... 이닷...! 라는 말을 하고 싶었슴니다.. 으하하하하

 

 

그럼 고고링

...!!!

 

 

 

LoginController

package com.cm.personalProject.controller;

import java.io.IOException;

import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.cm.personalProject.entity.User;
import com.cm.personalProject.service.KakaoAPI;
import com.cm.personalProject.service.NaverAPI;

import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j2;

@Log4j2
@AllArgsConstructor
@RequestMapping(value = "/social")
@Controller
public class LoginController {

	private KakaoAPI kakao;
	private NaverAPI naver;

	@GetMapping("/loginPage")
	public void getLoginPage() {

	}

	@GetMapping("/klogin")
	public String klogin(@RequestParam("code") String code) {
		System.out.println(code);
		String access_token = kakao.getAccessToken(code);
		System.out.println("controller access_token : " + access_token);

		String userInfo;
		try {
			userInfo = kakao.getUserInfo(access_token);
			if ("성공".equals(userInfo)) {
				return "redirect:/home";
			} else {
				return "redirect:/social/loginPage";
			}
		} catch (IOException e) {
			e.printStackTrace();
			return "redirect:/social/loginPage";
		}

	}

	@GetMapping("/nlogin")
	public String nlogin(@RequestParam("code") String code, Model model, HttpSession session) {
		System.out.println(code);

		String access_token = naver.getAccessToken(code);

		try {
			User userInfo = naver.getUserInfo(access_token).orElse(null);
			System.out.println("*****************" + userInfo);

			if (userInfo != null) {
				session.setAttribute("loginUser", userInfo);
				return "redirect:/home";
			} else {
				return "redirect:/social/loginPage";
			}

		} catch (Exception e) {

			return "redirect:/social/loginPage";
		}

	}

	@GetMapping(value = "/logout")
	public String logout(HttpSession session) {

		session.invalidate();
		return "redirect:/home";
	}
}

 

 

 

 

 

 

NaverAPI

package com.cm.personalProject.service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Optional;

import org.springframework.stereotype.Service;

import com.cm.personalProject.domain.OauthId;
import com.cm.personalProject.entity.User;
import com.cm.personalProject.repository.UserRepository;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;

@Service
@RequiredArgsConstructor
@Log4j2
public class NaverAPI {

	private final UserRepository repository;

	// Naver API로부터 인증 코드를 사용하여 액세스 토큰을 얻어오는 메서드
	public String getAccessToken(String code) {

		try {
			String redirectURI = URLEncoder.encode("http://localhost:8080/social/nlogin", "UTF-8");
			String apiURL = "https://nid.naver.com/oauth2.0/token?grant_type=authorization_code&";
			apiURL += "client_id=" + "****1lTRrhBHowHEvL4f";
			apiURL += "&client_secret=" + "****6CNy4Z";
			apiURL += "&redirect_uri=" + "http://localhost:8080/social/nlogin";
			apiURL += "&code=" + code;
			apiURL += "&state=" + 1234;
			System.out.println("apiURL=" + apiURL);

			URL url = new URL(apiURL);
			HttpURLConnection con = (HttpURLConnection) url.openConnection();
			con.setRequestMethod("POST");
			int responseCode = con.getResponseCode();
			BufferedReader br;

			if (responseCode == 200) { // 정상 호출
				br = new BufferedReader(new InputStreamReader(con.getInputStream()));
			} else { // 에러 발생
				br = new BufferedReader(new InputStreamReader(con.getErrorStream()));
				System.out.println("에러 응답: " + br.readLine()); // 에러 응답 출력
				return null;
			}

			String inputLine;
			StringBuffer res = new StringBuffer();
			while ((inputLine = br.readLine()) != null) {
				res.append(inputLine);
			}

			if (responseCode == 200) {
				System.out.println(res.toString());
			}

			String result = res.toString();
			System.out.println("response body : " + result);

			JsonParser parser = new JsonParser();
			JsonElement element = parser.parse(result);

			br.close();

			return element.getAsJsonObject().get("access_token").getAsString();
		} catch (Exception e) {
			log.error("Error during getAccessToken", e);
			return null;
		}
	}

	// getUserInfo
	public Optional<User> getUserInfo(String accessToken) throws IOException {

		// 네이버 로그인 접근 토큰;
		String apiURL = "https://openapi.naver.com/v1/nid/me";
		String headerStr = "Bearer " + accessToken; // Bearer 다음에 공백 추가
		String result = requestToServer(apiURL, headerStr);
		System.out.println("사용자 정보 " + result);

		JsonParser parser = new JsonParser();
		JsonElement element = parser.parse(result);

		JsonObject response = element.getAsJsonObject().get("response").getAsJsonObject();
		System.out.println("*****response: " + response);

		String token_id = response.getAsJsonObject().get("id").getAsString();
		String nickname = response.getAsJsonObject().get("name").getAsString();
		String email = response.getAsJsonObject().get("email").getAsString();
		System.out.println("email" + email);

		Optional<User> opt_user = repository.findById(new OauthId("naver", token_id));
		System.out.println("--------opt_user : " + opt_user);

		User user = new User();
		if (opt_user.isPresent()) {
			return opt_user;
		} else {
			user.setUseremail(email);
			user.setUsername(nickname);
			user.setOauthtype("naver");
			user.setOauthtoken(token_id);
			repository.save(user);

			return Optional.of(user);
		}

	}

	private String requestToServer(String apiURL, String headerStr) throws IOException {
		URL url = new URL(apiURL);
		HttpURLConnection con = (HttpURLConnection) url.openConnection();
		con.setRequestMethod("GET");
		System.out.println("header Str: " + headerStr);
		if (headerStr != null && !headerStr.equals("")) {
			con.setRequestProperty("Authorization", headerStr);
		}
		int responseCode = con.getResponseCode();
		BufferedReader br;
		System.out.println("responseCode=" + responseCode);
		if (responseCode == 200) { // 정상 호출
			br = new BufferedReader(new InputStreamReader(con.getInputStream()));
		} else { // 에러 발생
			br = new BufferedReader(new InputStreamReader(con.getErrorStream()));
		}
		String inputLine;
		StringBuffer res = new StringBuffer();
		while ((inputLine = br.readLine()) != null) {
			res.append(inputLine);
		}
		br.close();
		if (responseCode == 200) {
			return res.toString();
		} else {
			return null;
		}
	}

}

 

 

 

 

 

 

또한 로그인이 되었는지 확인이 되지 않아 테스트할 때 번거로워서

session에 로그인 유저 정보를 저장하고

로그아웃 기능을 구현하였다.

 

 

home.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h2>메롱메롱 테스트</h2>
	<c:if test="${not empty sessionScope.loginUser}">
      ${sessionScope.loginUser.username}님 안녕하세요.
      <br />
		<a href="social/logout">로그아웃</a>
		<a>게시판가기</a>
	</c:if>

	<c:if test="${empty sessionScope.loginUser}">
		<a href="social/loginPage">로그인</a>
	</c:if>

	<a>게시판가기</a>

</body>
</html>

 

 

 

 

 

.

.

.

 

 

 

 

사실 현재 코드 중복이 많아서 아쉬움이 많은 편이다.

일단 기능 구현에 초점을 두어서 코드 최적화 시키는 것을 뒤로 미뤘다.

 

다음 블로그 글에서는 중복을 없애고 최적화된 코드에 대해서 다뤄볼까 한다.

 

또한 구글 로그인 기능을 구현하기 전에

aws를 통하여 배포를 먼저 도전해보고 싶기 때문에

 

어떤 걸 먼저해야 될 지 고민을 해봐야 될 것 같다.

 

 

 

이상~

728x90