https://notspoon.tistory.com/47
구글 로그인 쉽게 구현하기 3편 - 로그인 구현하기 (SpringBoot + Vue.js)
로그인 처리 플로우는 아래와 같다. 인가 코드 받기 -> 토큰 받기 -> 사용자 정보 가져오기 해당 글에서는 인가 코드 받기는 클라이언트 페이지에서, 토큰 받기와 사용자 정보 가져오기는 서버에
notspoon.tistory.com
아이디저장
자동로그인(아이디 & 비밀번호 저장)
시간남으면 해볼것
> 비밀번호노출 변환
> 로그인창에 베스트셀러 이미지 띄우기 (랜덤으로)
[ 카카오 로그인 연동 ]
1. 클라이언트 쪽에서 로그인을 한다
2. 카카오 서버는 redirect url로 code를 전달해준다
3,4. code를 이용하여 access_token을 발급받는다
5. access_token을 서버로 전송한다
6,7. 서버에서는 받은 access_token을 이용하여 카카오 서버에서 사용자 정보를 받는다
8. 받은 사용자 정보를 이용하여 회원가입 또는 로그인을 진행한다
9. JWT등과 같이 사용자 식별 정보를 클라이언트로 보낸다
카카오 참고블로그
[Spring] 카카오 로그인 API(oAuth 2.0)-(1)
예전에 oAuth 2.0 개념에 대해서 공부를 하고, 지금은 그것에 대한 실전편이라고 보면 된다.개념적인 부분은 이전에 설명했기 때문에 링크로 대체한다.\-> https://velog.io/@shwncho/UMC-7%EC%A3%BC%EC%B0%A8 사
velog.io
[2021.03.17] 인턴 +16 카카오 로그인(REST API) - 정리 완료 (tistory.com)
[2021.03.17] 인턴 +16 카카오 로그인(REST API) - 정리 완료
[2021.03.17] 인턴 +16 카카오 로그인(REST API) - 정리 완료 developers.kakao.com/docs/latest/ko/kakaologin/rest-api Kakao Developers 카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지
injekim97.tistory.com
Kakao Developers
카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.
developers.kakao.com
회원가입한다.
애플리케이션 생성
도메인은 localhost로 지정.
Redirect URI 는 일단 아래의 두개로 설정해줬다.
(Controller에서 아래의 Getmapping으로 login/kakao 를 받아 실행하도록 해줬다.)
아래의 앱 키를 이용해 사용자의 코드를 카카오로 보내고 인증토큰을 받아온다.
받아올 사용자 정보를 지정해줄 수 있는데
일단은 닉네임과 카카오계정(이메일)을 선택동의로 받아온다.
[ 테스트_토큰받아오기 ]
( login.jsp )
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://developers.kakao.com/sdk/js/kakao.js"></script>
<script type="text/javascript">
function kakaoLogin(){
location.href="https://kauth.kakao.com/oauth/authorize?client_id=*****REST API*****&redirect_uri=http://localhost/login/kakao&response_type=code"
//window.open("https://kauth.kakao.com/oauth/authorize?client_id=*****REST API*****&redirect_uri=http://localhost/login&response_type=code",
// "_blank", "width=500, height=500");
}
</script>
</head>
<body>
<h1>로그인화면</h1>
....생략....
<div>
<button type="submit" onclick="kakaoLogin()">
<img src="img/login_kakaoBtn.png" alt="kakaoBtn">
</button>
</div>
</body>
</html>
( LoginController )
@ResponseBody
@GetMapping("/login/kakao")
public String kakaoLogin(@RequestParam(required = false) String code) {
// URL에 포함된 code를 이용하여 액세스 토큰 발급
System.out.println(code);
// UDfF6AQkRhxPqI_Cmx5-QDppFvtEb6Y1BLU0rer3QatyoUBv2RPbc1VuPoEkypVeyjtcywoqJQ8AAAGKWi2e0w
String access_Token = loginService.getKakaoToken(code);
System.out.println(access_Token);
//r3k9TVZu6kFJSlBiskRRWLVic5TIm30JLxUHRFZFCj1zGAAAAYpaLaCn
return "";
}
jsp에서 이동한 페이지의 code를 잡아와 서비스로 보낸다.
( LoginService ) _ gson 사용(import주의) _ gradle 의존성추가
package com.book.web.service;
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.URL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.book.web.dao.LoginDAO;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
@Service
public class LoginService {
@Autowired
private LoginDAO loginDAO;
public String getKakaoToken(String code){
String access_Token="";
String refresh_Token ="";
String reqURL = "https://kauth.kakao.com/oauth/token";
try{
URL url = new URL(reqURL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//POST 요청을 위해 기본값이 false인 setDoOutput을 true로
conn.setRequestMethod("POST");
conn.setDoOutput(true);
//POST 요청에 필요로 요구하는 파라미터 스트림을 통해 전송
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
StringBuilder sb = new StringBuilder();
sb.append("grant_type=authorization_code");
sb.append("&client_id=3ecca13d973c6d11e752a114a1e14922"); // TODO REST_API_KEY 입력
sb.append("&redirect_uri=http://localhost/login/kakao"); // TODO 인가코드 받은 redirect_uri 입력
sb.append("&code=" + code);
bw.write(sb.toString());
bw.flush();
//결과 코드가 200이라면 성공
int responseCode = conn.getResponseCode();
System.out.println("responseCode : " + responseCode);
//요청을 통해 얻은 JSON타입의 Response 메세지 읽어오기
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = "";
String result = "";
while ((line = br.readLine()) != null) {
result += line;
}
System.out.println("response body : " + result);
//Gson 라이브러리에 포함된 클래스로 JSON파싱 객체 생성
JsonParser parser = new JsonParser();
JsonElement element = parser.parse(result);
access_Token = element.getAsJsonObject().get("access_token").getAsString();
refresh_Token = element.getAsJsonObject().get("refresh_token").getAsString();
System.out.println("access_token : " + access_Token);
System.out.println("refresh_token : " + refresh_Token);
br.close();
bw.close();
}catch (IOException e) {
e.printStackTrace();
}
return access_Token;
}
}
build.gradle에 Gson 의존성 추가
// Gson
implementation'com.google.code.gson:gson:2.8.7'
그런데 2.8.7 버전에서는 parse() 메소드 대신 다른걸 사용한단다... 재확인필요
이렇게 해주면 카카로 로그인 버튼을 눌렀을 때 카카오로그인 & 정보제공동의를 하면
(사용자) code를 포함한 아래의 설정주소로 이동한다.
위의 code 부분을 controller에서 잡아서 서비스에 넘기고
서비스에서도 이런저런 처리를 통해 카카오로 보내 인증토큰을 받아온다.
성공하면 아래처럼 응답코드 200과 함께 토큰을 잘 가져온다.
responseCode : 200
response body : {"access_token":"r3k9TVZu6kFJSlBiskRRWLVic5TIm30JLxUHRFZFCj1zGAAAAYpaLaCn","token_type":"bearer","refresh_token":"jGpcwO6bO9Q3cpns8yR08jDdK9UPo3tbVI6HMvOdCj1zGAAAAYpaLaCm","id_token":"eyJraWQiOiI5ZjI1MmRhZGQ1ZjIzM2Y5M2QyZmE1MjhkMTJmZWEiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIzZWNjYTEzZDk3M2M2ZDExZTc1MmExMTRhMWUxNDkyMiIsInN1YiI6IjMwMDI3NTE0ODMiLCJhdXRoX3RpbWUiOjE2OTM3MzAwNTQsImlzcyI6Imh0dHBzOi8va2F1dGgua2FrYW8uY29tIiwibmlja25hbWUiOiJQWU8iLCJleHAiOjE2OTM3NTE2NTQsImlhdCI6MTY5MzczMDA1NCwiZW1haWwiOiJnb2d1czIyOEBoYW5tYWlsLm5ldCJ9.OgfFrEmgHDhOLn23ERlPc3XHELkoQuYSfimR60qaE9innzlY617Xtnyn-9-_NbwLJP4nwJiCHKluNdemqxiz7qxrda2IYE9Wv_6aWvVConMuIWgDPShvSYdy9sdUuYnRo6IjkUW64t8NGTw1BhJFokLQ23XhZb0DWee8ULKPLKklZyZ4DsknESBtKJB1wUlMfJOP8753Wf2TXjtrtTVCy8GO0oehRMi4Ny-CV205_5S1JewvvmdN612oN1y0oMydXgp7jHFimmS9QyEozbWpXQFgsUoOrjQFemads3BrpUfhp49FQK1o4ZnKEdDkQccGaSfmw-UOidYkUW08ByY6zQ","expires_in":21599,"scope":"account_email openid profile_nickname","refresh_token_expires_in":5183999}
access_token : r3k9TVZu6kFJSlBiskRRWLVic5TIm30JLxUHRFZFCj1zGAAAAYpaLaCn
refresh_token : jGpcwO6bO9Q3cpns8yR08jDdK9UPo3tbVI6HMvOdCj1zGAAAAYpaLaCm
위에서 받아온 access_Token을 가지고
서비스에 getKakaoUser() 메소드 실행
@ResponseBody
@GetMapping("/login/kakao")
public String kakaoLogin(@RequestParam(required = false) String code) {
// URL에 포함된 code를 이용하여 액세스 토큰 발급
//System.out.println(code);
String access_Token = loginService.getKakaoToken(code);
//System.out.println(accessToken);
loginService.getKakaoUser(access_Token);
return "";
}
서비스에서 이 token을 카카오로 보내 id와 이메일계정을 가져온다.
public String getKakaoToken(String code){
....생략....
}
public void getKakaoUser(String token){
String reqURL = "https://kapi.kakao.com/v2/user/me";
//access_token을 이용하여 사용자 정보 조회
try {
URL url = new URL(reqURL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Authorization", "Bearer " + token); //전송할 header 작성, access_token전송
//결과 코드가 200이라면 성공
int responseCode = conn.getResponseCode();
System.out.println("responseCode : " + responseCode);
//요청을 통해 얻은 JSON타입의 Response 메세지 읽어오기
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = "";
String result = "";
while ((line = br.readLine()) != null) {
result += line;
}
System.out.println("response body : " + result);
//Gson 라이브러리로 JSON파싱
JsonParser parser = new JsonParser();
JsonElement element = parser.parse(result);
// 받아올값 : profile_nickname(닉네임), kakao_account(이메일)
Long id = element.getAsJsonObject().get("id").getAsLong();
boolean hasEmail = element.getAsJsonObject().get("kakao_account").getAsJsonObject().get("has_email").getAsBoolean();
String email = "";
if (hasEmail) {
email = element.getAsJsonObject().get("kakao_account").getAsJsonObject().get("email").getAsString();
}
System.out.println("id : " + id);
System.out.println("email : " + email);
br.close();
} catch (IOException e) {
e.printStackTrace();
}
성공시 user 정보를 콘솔창에 잘 불러온다.
id : 3002751483
email : gogus228@hanmail.net
닉네임도 가져올 수 있도록 신청해놨는데 왜 못불러올까...다시 해보기
서비스에서 잡은 id & email값을 컨트롤러에서 잡아서 db로 보내 기존 members 의 데이터와 비교할 예정
auto 라는 이름의 필드를 추가해 기본값은 0으로 설정해줬다. 카카오 로그인을 했을 때 auto가 1로 바뀌도록 설정해준다.
카카오로그인 (버튼클릭시)
1. 새로운 계정생성 : 카카오 계정(email)과 일치하는 email이 없고, 있더라도 auto가 0일때
새로 생성한 계정의 id를 session에 저장.
2. 기존계정으로 처리: 카카오 계정(email)과 일치하는 email이 있고, auto가 1일때
해당 계정의 id를 session에 저장.
서비스에서는 kmap 을 생성해서 kid, kemail 값을 담아 내보낸다.
package com.book.web.Login;
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.URL;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
@Service
public class LoginService {
@Autowired
private LoginDAO loginDAO;
// 카카오에 코드전송 & 인증토큰받기
public String getKakaoToken(String code){
String access_Token="";
String refresh_Token ="";
String reqURL = "https://kauth.kakao.com/oauth/token";
try{
URL url = new URL(reqURL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//POST 요청을 위해 기본값이 false인 setDoOutput을 true로
conn.setRequestMethod("POST");
conn.setDoOutput(true);
//POST 요청에 필요로 요구하는 파라미터 스트림을 통해 전송
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
StringBuilder sb = new StringBuilder();
sb.append("grant_type=authorization_code");
sb.append("&client_id=3ecca13d973c6d11e752a114a1e14922"); // TODO REST_API_KEY 입력
sb.append("&redirect_uri=http://localhost/login/kakao"); // TODO 인가코드 받은 redirect_uri 입력
sb.append("&code=" + code);
bw.write(sb.toString());
bw.flush();
//결과 코드가 200이라면 성공
int responseCode = conn.getResponseCode();
//System.out.println("responseCode : " + responseCode);
//요청을 통해 얻은 JSON타입의 Response 메세지 읽어오기
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = "";
String result = "";
while ((line = br.readLine()) != null) {
result += line;
}
System.out.println("response body : " + result);
//Gson 라이브러리에 포함된 클래스로 JSON파싱 객체 생성
JsonParser parser = new JsonParser();
JsonElement element = parser.parse(result);
access_Token = element.getAsJsonObject().get("access_token").getAsString();
refresh_Token = element.getAsJsonObject().get("refresh_token").getAsString();
//System.out.println("access_token : " + access_Token);
//System.out.println("refresh_token : " + refresh_Token);
br.close();
bw.close();
}catch (IOException e) {
e.printStackTrace();
}
return access_Token;
}
// 카카오에 토큰전송 & 사용자정보받기
public Map<String, Object> getKakaoUser(String access_Token){
Map<String, Object> kmap = new HashMap<>();
String reqURL = "https://kapi.kakao.com/v2/user/me";
String email = "";
String nickname = "";
//access_token을 이용하여 사용자 정보 조회
try {
URL url = new URL(reqURL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Authorization", "Bearer " + access_Token); //전송할 header 작성, access_token전송
//결과 코드가 200이라면 성공
int responseCode = conn.getResponseCode();
System.out.println("responseCode : " + responseCode);
//요청을 통해 얻은 JSON타입의 Response 메세지 읽어오기
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = "";
String result = "";
while ((line = br.readLine()) != null) {
result += line;
}
//System.out.println("response body : " + result);
//Gson 라이브러리로 JSON파싱
JsonParser parser = new JsonParser();
JsonElement element = parser.parse(result);
// 받아올값 : profile_nickname(닉네임), kakao_account(이메일)
Long id = element.getAsJsonObject().get("id").getAsLong();
boolean hasEmail = element.getAsJsonObject().get("kakao_account").getAsJsonObject().get("has_email").getAsBoolean();
//boolean hasName = element.getAsJsonObject().get("profile_nickname").getAsJsonObject().get("has_Name").getAsBoolean();
if (hasEmail) {
email = element.getAsJsonObject().get("kakao_account").getAsJsonObject().get("email").getAsString();
}
// if (hasName) {
// nickname = element.getAsJsonObject().get("profile_nickname").getAsJsonObject().get("nickname").getAsString();
// }
System.out.println("id : " + id);
System.out.println("email : " + email);
//System.out.println("nickname : " + nickname);
kmap.put("kid", id);
kmap.put("kemail", email);
//System.out.println(kmap); // {kid=3002751483, kemail=gogus228@hanmail.net}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
return kmap;
}
public int hasKakaoUser(Map<String, Object> kUser) {
return loginDAO.hasKakaoUser(kUser);
}
public void setKakaoUser(Map<String, Object> kUser) {
loginDAO.setKakaoUser(kUser);
}
public Map<String, Object> autoLogin(String suserID) {
return loginDAO.autoLogin(suserID);
}
}
컨트롤러에서는 access_Token를 매개변수로 getKakaoUser() 메소드를 실행시켜
결과값을(kid & kemail) kUser에 담는다.
kUser를 매개변수로 hasKakaoUser() 메소드를 실행시켜 db에 해당 kemail 계정을 가지고 있으면서 auto값이 1인 데이터를 찾는다. (있다면 1, 없다면 0을 리턴)
result = 1 : db에 kakao 계정정보 있다면 로그인진행 (해당 id를 세션에 저장)
result = 1 : db에 kakao 계정정보 없다면 db에 kakao계정(kid & kemail)으로 새로운 계정 생성 (INSERT) & auto 1로 설정
subjoin 페이지를 띄워서 이름&주소 입력 후 db에 저장(UPDATE)시킨다.
JoinController에서 subjoin() 메소드로 처리해줬다.
@GetMapping("/login/kakao")
public String kakaoLogin(@RequestParam(required = false) String code, HttpSession session, Model model) {
// URL에 포함된 code를 이용하여 액세스 토큰 발급
// System.out.println(code);
String access_Token = loginService.getKakaoToken(code);
// System.out.println(access_Token);
Map<String, Object> kUser = loginService.getKakaoUser(access_Token);
// System.out.println(kUser); // {kid=3002751483, kemail=gogus228@hanmail.net}
// kakao 로그인기록 확인
int result = loginService.hasKakaoUser(kUser);
if (kUser != null) { // kakao연결성공
if (result == 1) {
// db에 kakao계정정보 있다면 로그인진행
return "redirect:/";
} else {
loginService.setKakaoUser(kUser); // db에 kakao계정정보 없다면 생성(id&email)
session.setAttribute("mid", kUser.get("kid")); // kid 세션에 저장
model.addAttribute("memail", kUser.get("kemail"));
// 위의 mid, memail은 subjoin에 자동기입
return "/subjoin";
}
} else {
return "redirect:login";
}
}
[ loginMapper ]
<insert id="setKakaoUser" parameterType="Map">
INSERT INTO members (mid, memail, kakao)
VALUES(#{kid} ,#{kemail}, 1)
</insert>
[ JoinController ]
package com.book.web.join;
import org.springframework.beans.factory.annotation.Autowired;
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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class JoinController {
@Autowired
private JoinService joinService;
...생략...
// 로그인연동 추가정보
@GetMapping("/subjoin")
public String subjoin() {
return "subjoin";
}
@PostMapping("/login/subjoin")
public String subjoin(JoinDTO joinDTO) {
joinService.subjoin(joinDTO);
//System.out.println("jsp에서 오는 값 : " + joinDTO);
System.out.println("k계정넣기성공");
return "redirect:/";
}
}
[ joinMapper ]
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.book.web.join.JoinDAO">
...생략...
<update id="subjoin" parameterType="JoinDTO">
UPDATE members
SET mname=#{mname}, maddr=#{maddr}, mpw=#{mid}
WHERE memail=#{memail} and mid=#{mid}
</update>
</mapper>
[ subjoin.jsp ]
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인 연동시 추가정보 입력창</title>
<script src="./js/jquery-3.7.0.min.js"></script>
<script type="text/javascript">
</script>
</head>
<body>
<form action="./subjoin" method="post">
<div class="join-div" align="center">
<div>
<h1>추가정보입력</h1>
</div>
<div>
<div>아이디</div>
<div>
<input type="text" name="mid" id="mid" value="${sessionScope.mid}" />
</div>
</div>
<div>
<div>이름</div>
<div>
<input type="text" name="mname" id="mname" placeholder="이름을 입력해 주세요"/>
</div>
</div>
<div>
<div>주소</div>
<div>
<input type="text" name="maddr" placeholder="주소를 입력해 주세요"/>
</div>
</div>
<div>
<div>이메일</div>
<div>
<input type="text" name="memail" id="memail" value="${memail }" />
</div>
</div>
<div>
<button type="reset">취소</button>
<button type="submit" id="joinjoin">가입</button>
</div>
</div>
</form>
</body>
</html>
'국비과정 > 팀프로젝트' 카테고리의 다른 글
[팀플] DongneBook_ 자동로그인 (0) | 2023.09.27 |
---|---|
[팀플] DongneBook_ 로그인구현 (네이버) (0) | 2023.09.08 |
[DongneBook] 세미프로젝트 관련 자료 (0) | 2023.09.02 |
[Thread] 토이프로젝트 관련 정리 (0) | 2023.08.07 |
[Threads] 토이프로젝트 관련 자료 (0) | 2023.08.04 |