session 을 이용한 Kakao로그인-2.구현
session 을 이용한 kakao로그인- 1.Kakao Developers 설정
먼저, Kakao Developers 에 접속 해서 애플리케이션을 추가 해야한다. https://developers.kakao.com/ Kakao Developers카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기
zee0541.tistory.com
kakao developer 설정을 안료 했으면
코드를 작성하면 된다.
저는 Spring Framework 를 사용하였습니다. 참고 부탁드립니다.
KakaoVO
@Data // kakaoVO 는 kakao 사용자 정보를 담는데 사용되는 것이다
public class KakaoVO {
private long kakaoId; // kakao 사용자 고유 ID
private String nickname; // 사용자 닉네임
private String kakaoEmail; // 사용자 이메일
}
UserMapper
public interface UserMapper {
public void insertUser(UserVO vo); //회원가입 처리
public UserVO login(Map<String, String> params); //로그인 처리
public int checkEmail(String email); //이메일 중복체크
public boolean updateUser(UserVO vo); //회원정보 수정(비밀번호 미포함)
public boolean updateUserPw(UserVO vo); //회원정보 수정(비밀번호 포함)
public boolean deleteUser(long uno); //회원 탈퇴
}
기존 UserMapper 에서 2개의 인터페이스 메소드 를 추가해야 한다.
//사용자 이름(username)을 기준으로 해당 사용자의 정보르 검색
public UserVO findByUsername(String username);
// 이메일 주소(email)를 기준으로 해당 사용자릐 정보를 검색
public UserVO searchUsersByEmail(String email);
KakaoService 전체 코드
@Service
@Log4j
public class KakaService {
@Autowired
private UserMapper usermapper;
public String getToken(String code) throws IOException {
String host = "https://kauth.kakao.com/oauth/token";
HttpURLConnection urlConnection = null;
String token = "";
try {
URL url = new URL(host);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setDoOutput(true);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(urlConnection.getOutputStream()))) {
StringBuilder sb = new StringBuilder();
sb.append("grant_type=authorization_code");
sb.append("&client_id=REST KEY"); // 앱키 -> 본인 REST KEY 작성
sb.append("&redirect_uri=Redirect UR"); // 등록한 Redirect URI 작성
sb.append("&code=").append(code);
bw.write(sb.toString());
bw.flush();
}
int responseCode = urlConnection.getResponseCode();
log.info("responseCode = " + responseCode);
if (responseCode == 200) { // 성공한 경우만 처리
StringBuilder result = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()))) {
String line;
while ((line = br.readLine()) != null) {
result.append(line);
}
}
log.info("result = " + result.toString());
JsonParser parser = new JsonParser();
JsonObject elem = parser.parse(result.toString()).getAsJsonObject();
token = elem.get("access_token").getAsString();
} else {
log.error("Failed to get token. Response code: " + responseCode);
}
} catch (IOException e) {
log.error("Error occurred: " + e.getMessage(), e);
throw e;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return token;
}
public KakaoVO userInfo(String access_Token) throws IOException {
log.info("사용자 정보 가져오기 시작------------------------");
KakaoVO userInfo = new KakaoVO();
String reqURl = "https://kapi.kakao.com/v2/user/me";
try {
URL url = new URL(reqURl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Authorization", "Bearer " + access_Token);
int responseCode = conn.getResponseCode();
log.info("응답 코드: " + responseCode);
if (responseCode == 200) { // 성공한 경우에만 처리
StringBuilder result = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
String line;
while ((line = br.readLine()) != null) {
result.append(line);
}
}
log.info("카카오 사용자 정보 응답 내용: " + result.toString()); // 전체 응답 로그 출력
JsonParser parser = new JsonParser();
JsonObject element = parser.parse(result.toString()).getAsJsonObject();
if (element.has("properties")) {
JsonObject properties = element.get("properties").getAsJsonObject();
String nickname = properties.has("nickname") ? properties.get("nickname").getAsString() : null;
userInfo.setNickname(nickname);
log.info("nickname: " + nickname);
} else {
log.warn("properties 객체가 존재하지 않습니다.");
}
if (element.has("kakao_account")) {
JsonObject kakao_account = element.get("kakao_account").getAsJsonObject();
String email = kakao_account.has("email") ? kakao_account.get("email").getAsString() : null;
userInfo.setKakaoEmail(email);
log.info("email: " + email);
} else {
log.warn("kakao_account 객체가 존재하지 않습니다.");
}
long id = element.has("id") ? element.get("id").getAsLong() : -1;
userInfo.setKakaoId(id);
log.info("id: " + id);
} else {
log.error("사용자 정보를 가져오는 데 실패했습니다. 응답 코드: " + responseCode);
}
} catch (Exception e) {
log.error("사용자 정보 가져오는 중 예외 발생: " + e.getMessage(), e);
}
return userInfo;
}
public void setAuthenticationWithSession(String email, HttpSession session) {
// 데이터베이스에서 사용자 정보 가져오기
UserVO kakaouser = usermapper.findByUsername(email);
// 사용자 정보를 세션에 저장하기
session.setAttribute("user", kakaouser);
// 추가로 필요한 데이터를 세션에 저장할 수 있음
session.setAttribute("isAuthenticated", true);
}
}
메서드 만다 설명을 하겠습니다.
//Kakao OAuth2 인증을 통해 Access Token 응 얻는 메소드 이다
public String getToken(String code) throws IOException {
// Kakao에서 Access Token을 발급받기 위한 엔드포인트
String host = "https://kauth.kakao.com/oauth/token";
HttpURLConnection urlConnection = null;
String token = "";
try {
URL url = new URL(host);
urlConnection = (HttpURLConnection) url.openConnection();
// post 요청으로 설정
urlConnection.setRequestMethod("POST");
//요청 본문에 데이터를 쓸 수 있도록 설정
urlConnection.setDoOutput(true);
//요청 본문의 데이터 타입을 설정
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(urlConnection.getOutputStream()))) {
StringBuilder sb = new StringBuilder();
sb.append("grant_type=authorization_code");
sb.append("&client_id=REST KEY"); // 앱키 -> 본인 REST KEY 작성
sb.append("&redirect_uri=Redirect URI"); // 등록한 Redirect URI 작성
sb.append("&code=").append(code);
bw.write(sb.toString());
bw.flush();
}
// HTTP 응답 코드가 200인 경우에만 Access Token 처리를 진행
int responseCode = urlConnection.getResponseCode();
log.info("responseCode = " + responseCode);
if (responseCode == 200) { // 성공한 경우만 처리
//BufferedReader를 사용해 Kakao 서버로부터 응답받은 데이터를 읽어온다
StringBuilder result = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()))) {
String line;
while ((line = br.readLine()) != null) {
result.append(line);
}
}
log.info("result = " + result.toString());
// access_token : JSON 응답에서 Accsee Token 값을 추출한다.
// JsonParser: JSON 문자열을 파싱하여 JsonObject로 변환합니다.
JsonParser parser = new JsonParser();
JsonObject elem = parser.parse(result.toString()).getAsJsonObject();
token = elem.get("access_token").getAsString();
} else {
log.error("Failed to get token. Response code: " + responseCode);
}
} catch (IOException e) {
log.error("Error occurred: " + e.getMessage(), e);
throw e;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return token;
}
//String access_Token -> 카카오 인증 서버에서 발급된 엑세스
// KakaoVO -> 사용자의 정보를 담은 데이터 객체
public KakaoVO userInfo(String access_Token) throws IOException {
log.info("사용자 정보 가져오기 시작------------------------");
KakaoVO userInfo = new KakaoVO();
// 사용자 정보를 요청하는 카카오 API의 엔드포인드 URL
String reqURl = "https://kapi.kakao.com/v2/user/me";
try {
//HttpURLConnection 객체를 통해 API 서버와 연결
// GET 요청 한다.
// Authorization 헤더에 Bearer 토큰 형식으로 액세스 토큰을 추가. (OAuth 인증 방식)
URL url = new URL(reqURl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Authorization", "Bearer " + access_Token);
// 서버에서 반환한 HTTP 응답 코드를 확인
int responseCode = conn.getResponseCode();
log.info("응답 코드: " + responseCode);
if (responseCode == 200) { // 성공한 경우에만 처리
// 서버에서 반환한 JSON 응답 데이터를 읽어서 StringBuilder에 저장.
StringBuilder result = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
String line;
while ((line = br.readLine()) != null) {
result.append(line);
}
}
log.info("카카오 사용자 정보 응답 내용: " + result.toString()); // 전체 응답 로그 출력
//JSON 데이터를 파싱하여 Java에서 처리 가능한 객체로 변환.
JsonParser parser = new JsonParser();
JsonObject element = parser.parse(result.toString()).getAsJsonObject();
if (element.has("properties")) {
// 사용자 프로필 정보가 담긴 properties 객체에서 닉네임을 추출.
JsonObject properties = element.get("properties").getAsJsonObject();
String nickname = properties.has("nickname") ? properties.get("nickname").getAsString() : null;
userInfo.setNickname(nickname);
log.info("nickname: " + nickname);
} else {
log.warn("properties 객체가 존재하지 않습니다.");
}
if (element.has("kakao_account")) {
//사용자 계정 정보가 담긴 kakao_account 객체에서 이메일을 추출.
JsonObject kakao_account = element.get("kakao_account").getAsJsonObject();
String email = kakao_account.has("email") ? kakao_account.get("email").getAsString() : null;
userInfo.setKakaoEmail(email);
log.info("email: " + email);
} else {
log.warn("kakao_account 객체가 존재하지 않습니다.");
}
//카카오 사용자의 고유 ID를 추출.
long id = element.has("id") ? element.get("id").getAsLong() : -1;
userInfo.setKakaoId(id);
log.info("id: " + id);
} else {
log.error("사용자 정보를 가져오는 데 실패했습니다. 응답 코드: " + responseCode);
}
} catch (Exception e) {
log.error("사용자 정보 가져오는 중 예외 발생: " + e.getMessage(), e);
}
return userInfo;
}
public void setAuthenticationWithSession(String email, HttpSession session) {
// 데이터베이스에서 사용자 정보 가져오기
UserVO kakaouser = usermapper.findByUsername(email);
// 사용자 정보를 세션에 저장하기
session.setAttribute("user", kakaouser);
// 추가로 필요한 데이터를 세션에 저장할 수 있음
session.setAttribute("isAuthenticated", true);
}
UserService
public interface UserService {
public void register(UserVO vo); //회원가입
public boolean checkEmail(String email); //회원가입 이메일 중복체크
public UserVO login(String username, String password); //로그인처리 (DB회원 조회)
public boolean updateUser(UserVO vo); //회원정보 수정 (비밀번호 포함X)
public boolean updateUserPw(UserVO vo); //회원정보 수정 (비밀번호 포함O)
public boolean deleteUserByEmail(long uno); //회원 탈퇴
}
기존 UserService 에서 메서드 를 추가해야한다.
public UserVO findByUsername(String username);
UserVO searchUsersByEmail(String email)
UserServicelmple 추가
@Override
public UserVO findByUsername(String username) {
return mapper.findByUsername(username);
}
@Override
public UserVO searchUsersByEmail(String email) {
log.info("이메일로 사용자 검색 -----> " + email);
UserVO userVO = mapper.searchUsersByEmail(email);
if (userVO != null) {
log.info("이메일로 찾은 사용자: " + userVO);
} else {
log.warn("해당 이메일로 사용자를 찾을 수 없습니다.");
}
return userVO;
}
UserMapper.xml 추가
<select id="findByUsername" parameterType="java.lang.String" resultType="org.zerock.domain.UserVO">
select * from users where name = #{name, jdbcType=VARCHAR}
</select>
<select id="searchUsersByEmail" parameterType="java.lang.String" resultType="org.zerock.domain.UserVO">
SELECT * FROM users WHERE email = #{email, jdbcType=VARCHAR}
</select>
Cotroller 같은 경우 KakaoController 클래스 를 만들거나 아니면
UserController 에 추가 해도 된다.
저는 따로 class 를 만들지 않고 UserController 에 추가했다.
UserController
@GetMapping("/login/oauth2/code/kakao")
public String login(@RequestParam("code") String code, HttpSession session, HttpServletRequest request,
RedirectAttributes rttr, Model model) throws IOException {
log.info("코드 받음: " + code);
String url = "";
// Access Token 발급
String access_token = kakaoservice.getToken(code);
log.info("발급된 액세스 토큰: " + access_token);
// 사용자 정보 가져오기
KakaoVO userInfo = kakaoservice.userInfo(access_token);
model.addAttribute("code", code);
model.addAttribute("access_token", access_token);
model.addAttribute("userInfo", userInfo);
String email = userInfo.getKakaoEmail();
String nickname = userInfo.getNickname();
log.info("사용자 이메일: " + email);
UserVO users = usersService.searchUsersByEmail(email); // 이메일로 사용자 검색
if (users != null) {
// 기존 사용자 처리 로직
session = request.getSession();
log.info("기존 사용자 세션 설정: " + session);
session.setAttribute("nickname", userInfo.getNickname());
session.setAttribute("access_token", access_token);
session.setAttribute("KakaoId", userInfo.getKakaoId());
session.setAttribute("email", userInfo.getKakaoEmail());
log.info("nickname: " + userInfo.getNickname());
url = "redirect:/"; // 홈 화면으로 이동
} else {
// 신규 사용자 처리 로직
log.info("사용자가 존재하지 않습니다. 신규 사용자 등록 필요");
rttr.addAttribute("userEmail", email);
rttr.addAttribute("nickname", nickname);
url = "redirect:/user/join"; // 회원가입 페이지로 이동
}
log.info("리다이렉트 URL: " + url);
return url;
}
마지막 으로
<a href="https://kauth.kakao.com/oauth/authorize?client_id=REST KEY&redirect_uri=http:Redirect URL&response_type=code&prompt=login">
<img src="${pageContext.request.contextPath}/resources/img/kakao_login_large_wide.png" alt="카카오 로그인">
</a>
작성하면
카카오 이미지 가 나올것이다
이미지 를 클릭하면
카카오 로그인 페이지 로 이동한다.
인증 까지 했다.
그런데 회원가입 페이지 로 이동한다.
왜?? 로그인 도 성공했는데 회원가입 페이지로 넘어갈까 생각하다가 알아냈다.
이유는 DB user테이블 에 설정 값 이메일 이랑 카카오계정 이메일 이랑 일치 해야지 로그인 성공이 된다,
(Conttoller 에서 일치해야 로그인 성공할수 있도록 구현했다...)
그럼, 카카오 계정 이메일 일치하게 DB user테이블 에 값 을 설정 하고 다시 해보겠다.
다시 카카오 로그인 페이지 로 이동되고
2단계 인증 까지 성공하면 Spring 에 console 창에 이렇게 나오면 kakao로그인 이 성공한거다.
이번에는 session 으로 구현을 했는데 Security 으로 연동해서 구현하는 것 보다 간단했는데 별 차이는 없었다.
글을 읽으시고 설명이 더 필요하다고 하시면 댓글로 남겨주시면 수정하겠습니다!