Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: 카카오 로그인 추가 #65

Merged
merged 3 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import com.HHive.hhive.domain.user.UserDetailsImpl;
import com.HHive.hhive.domain.user.dto.*;
import com.HHive.hhive.domain.user.entity.User;
import com.HHive.hhive.domain.user.service.KakaoService;
import com.HHive.hhive.domain.user.service.UserService;
import com.HHive.hhive.global.common.CommonResponse;
import com.HHive.hhive.global.jwt.JwtUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
Expand All @@ -21,15 +23,15 @@
public class UserController {

private final UserService userService;

private final JwtUtil jwtUtil;
private final KakaoService kaKaoService;

@PostMapping("/signup")
public ResponseEntity<CommonResponse<Void>> signup(
@Valid @RequestBody UserSignupRequestDTO requestDTO) {

userService.signup(requestDTO);
return ResponseEntity.ok().body(CommonResponse.of(HttpStatus.CREATED.value(),"회원가입 성공", null));
return ResponseEntity.ok().body(CommonResponse.of(HttpStatus.CREATED.value(), "회원가입 성공", null));
}

@PostMapping("/login")
Expand Down Expand Up @@ -97,5 +99,19 @@ public ResponseEntity<CommonResponse<Void>> deleteUser(

//TODO: 카테고리 선택

//TODO: 카카오 로그인
@GetMapping("/kakao/callback")
public String kakaoLogin(@RequestParam String code, HttpServletResponse response) throws JsonProcessingException {

// jwt 토큰 반환
String token = kaKaoService.kakaoLogin(code);

// 반환된 토큰을 쿠키에 넣음
Cookie cookie = new Cookie(jwtUtil.JWT_COOKIE_NAME, token);
cookie.setPath("/");

// + response 객체에 넣음
response.addCookie(cookie);

return "redirect:/";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.HHive.hhive.domain.user.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class KakaoUserInfoDTO {

private Long id;
private String nickname;
private String email;

public KakaoUserInfoDTO(Long id, String nickname, String email) {
this.id = id;
this.nickname = nickname;
this.email = email;
}
}
114 changes: 114 additions & 0 deletions src/main/java/com/HHive/hhive/domain/user/service/KakaoService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package com.HHive.hhive.domain.user.service;

import com.HHive.hhive.domain.user.dto.KakaoUserInfoDTO;
import com.HHive.hhive.domain.user.repository.UserRepository;
import com.HHive.hhive.global.jwt.JwtUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;

@Slf4j(topic = "KAKAO Login")
@Service
@RequiredArgsConstructor
public class KakaoService {

private final PasswordEncoder passwordEncoder;
private final UserRepository userRepository;
private final RestTemplate restTemplate;
private final JwtUtil jwtUtil;

public String kakaoLogin(String code) throws JsonProcessingException {

String accessToken = getToken(code);
KakaoUserInfoDTO kakaoUserInfoDTO = getKakaoUserInfo(accessToken);

return null;
}

// 액세스 토큰 요청
private String getToken(String code) throws JsonProcessingException {

log.info("인가코드 : " + code);

URI uri = UriComponentsBuilder
.fromUriString("https://kauth.kakao.com")
.path("/oauth/token")
.encode()
.build()
.toUri();

HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", "${social.api.key.google}");
body.add("redirect_uri", "http://15.165.158.12/api/users/kakao/callback");
body.add("code", code);

RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri)
.headers(headers)
.body(body);

// 카카오 서버로 HTTP 요청 보내기
ResponseEntity<String> response = restTemplate.exchange(
requestEntity,
String.class
);

// 액세스 토큰 파싱
JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
return jsonNode.get("access_token").asText();
}

// 사용자 정보 요청
private KakaoUserInfoDTO getKakaoUserInfo(String accessToken) throws JsonProcessingException {

log.info("acccessToken : " + accessToken);

URI uri = UriComponentsBuilder
.fromUriString("https://kauth.kakao.com")
.path("/oauth/token")
.encode()
.build()
.toUri();

HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer%" + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
.post(uri)
.headers(headers)
.body(new LinkedMultiValueMap<>());

ResponseEntity<String> response = restTemplate.exchange(
requestEntity,
String.class
);

JsonNode jsonNode = new ObjectMapper().readTree(response.getBody());
Long id = jsonNode.get("id").asLong();
String nickname = jsonNode.get("properties")
.get("nickname").asText();
String email = jsonNode.get("kakao_account")
.get("email").asText();

log.info("카카오 사용자 정보: " + id + ", " + nickname + ", " + email);
return new KakaoUserInfoDTO(id, nickname, email);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.HHive.hhive.global.config;

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

import java.time.Duration;

@Configuration
public class RestTemplateConfig {

@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {

// 외부 API 호출 시 30초가 지나도 응답이 없을 때 강제 종료
return restTemplateBuilder
.setConnectTimeout(Duration.ofSeconds(30))
.setReadTimeout(Duration.ofSeconds(30))
.build();
}
}
14 changes: 12 additions & 2 deletions src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,18 @@ spring:

jpa:
hibernate:
ddl-auto: none
ddl-auto: update
properties:
hibernate:
show_sql: true
open-in-view: false
open-in-view: false

jwt:
secret:
key: ${JWT_SECRET_KEY}


social:
api:
key:
kakao: ${KAKAO_API_KEY}
7 changes: 6 additions & 1 deletion src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,9 @@ spring:

jwt:
secret:
key: ${JWT_SECRET_KEY}
key: ${JWT_SECRET_KEY}

social:
api:
key:
kakao: ${KAKAO_API_KEY}
7 changes: 6 additions & 1 deletion src/main/resources/application-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,9 @@ spring:

jwt:
secret:
key: ${JWT_SECRET_KEY}
key: ${JWT_SECRET_KEY}

social:
api:
key:
kakao: ${KAKAO_API_KEY}
Loading