-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Feat/#41/소셜 로그인 카카오 구현
- Loading branch information
Showing
14 changed files
with
336 additions
and
36 deletions.
There are no files selected for viewing
15 changes: 15 additions & 0 deletions
15
src/main/java/space/space_spring/config/RestTemplateConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package space.space_spring.config; | ||
|
||
import org.springframework.boot.web.client.RestTemplateBuilder; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.web.client.RestTemplate; | ||
|
||
@Configuration | ||
public class RestTemplateConfig { | ||
|
||
@Bean | ||
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { | ||
return restTemplateBuilder.build(); | ||
} | ||
} |
75 changes: 75 additions & 0 deletions
75
src/main/java/space/space_spring/controller/OAuthController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package space.space_spring.controller; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import space.space_spring.dto.oAuthInfo.KakaoInfo; | ||
import space.space_spring.entity.User; | ||
import space.space_spring.response.BaseResponse; | ||
import space.space_spring.service.OAuthService; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/oauth") | ||
@Slf4j | ||
public class OAuthController { | ||
|
||
private final OAuthService oAuthService; | ||
|
||
@Value("${oauth.kakao.client.id}") | ||
private String clientId; | ||
|
||
@Value("${oauth.kakao.redirect.uri}") | ||
private String redirectUri; | ||
|
||
@Value("${oauth.kakao.client.secret}") | ||
private String clientSecret; | ||
|
||
/** | ||
* 유저가 카카오 로그인 동의 시 호출될 콜백 함수 | ||
*/ | ||
@GetMapping("/callback/kakao") | ||
public BaseResponse<String> kakaoCallback(@RequestParam(name = "code") String code, HttpServletResponse response) { | ||
|
||
// TODO 1. 인가코드 받기 | ||
// 카카오 인증 서버는 서비스 서버의 Redirect URI로 인가 코드를 전달함 | ||
log.info("인가 코드 = {}", code); | ||
|
||
// TODO 2. 인가코드를 기반으로 토큰(Access Token) 발급 | ||
String accessToken = null; | ||
try { | ||
accessToken = oAuthService.getAccessToken(code, clientId, redirectUri, clientSecret); | ||
} catch (JsonProcessingException e) { | ||
throw new RuntimeException(e); | ||
} | ||
log.info("accessToken = {}", accessToken); | ||
|
||
// TODO 3. 엑세스 토큰를 통해 유저 정보 조회 | ||
KakaoInfo kakaoInfo = null; | ||
try { | ||
kakaoInfo = oAuthService.getKakaoInfo(accessToken); | ||
} catch (JsonProcessingException e) { | ||
throw new RuntimeException(e); | ||
} | ||
log.info("kakaoInfo.getEmail = {}", kakaoInfo.getEmail()); | ||
log.info("kakaoInfo.getNickname = {}", kakaoInfo.getNickName()); | ||
|
||
// TODO 4. 카카오 사용자 정보 확인 | ||
// 유저 email 정보가 db에 없을 시 -> 회원가입 & 로그인 | ||
// 유저 email 정보가 db에 있을 시 -> 로그인 | ||
User userByOAuthInfo = oAuthService.findUserByOAuthInfo(kakaoInfo); | ||
|
||
// TODO 5. 카카오 로그인 유저에게 jwt 발급 | ||
String jwtOAuthLogin = oAuthService.provideJwtToOAuthUser(userByOAuthInfo); | ||
response.setHeader("Authorization", "Bearer " + jwtOAuthLogin); | ||
log.info("jwtOAuthLogin = {}", jwtOAuthLogin); | ||
|
||
return new BaseResponse<>("카카오 로그인 성공"); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
src/main/java/space/space_spring/controller/TestOAuthController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package space.space_spring.controller; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Controller; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
|
||
@Controller | ||
@RequestMapping("/oauth") | ||
@Slf4j | ||
public class TestOAuthController { | ||
|
||
@Value("${oauth.kakao.client.id}") | ||
private String clientId; | ||
|
||
@Value("${oauth.kakao.redirect.uri}") | ||
private String redirectUri; | ||
|
||
/** | ||
* 카카오 로그인 요청 처리 | ||
* 카카오 인증 서버의 인증 및 동의 요청 페이지로 redirect | ||
*/ | ||
@GetMapping("/kakao") | ||
public String kakaoConnect() { | ||
StringBuffer url = new StringBuffer(); | ||
url.append("https://kauth.kakao.com/oauth/authorize?"); | ||
url.append("client_id="+clientId); | ||
url.append("&redirect_uri="+redirectUri); | ||
url.append("&response_type=code"); | ||
return "redirect:" + url.toString(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
src/main/java/space/space_spring/dto/oAuthDto/KakaoLoginDto.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package space.space_spring.dto.oAuthDto; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
@AllArgsConstructor | ||
public class KakaoLoginDto { | ||
|
||
private String email; | ||
|
||
private String nickname; | ||
|
||
public void saveKakaoLoginDto(String email, String nickname) { | ||
this.email = email; | ||
this.nickname = nickname; | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
src/main/java/space/space_spring/dto/oAuthInfo/KakaoInfo.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package space.space_spring.dto.oAuthInfo; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
public class KakaoInfo { | ||
|
||
private String nickName; | ||
|
||
private String email; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,5 +8,4 @@ | |
public class PostUserSignupResponse { | ||
|
||
private Long userId; | ||
private String jwt; // 이게 있어야할까? | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
src/main/java/space/space_spring/entity/enumStatus/UserSignupType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package space.space_spring.entity.enumStatus; | ||
|
||
import lombok.Getter; | ||
|
||
@Getter | ||
public enum UserSignupType { | ||
LOCAL("local"), | ||
KAKAO("kakao"), | ||
NAVER("naver"), | ||
GOOGLE("google"); | ||
|
||
private String signupType; | ||
|
||
UserSignupType(String signupType) { | ||
this.signupType = signupType; | ||
} | ||
} |
112 changes: 112 additions & 0 deletions
112
src/main/java/space/space_spring/service/OAuthService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package space.space_spring.service; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.HttpEntity; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpMethod; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import org.springframework.util.LinkedMultiValueMap; | ||
import org.springframework.util.MultiValueMap; | ||
import org.springframework.web.client.RestTemplate; | ||
import space.space_spring.dao.UserDao; | ||
import space.space_spring.dto.oAuthInfo.KakaoInfo; | ||
import space.space_spring.entity.User; | ||
import space.space_spring.jwt.JwtLoginProvider; | ||
import space.space_spring.util.user.UserUtils; | ||
|
||
import java.util.UUID; | ||
|
||
import static space.space_spring.entity.enumStatus.UserSignupType.KAKAO; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class OAuthService { | ||
|
||
private final UserUtils userUtils; | ||
private final JwtLoginProvider jwtLoginProvider; | ||
|
||
/** | ||
* 카카오 인증 서버가 전달해준 유저의 인가코드로 토큰 발급 요청 | ||
*/ | ||
public String getAccessToken(String code, String clientId, String redirectUri, String clientSecret) throws JsonProcessingException { | ||
|
||
// TODO 1. HTTP Header 생성 | ||
HttpHeaders headers = new HttpHeaders(); | ||
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8"); | ||
|
||
// TODO 2. HTTP Body 생성 | ||
MultiValueMap<String, String> body = new LinkedMultiValueMap<>(); | ||
body.add("grant_type", "authorization_code"); | ||
body.add("client_id", clientId); | ||
body.add("redirect_uri", redirectUri); | ||
body.add("code", code); | ||
body.add("client_secret", clientSecret); | ||
|
||
// TODO 3. 카카오 인증 서버로 HTTP 요청 보내기 | ||
HttpEntity<MultiValueMap<String, String>> kakaoTokenRequest = new HttpEntity<>(body, headers); | ||
RestTemplate rt = new RestTemplate(); | ||
ResponseEntity<String> response = rt.exchange( | ||
"https://kauth.kakao.com/oauth/token", | ||
HttpMethod.POST, | ||
kakaoTokenRequest, | ||
String.class | ||
); | ||
|
||
// TODO 4. 카카오 인증 서버로부터의 HTTP 응답 (JSON) -> 액세스 토큰만 파싱 | ||
String responseBody = response.getBody(); | ||
ObjectMapper objectMapper = new ObjectMapper(); | ||
JsonNode jsonNode = objectMapper.readTree(responseBody); | ||
|
||
return jsonNode.get("access_token").asText(); | ||
} | ||
|
||
/** | ||
* 카카오 인증 서버로부터 받은 access token 으로 해당 유저의 사용자 정보 가져오기 | ||
*/ | ||
public KakaoInfo getKakaoInfo(String accessToken) throws JsonProcessingException { | ||
|
||
// TODO 1. HTTP Header 생성 | ||
HttpHeaders headers = new HttpHeaders(); | ||
headers.add("Authorization", "Bearer " + accessToken); | ||
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8"); | ||
|
||
// TODO 2. 카카오 api 서버로 HTTP 요청 보내기 | ||
HttpEntity<MultiValueMap<String, String>> kakaoUserInfoRequest = new HttpEntity<>(headers); | ||
RestTemplate rt = new RestTemplate(); | ||
ResponseEntity<String> response = rt.exchange( | ||
"https://kapi.kakao.com/v2/user/me", | ||
HttpMethod.POST, | ||
kakaoUserInfoRequest, | ||
String.class | ||
); | ||
|
||
// TODO 3. responseBody에 있는 정보 꺼내기 | ||
String responseBody = response.getBody(); | ||
ObjectMapper objectMapper = new ObjectMapper(); | ||
JsonNode jsonNode = objectMapper.readTree(responseBody); | ||
|
||
// 유저의 이메일, nickname 정보 get | ||
String email = jsonNode.get("kakao_account").get("email").asText(); | ||
String nickname = jsonNode.get("properties").get("nickname").asText(); | ||
|
||
return new KakaoInfo(nickname, email); | ||
} | ||
|
||
@Transactional | ||
public User findUserByOAuthInfo(KakaoInfo kakaoInfo) { | ||
String email = kakaoInfo.getEmail(); | ||
String nickname = kakaoInfo.getNickName(); | ||
|
||
// 카카오 서버로부터 얻은 정보로 회원가입 or 로그인 | ||
return userUtils.findOrCreateUserForOAuthInfo(email, nickname, KAKAO); | ||
} | ||
|
||
public String provideJwtToOAuthUser(User userByOAuthInfo) { | ||
return jwtLoginProvider.generateToken(userByOAuthInfo); | ||
} | ||
} |
Oops, something went wrong.