HTML
<div id="External-login">
<a id="kakaoLoginLink"><img src="${contextPath}/resources/img/user/kakao_login_large_narrow 1.png" alt="카카오 로그인"></a>
</div>
kakao_login.js
// 카카오 로그인 설정
window.onload = function() {
const clientId= " 본인 ClientId ";
// 리다이렉트 URI를 UTF-8로 인코딩(encodeURIComponent)해서 저장
const redirectURI = encodeURIComponent("http://localhost:8888/staez/kakao-login")
//CSRF 방지 토큰이나 OAuth 2.0의 상태 파라미터 등 다양한 보안 및 고유 식별 용도
const state = Math.random().toString(36).substring(2);
//로그인 api url
+ 'client_id=' + clientId + "&redirect_uri=" + redirectURI + '&state=' + state;
const loginBtn = document.getElementById('kakaoLoginLink');
loginBtn.href = apiURL;
}
loginForm.js(이곳에도 작성해야함)-다른 간편로그인 있을 때
document.addEventListener('DOMContentLoaded', function() {
// 카카오 로그인 설정
const kakaoClientId = " 본인 ClientId ";
const kakaoRedirectURI = encodeURIComponent("http://localhost:8888/staez/kakao-login");
const kakaoState = Math.random().toString(36).substring(2);
+ 'client_id=' + kakaoClientId + "&redirect_uri=" + kakaoRedirectURI + '&state=' + kakaoState;
const kakaoLoginBtn = document.getElementById('kakaoLoginLink');
if (kakaoLoginBtn) {
kakaoLoginBtn.href = kakaoApiURL;
} else {
console.log("카카오 로그인 버튼을 찾을 수 없습니다."); // 디버깅용 콘솔 로그
}
// 기타 다른 간편로그인
}
loginForm.js(이곳에도 작성해야함)-한개만 있을 때
document.addEventListener('DOMContentLoaded', function() {
// 카카오 로그인 설정
window.onload = function() {
const clientId= " 본인 ClientId ";
// 리다이렉트 URI를 UTF-8로 인코딩(encodeURIComponent)해서 저장
const redirectURI = encodeURIComponent("http://localhost:8888/staez/kakao-login")
//CSRF 방지 토큰이나 OAuth 2.0의 상태 파라미터 등 다양한 보안 및 고유 식별 용도
const state = Math.random().toString(36).substring(2);
//로그인 api url
+ 'client_id=' + clientId + "&redirect_uri=" + redirectURI + '&state=' + state;
const loginBtn = document.getElementById('kakaoLoginLink');
loginBtn.href = apiURL;
}
}
Spring STS @Controller
package com.spring.staez.user.controller;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.spring.staez.user.model.vo.User;
import com.spring.staez.user.service.UserService;
@Controller
public class LoginController {
@Autowired
private UserService userService;
// 카카오 로그인 콜백을 처리하는 메서드입니다.
@RequestMapping("/kakao-login")
public String kakaoLoginCallback(HttpServletRequest request, HttpSession session) {
// 카카오 애플리케이션의 클라이언트 ID와 시크릿 키입니다.
String kakaoClientId = "본인 ClientId";
String kakaoClientSecret = "본인 ClientSecret";
// 카카오 인증 서버로부터 받은 인가 코드를 가져옵니다.
String kakaoCode = request.getParameter("code");
try {
// 리다이렉트 URI를 UTF-8로 인코딩합니다.
String redirectURI = URLEncoder.encode("http://localhost:8888/staez/kakao-login", "UTF-8");
// 액세스 토큰 요청 URL입니다.
String tokenRequestUrl = "https://kauth.kakao.com/oauth/token";
// URL 객체를 생성합니다.
URL url = new URL(tokenRequestUrl);
// URL 연결을 설정하고, HTTP POST 메서드를 사용합니다.
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
// POST 요청에 필요한 파라미터를 설정합니다.
String postParams = "grant_type=authorization_code"
+ "&client_id=" + kakaoClientId
+ "&redirect_uri=" + redirectURI
+ "&code=" + kakaoCode
+ "&client_secret=" + kakaoClientSecret;
// 요청 본문을 전송합니다.
con.setDoOutput(true);
OutputStream os = con.getOutputStream();
os.write(postParams.getBytes());
os.flush();
os.close();
// 응답 코드를 확인합니다.
int responseCode = con.getResponseCode();
// 응답 데이터를 읽어옵니다.
BufferedReader br = new BufferedReader(new InputStreamReader(responseCode == 200 ? con.getInputStream() : con.getErrorStream()));
String inputLine;
StringBuffer res = new StringBuffer();
while ((inputLine = br.readLine()) != null) {
res.append(inputLine);
}
br.close();
// 응답 코드가 200(성공)일 때
if (responseCode == 200) {
// 응답 결과를 문자열로 변환합니다.
String result = res.toString();
// JSON 파서를 사용하여 응답 결과를 JSON 객체로 변환합니다.
JsonObject totalObj = JsonParser.parseString(result).getAsJsonObject();
// 액세스 토큰을 추출합니다.
String token = totalObj.get("access_token").getAsString();
// 사용자 정보 요청 URL입니다.
String userInfoUrl = "https://kapi.kakao.com/v2/user/me";
// URL 객체를 생성합니다.
URL userInfoUrlObj = new URL(userInfoUrl);
// URL 연결을 설정하고, HTTP GET 메서드를 사용합니다.
HttpURLConnection userInfoCon = (HttpURLConnection) userInfoUrlObj.openConnection();
userInfoCon.setRequestMethod("GET");
// 요청 헤더에 액세스 토큰을 추가합니다.
userInfoCon.setRequestProperty("Authorization", "Bearer " + token);
// 사용자 정보 요청 응답 코드를 확인합니다.
int userInfoResponseCode = userInfoCon.getResponseCode();
// 응답 데이터를 읽어옵니다.
BufferedReader userInfoBr = new BufferedReader(new InputStreamReader(userInfoResponseCode == 200 ? userInfoCon.getInputStream() : userInfoCon.getErrorStream()));
String userInfoInputLine;
StringBuffer userInfoRes = new StringBuffer();
while ((userInfoInputLine = userInfoBr.readLine()) != null) {
userInfoRes.append(userInfoInputLine);
}
userInfoBr.close();
// 사용자 정보 요청 응답 코드가 200(성공)일 때
if (userInfoResponseCode == 200) {
// 응답 결과를 문자열로 변환합니다.
String userInfoResult = userInfoRes.toString();
// JSON 파서를 사용하여 응답 결과를 JSON 객체로 변환합니다.
JsonObject userInfoObj = JsonParser.parseString(userInfoResult).getAsJsonObject();
// 사용자 정보를 포함한 kakao_account 객체를 추출합니다.
JsonObject kakaoAccount = userInfoObj.getAsJsonObject("kakao_account");
// 이메일을 추출합니다.
String email = kakaoAccount.get("email").getAsString();
// 이메일로 사용자 검색
User user = userService.findUserByEmail(email);
if (user != null) {
// 사용자가 존재하면 로그인 처리
session.setAttribute("loginUser", user);
return "redirect:/"; // 메인 페이지로 리다이렉트
} else {
// 사용자가 존재하지 않으면 회원가입 페이지로 이동
return "redirect:/insertForm.me";
}
}
}
} catch (Exception e) {
e.printStackTrace(); // 예외가 발생하면 스택 추적을 출력합니다.
}
// 오류가 발생하거나 인증 실패 시 메인 페이지로 리다이렉트
return "redirect:/";
}
}
//API에 GET요청 보내고 응답을 받아오는 메서드
private static String get(String apiUrl, Map<String, String> requestHeaders) {
HttpURLConnection conn = connect(apiUrl);
try {
conn.setRequestMethod("GET");
for (Map.Entry<String, String> header : requestHeaders.entrySet()) {
conn.setRequestProperty(header.getKey(), header.getValue());
}
int responseCode = conn.getResponseCode();
if(responseCode == 200) {
return readBody(conn.getInputStream());
} else {
return readBody(conn.getErrorStream());
}
} catch (IOException e) {
throw new RuntimeException("API 요청과 응답 실패. : " + apiUrl, e);
} finally {
conn.disconnect();
}
}
//API에 연결하기위한 HttpURLConnection객체를 생성하고 반환하는 메서드
private static HttpURLConnection connect(String apiUrl) {
try {
URL url = new URL(apiUrl);
return (HttpURLConnection)url.openConnection();
} catch (MalformedURLException e) {
e.printStackTrace();
throw new RuntimeException("API URL이 잘못되었습니다. : " + apiUrl, e);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("연결이 실패하였습니다. : " + apiUrl, e);
}
}
private static String readBody(InputStream body) {
InputStreamReader streamReader = new InputStreamReader(body);
try (BufferedReader br = new BufferedReader(streamReader)){
StringBuilder responseBody = new StringBuilder();
String line;
while((line = br.readLine()) != null) {
responseBody.append(line);
}
return responseBody.toString();
} catch (IOException e) {
throw new RuntimeException("API 응답을 읽는데 실패하였습니다.", e);
}
}
}
Spring STS interface UserService / UserServiceImpl
import java.util.Map;
import com.spring.staez.user.model.vo.User;
public interface UserService {
public User loginUser(User u);
// 간편로그인 이메일 유무 확인 (네이버, 카카오, 구글)
User findUserByEmail(String email);
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
import java.util.Map;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.spring.staez.user.model.dao.UserDao;
import com.spring.staez.user.model.vo.User;
@Service
public class UserServiceImpl implements UserService{
@Autowired
SqlSessionTemplate sqlSession;
@Autowired
UserDao userDao;
// 간편로그인 이메일 유무 확인 (네이버, 카카오, 구글)
@Override
public User findUserByEmail(String email) {
return userDao.findUserByEmail(sqlSession,email);
}
}
Spring STS class Dao
package com.spring.staez.user.model.dao;
import java.util.HashMap;
import java.util.Map;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.spring.staez.user.model.vo.User;
@Repository
public class UserDao {
@Autowired
private SqlSessionTemplate sqlSession;
// 간편로그인 이메일 유무 확인 (네이버, 카카오, 구글)
public User findUserByEmail(SqlSessionTemplate sqlSession, String email) {
return sqlSession.selectOne("userMapper.findUserByEmail", email);
}
}
Spring STS PSQL 기준
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace: 해당 mapper파일의 고유한 별칭 -->
<mapper namespace="userMapper">
<resultMap type="User" id="userResult">
<result column="user_no" property="userNo" />
<result column="user_id" property="userId" />
<result column="user_pwd" property="userPwd" />
<result column="user_name" property="userName" />
<result column="nickname" property="nickname" />
<result column="gender" property="gender" />
<result column="birth" property="birth" />
<result column="address" property="address" />
<result column="email" property="email" />
<result column="phone" property="phone" />
<result column="genre_like" property="genreLike" />
<result column="grade" property="grade" />
<result column="enroll_date" property="enrollDate" />
<result column="withdrawal_date" property="withdrawalDate" />
<result column="user_status" property="userStatus" />
</resultMap>
<resultMap type="java.util.Map" id="emailResult">
<result column="email" property="email"/>
<result column="authno" property="authNo"/>
<result column="send_time" property="sendTime"/>
</resultMap>
<!-- 간편로그인 이메일 유무 확인 (네이버, 카카오, 구글) -->
<select id="findUserByEmail" parameterType="string" resultType="com.spring.staez.user.model.vo.User">
SELECT *
FROM staez_user
WHERE email = #{email}
</select>
</mapper>