https://notspoon.tistory.com/47
아이디저장
자동로그인(아이디 & 비밀번호 저장)
시간남으면 해볼것
> 비밀번호노출 변환
> 로그인창에 베스트셀러 이미지 띄우기 (랜덤으로)
[ 카카오 로그인 연동 ]
1. 클라이언트 쪽에서 로그인을 한다
2. 카카오 서버는 redirect url로 code를 전달해준다
3,4. code를 이용하여 access_token을 발급받는다
5. access_token을 서버로 전송한다
6,7. 서버에서는 받은 access_token을 이용하여 카카오 서버에서 사용자 정보를 받는다
8. 받은 사용자 정보를 이용하여 회원가입 또는 로그인을 진행한다
9. JWT등과 같이 사용자 식별 정보를 클라이언트로 보낸다
카카오 참고블로그
[2021.03.17] 인턴 +16 카카오 로그인(REST API) - 정리 완료 (tistory.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 |