728x90
https://innovation123.tistory.com/181
https://innovation123.tistory.com/181
https://developers.kakao.com/docs/latest/ko/kakaologin/trouble-shooting#code
application.properties
#Kakao Login
client_id=REST API 키
redirect_uri= Redirect URI
secret_code= Client Secret(보안에서 설정되어 있지 않으면 필요 없음)
KakaoLoginController
package com.moonBam.controller.member.OpenApi;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.moonBam.controller.member.DebugBoardController;
import com.moonBam.controller.member.SecurityController;
import com.moonBam.dto.MemberDTO;
import com.moonBam.service.member.OpenApiService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
public class KakaoLoginController {
@Value("${client_id}")
private String client_id;
@Value("${redirect_uri}")
private String redirect_uri;
@Value("${secret_code}")
private String secret_code;
@Autowired
OpenApiService serv;
@Autowired
DebugBoardController dbc;
@Autowired
SecurityController sc;
@Autowired
OpenApiController oac;
// 카카오에서 토큰 받아오기
@GetMapping("/getKakaoAuthUrl")
public ResponseEntity<Object> getKakaoAuthUrl() throws URISyntaxException {
String reqUrl = "https://kauth.kakao.com/oauth/authorize?client_id="+client_id+"&redirect_uri="+redirect_uri+"&response_type=code";
URI redirectUri = new URI(reqUrl);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setLocation(redirectUri);
return new ResponseEntity<>(httpHeaders, HttpStatus.SEE_OTHER);
}
// 카카오에서 리다이렉션
@RequestMapping(value = "kakaoLogin")
public ModelAndView getKakaoCode(String code, HttpSession session) throws IOException{
//1. 받은 인가 코드 받기
//System.out.println(code);
//2. 코드를 통해 토큰 받기
String accessToken = getAccessToken(code);
//System.out.println("accessToken: "+accessToken);
//3. 사용자 정보 받기
Map<String, Object> map = getUserInfo(accessToken);
ObjectMapper objectMapper = new ObjectMapper();
//아이디
String id = sc.encrypt(String.valueOf(map.get("id")));
//이미 가입한 사람인지 확인
MemberDTO check = serv.selectOneAPIMember(id);
ModelAndView mav = new ModelAndView();
//미가입자일 경우, 자동 가입
if(check == null) {
//비밀번호
String pw = sc.encrypt("Kakao"+dbc.getNum(16));
//이름
String jsonString = objectMapper.writeValueAsString(map.get("properties"));
JsonNode jsonNode = objectMapper.readTree(jsonString);
String name = jsonNode.get("nickname").asText();
//닉네임
String nickname = oac.randomNickname();
//이메일
String jsonString2 = objectMapper.writeValueAsString(map.get("kakao_account"));
JsonNode jsonNode2 = objectMapper.readTree(jsonString2);
String email = jsonNode2.get("email").asText();
String[] emailParts = email.split("@");
//유저 가입일
Date currentDate = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
String userSignDate = dateFormat.format(currentDate);
MemberDTO dto = new MemberDTO();
dto.setUserId(id);
dto.setUserPw(pw);
dto.setUserName(name);
dto.setNickname(nickname);
dto.setUserEmailId(emailParts[0]);
dto.setUserEmailDomain(emailParts[1]);
dto.setUserSignDate(userSignDate);
dto.setUserType("1");
//회원가입
serv.insertAPIMember(dto);
//닉네임 변경하는 화면으로 이동
MemberDTO nDTO = serv.selectOneAPIMember(dto.getUserId());
session.setAttribute("loginUser", nDTO);
mav.addObject("dto", nDTO);
mav.setViewName("member/Login/APILogin");
return mav;
//가입자일 경우, 메인으로 이동
} else {
session.setAttribute("loginUser", check);
mav.setViewName("redirect:/");
return mav;
}
}
// 카카오에서 액세스 토큰 받아오기
public String getAccessToken(String code) throws IOException {
String accessToken = "";
String refreshToken = "";
String reqUrl = "https://kauth.kakao.com/oauth/token";
URL url = new URL(reqUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
conn.setDoOutput(true);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
StringBuilder sb = new StringBuilder();
sb.append("grant_type=authorization_code");
sb.append("&client_id=").append(client_id);
sb.append("&redirect_uri=").append(redirect_uri);
sb.append("&code=").append(code);
sb.append("&client_secret=").append(secret_code);
bw.write(sb.toString());
bw.flush();
int responseCode = conn.getResponseCode();
log.info("[KakaoApi.getAccessToken] responseCode = {}", responseCode);
System.out.println(responseCode);
BufferedReader br;
if (responseCode >= 200 && responseCode <= 300) {
br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
} else {
br = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
}
String line = "";
StringBuilder responseSb = new StringBuilder();
while((line = br.readLine()) != null){
responseSb.append(line);
}
System.out.println("responseSb: "+responseSb);
String resultJson = responseSb.toString();
ObjectMapper objectMapper = new ObjectMapper();
String resultString = objectMapper.writeValueAsString(resultJson);
Map<String, Object> map = objectMapper.readValue(resultJson, Map.class);
String result = (String) map.get("access_token");
br.close();
bw.close();
return result;
}
// 카카오에서 액세스 토큰에서 유저 데이터 받아오기
public Map<String, Object> getUserInfo(String accessToken) throws IOException {
HashMap<String, Object> userInfo = new HashMap<>();
String reqUrl = "https://kapi.kakao.com/v2/user/me";
URL url = new URL(reqUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Authorization", "Bearer " + accessToken);
conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
int responseCode = conn.getResponseCode();
log.info("[KakaoApi.getUserInfo] responseCode : {}", responseCode);
BufferedReader br;
if (responseCode >= 200 && responseCode <= 300) {
br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
} else {
br = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
}
String line = "";
StringBuilder responseSb = new StringBuilder();
while((line = br.readLine()) != null){
responseSb.append(line);
}
String resultJson = responseSb.toString();
ObjectMapper objectMapper = new ObjectMapper();
String resultString = objectMapper.writeValueAsString(resultJson);
Map<String, Object> map = objectMapper.readValue(resultJson, Map.class);
br.close();
return map;
}
}
OpenApiController
//랜덤 이름 생성기
@PostMapping("/randomNickname")
public @ResponseBody String randomNickname(){
List<String> adjectives = Arrays.asList("행복한", "슬픈", "게으른", "슬기로운", "수줍은",
"그리운", "더러운", "섹시한", "배고픈", "배부른", "부자", "재벌", "웃고있는", "깨발랄한",
"못난", "날렵한", "긍정적인", "부정적인", "똑똑한", "멋진");
List<String> names = Arrays.asList("aa", "bb", "cc", "dd", "ee", "ff",
"gg", "hh", "ii", "jj");
String number = (int)(Math.random() * 99)+1 +"";
Collections.shuffle(adjectives);
Collections.shuffle(names);
String adj = adjectives.get(0);
String name = names.get(0);
return adj+"_"+name+"_"+number;
}
APILogin
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>닉네임 변경</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
</head>
<body>
<div class="container mt-5">
<div class="card">
<div class="card-body">
<h3 class="card-title">닉네임을 변경하시겠습니까?</h3>
<form action="<c:url value='/changeNickname'/>" method="post">
<input type="hidden" name="userId" value="${dto.userId}">
<input type="hidden" id="check" value="${dto.nickname}">
<input type="text" id="nickname" name="nickname" value="${dto.nickname}" minlength="2" required autofocus="autofocus" class="form-control mb-3">
<span id="confirmNicknameError" style="color: red;"></span>
<div class="d-grid gap-2 d-md-flex justify-content-md-end mt-3">
<input type="button" id="autoNickname" name="clickType" value="자동 닉네임 생성" class="btn btn-success me-md-2">
<input type="submit" name="clickType" value="변경하기" class="btn btn-primary me-md-2">
<input type="submit" name="clickType" value="그대로 사용하기" class="btn btn-secondary">
</div>
</form>
</div>
</div>
</div>
<script type="text/javascript">
//새로고침, 뒤로가기, 나가기 시 경고창 함수
function f5Control(event){
event.preventDefault();
event.returnValue = '';
}
$(function(){
//새로고침, 뒤로가기, 나가기 시 경고창 함수 출력
window.addEventListener('beforeunload', f5Control);
//뒤로가기 단축키을 누르면 로그인 메인으로 이동(Alt + <- 기능)(브라우저 뒤로가기 버튼은 막히지 않음)
window.history.pushState(null, null, window.location.href);
window.onpopstate = function(event) {
window.history.pushState(null, null, window.location.href);
window.location.href= "<c:url value='/'/>";
};
$(".btn").on("click", function(){
window.removeEventListener('beforeunload', f5Control);
})
//자동 닉네임 생성기
$("#autoNickname").on("click", function(){
var nicknameSpace = $("#nickname");
$.ajax({
type: "POST",
url: "<c:url value='/randomNickname'/>",
success: function (response) {
nicknameSpace.val(response);
},
error: function (error) {
console.error("닉네임 자동 생성기 에러:", error);
}
})
})
//닉네임 중복 확인
var prevNickname = "";
$("#nickname").on("focusout", function() {
var nickname = $("#nickname").val();
var errorSpan = $("#confirmNicknameError");
var check = $("#check").val()
if(check !== nickname){
if (nickname !== prevNickname) {
$.ajax({
type: "POST",
url: "<c:url value='/AjaxNicknameDuplicate'/>",
data: { nickname: nickname },
success: function (response) {
//닉네임이 DB에 저장된 닉네임과 일치하는 데이터가 있을 경우, ajax 출력
if (response === "duplicate") {
errorSpan.text("이미 사용 중인 닉네임입니다.");
} else {
errorSpan.text("");
}
},
error: function (error) {
console.error("닉네임 중복 검사 에러:", error);
}
})
prevNickname = nickname;
};
};
});
//닉네임 중복일 경우, 이벤트 중지
$("form").on("submit", function(){
if($("#confirmNicknameError").text() == "이미 사용 중인 닉네임입니다."){
event.preventDefault();
alert("중복된 닉네임입니다.");
} else {
$("form")[0].submit();
}
})
})
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
</body>
</html>
'팀프로젝트 > SpringBoot' 카테고리의 다른 글
스프링부트 팀플) 20240330 개인정보 제거 (0) | 2024.03.31 |
---|---|
스프링부트 팀플) 20240329 네이버 로그인 (0) | 2024.03.30 |
스프링부트 팀플) 20240327 구글로그인 기능 구현 (0) | 2024.03.27 |
스프링부트 팀플) 20240325 게시판 전체 (0) | 2024.03.25 |
스프링부트 팀플) 20240321_익명 게시판의 익명 유저가 추천/비추천했는지를 저장하고 출력 (0) | 2024.03.21 |