Skip to content

Commit

Permalink
Merge pull request #14 from AlongTheBlue/develop
Browse files Browse the repository at this point in the history
[Feat] 소셜 로그인 - 카카오 로그인 구현
  • Loading branch information
MoonInbae authored Oct 1, 2024
2 parents 2169589 + 4eeee41 commit c2d512b
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.alongtheblue.alongtheblue_server.domain.oauth.api;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.alongtheblue.alongtheblue_server.domain.oauth.application.KakaoService;
import org.alongtheblue.alongtheblue_server.domain.userInfo.application.UserInfoService;
import org.alongtheblue.alongtheblue_server.domain.userInfo.domain.UserInfo;
import org.alongtheblue.alongtheblue_server.global.common.response.ApiResponse;
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 java.util.Map;

@Tag(name = "회원가입/로그인 API", description = "회원가입, 로그인, 사용자 정보 조회 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/oauth")
public class OauthController {
private final KakaoService kakaoService;
private final UserInfoService userinfoService;

@Operation(summary="리다이렉션 url 반환 API")
@GetMapping("/login")
public ApiResponse<String> getKakaoLoginUrl() {
return kakaoService.getKakaoLoginUrl();
}

@Operation(summary="로그인 후 콜백 메소드")
@GetMapping("/callback")
public ApiResponse<String> kakaoLogin(@RequestParam("code") String code) {
System.out.println("0000000000000000000000000");
String accessToken = kakaoService.getAccessToken(code);
Map<String, Object> userInfo = kakaoService.getUserInfo(accessToken);
return ApiResponse.ok("사용자의 user ID를 성공적으로 조회했습니다.", userinfoService.retrieveOrCreateUser(userInfo).getData().getUid());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package org.alongtheblue.alongtheblue_server.domain.oauth.application;

import lombok.RequiredArgsConstructor;
import org.alongtheblue.alongtheblue_server.global.common.response.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.reactive.function.client.WebClient;

import java.util.Map;

@Service
@Transactional
@RequiredArgsConstructor
public class KakaoService {

@Value("${security.oauth2.client.registration.kakao.client-id}")
private String clientId;

@Value("${security.oauth2.client.registration.kakao.client-secret}")
private String clientSecret;

@Value("${security.oauth2.client.registration.kakao.redirect-uri}")
private String redirectUri;

@Value("${security.oauth2.client.provider.kakao.authorization-uri}")
private String authUrl;

@Value("${security.oauth2.client.provider.kakao.token-uri}")
private String tokenUri;

@Value("${security.oauth2.client.provider.kakao.user-info-uri}")
private String userInfoUri;

private final WebClient webClient;

@Autowired
public KakaoService(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder.baseUrl("https://kauth.kakao.com").build();
}

public ApiResponse<String> getKakaoLoginUrl(){
String kakaoAuthUrl = String.format("%s?client_id=%s&redirect_uri=%s&response_type=code",
authUrl, clientId, redirectUri);
return ApiResponse.ok("Redirect to Kakao", kakaoAuthUrl);
}

public String getAccessToken(String code) {
return webClient.post()
.uri(uriBuilder -> uriBuilder.path("/oauth/token")
.queryParam("grant_type", "authorization_code")
.queryParam("client_id", clientId)
.queryParam("client_secret", clientSecret)
.queryParam("redirect_uri", redirectUri)
.queryParam("code", code)
.build())
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.retrieve()
.bodyToMono(Map.class)
.map(response -> (String) response.get("access_token"))
.block(); // 비동기 처리 시 block() 사용 자제, 여기서는 예제 단순화를 위해 사용
}

public Map<String, Object> getUserInfo(String accessToken) {
return webClient.mutate()
.baseUrl("https://kapi.kakao.com")
.build()
.get()
.uri("/v2/user/me")
// .uri("https://kapi.kakao.com/v2/user/me")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken)
.retrieve()
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {})
.block(); // 비동기 처리 시 block() 사용 자제, 여기서는 예제 단순화를 위해 사용
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@ public ApiResponse<UserInfo> createUserInfo(@Valid @RequestBody CreateUserInfoDt
}
@Operation(summary = "UserInfo 전체 조회 API")
@GetMapping("/userInfo")
public ApiResponse<List<UserInfo>> retrieveAllFaq() {
public ApiResponse<List<UserInfo>> retrieveAllUserInfo() {
return userInfoService.retrieveAllUserInfo();
}

@Operation(summary = "ID로 UserInfo 조회 API")
@GetMapping("/user")
public ApiResponse<UserInfo> retrieveUserInfo(@RequestHeader("Authorization") String uid) {
return userInfoService.retrieveUserInfo(uid);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
import org.alongtheblue.alongtheblue_server.domain.userInfo.domain.UserInfo;
import org.alongtheblue.alongtheblue_server.domain.userInfo.dto.CreateUserInfoServiceDto;
import org.alongtheblue.alongtheblue_server.global.common.response.ApiResponse;
import org.alongtheblue.alongtheblue_server.global.error.ErrorCode;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
@Transactional
Expand All @@ -29,4 +32,44 @@ public ApiResponse<List<UserInfo>> retrieveAllUserInfo() {
}
return ApiResponse.ok("UserInfo 목록을 성공적으로 조회했습니다.", userInfoList);
}

public ApiResponse<UserInfo> retrieveOrCreateUser(Map<String, Object> userInfo) {
String uuid = userInfo.get("id").toString();
Optional<UserInfo> optionalUser = userInfoRepository.findByUid(uuid);
if(optionalUser.isEmpty()) {
System.out.println("11111111111111");
return this.createUser(userInfo);
}
UserInfo savedUser = optionalUser.get();
return ApiResponse.ok("사용자 정보를 성공적으로 조회했습니다.", savedUser);
// User user = userRepository.findById(uuid).orElseGet(() -> {
// User newUser = new User();
// newUser.setId(uuid);
// newUser.setEmail(email);
// newUser.setNickname(name);
// return userRepository.save(newUser);
// });
}

private ApiResponse<UserInfo> createUser(Map<String, Object> userInfo) {
Map<String, Object> kakao_account = (Map<String, Object>) userInfo.get("kakao_account");
Map<String, Object> profile = (Map<String, Object>) kakao_account.get("profile");
String uuid = userInfo.get("id").toString();
String name = (String) profile.get("nickname");
System.out.println(name);
UserInfo user = UserInfo.builder()
.uid(uuid)
.userName(name)
.build();
UserInfo savedUser = userInfoRepository.save(user);
return ApiResponse.ok("사용자 정보를 성공적으로 등록했습니다.", savedUser);
}

public ApiResponse<UserInfo> retrieveUserInfo(String uid) {
Optional<UserInfo> optionalUser = userInfoRepository.findByUid(uid);
if(optionalUser.isEmpty()) return ApiResponse.withError(ErrorCode.INVALID_USER_ID);
UserInfo userInfo = optionalUser.get();
return ApiResponse.ok("사용자 정보를 성공적으로 조회했습니다.", userInfo);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

import org.alongtheblue.alongtheblue_server.domain.userInfo.domain.UserInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.Optional;

public interface UserInfoRepository extends JpaRepository<UserInfo, Long> {
@Query("SELECT u FROM UserInfo u WHERE u.uid = :uid ")
Optional<UserInfo> findByUid(String uid);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
public class SecurityConfig {

private static final String[] WHITE_LIST = {
"/v3/api-docs/**", "/swagger-ui/**", "/swagger-resources/**", "/", "/api/**", "/error/**", "/swagger", "/swagger/**"
"/v3/api-docs/**", "/swagger-ui/**", "/swagger-resources/**", "/", "/api/**", "/error/**", "/swagger", "/swagger/**", "/oauth/**"
};

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ public enum ErrorCode {
ERROR_S3_DELETE_OBJECT(HttpStatus.INTERNAL_SERVER_ERROR, "서버 문제 S3 이미지 삭제에 실패하였습니다."),
ERROR_S3_UPDATE_OBJECT(HttpStatus.INTERNAL_SERVER_ERROR, "서버 문제로 S3 이미지 업로드에 실패하였습니다."),

INVALID_RESTAURANT_ID(HttpStatus.BAD_REQUEST, "유효하지 않은 restaurant 식별자입니다.");
INVALID_RESTAURANT_ID(HttpStatus.BAD_REQUEST, "유효하지 않은 restaurant 식별자입니다."),

INVALID_USER_ID(HttpStatus.BAD_REQUEST, "유효하지 않은 user 식별자입니다.");

private final HttpStatus status;
private final String message;
Expand Down

0 comments on commit c2d512b

Please sign in to comment.