From 05e81e02239fc841416b27ee4b93fc475c512bd9 Mon Sep 17 00:00:00 2001 From: hoyeonyy Date: Thu, 15 Aug 2024 16:30:54 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/controller/AuthController.java | 21 +-- .../auth/dto/request/OauthRequest.java | 6 + .../auth/dto/response/OauthResponse.java | 6 + .../backend/auth/service/AuthService.java | 123 +++++++++++++----- .../backend/config/RestClientConfig.java | 16 +++ .../mouda/backend/config/WebMvcConfig.java | 8 +- .../mouda/backend/member/domain/Member.java | 6 +- .../backend/member/domain/MemberTest.java | 16 +-- 8 files changed, 135 insertions(+), 67 deletions(-) create mode 100644 backend/src/main/java/mouda/backend/auth/dto/request/OauthRequest.java create mode 100644 backend/src/main/java/mouda/backend/auth/dto/response/OauthResponse.java create mode 100644 backend/src/main/java/mouda/backend/config/RestClientConfig.java diff --git a/backend/src/main/java/mouda/backend/auth/controller/AuthController.java b/backend/src/main/java/mouda/backend/auth/controller/AuthController.java index 15cc9aed3..b427c6994 100644 --- a/backend/src/main/java/mouda/backend/auth/controller/AuthController.java +++ b/backend/src/main/java/mouda/backend/auth/controller/AuthController.java @@ -1,20 +1,17 @@ package mouda.backend.auth.controller; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import lombok.RequiredArgsConstructor; import mouda.backend.auth.dto.request.LoginRequest; +import mouda.backend.auth.dto.request.OauthRequest; import mouda.backend.auth.dto.response.LoginResponse; import mouda.backend.auth.service.AuthService; import mouda.backend.common.RestResponse; -import mouda.backend.member.domain.Member; -import mouda.backend.member.repository.MemberRepository; @RestController @RequestMapping("/v1/auth") @@ -22,7 +19,6 @@ public class AuthController implements AuthSwagger { private final AuthService authService; - private final MemberRepository memberRepository; @Override @PostMapping("/login") @@ -32,16 +28,9 @@ public ResponseEntity> login(@RequestBody LoginReque return ResponseEntity.ok().body(new RestResponse<>(response)); } - @GetMapping("/kakao/oauth") - public String kakao( - @RequestParam("code") String code, - @RequestParam(value = "error", required = false) String error, - @RequestParam(value = "error_description", required = false) String error_description, - @RequestParam(value = "state", required = false) String state - ) { - System.out.println(code); - Member member = new Member(code); - memberRepository.save(member); - return code; + @PostMapping("/kakao/oauth") + public String kakao(@RequestBody OauthRequest oauthRequest) { + return authService.oauthLogin(oauthRequest.code()); + } } diff --git a/backend/src/main/java/mouda/backend/auth/dto/request/OauthRequest.java b/backend/src/main/java/mouda/backend/auth/dto/request/OauthRequest.java new file mode 100644 index 000000000..8a26fb470 --- /dev/null +++ b/backend/src/main/java/mouda/backend/auth/dto/request/OauthRequest.java @@ -0,0 +1,6 @@ +package mouda.backend.auth.dto.request; + +public record OauthRequest( + String code +) { +} diff --git a/backend/src/main/java/mouda/backend/auth/dto/response/OauthResponse.java b/backend/src/main/java/mouda/backend/auth/dto/response/OauthResponse.java new file mode 100644 index 000000000..a9ba9f182 --- /dev/null +++ b/backend/src/main/java/mouda/backend/auth/dto/response/OauthResponse.java @@ -0,0 +1,6 @@ +package mouda.backend.auth.dto.response; + +public record OauthResponse( + String id_token +) { +} diff --git a/backend/src/main/java/mouda/backend/auth/service/AuthService.java b/backend/src/main/java/mouda/backend/auth/service/AuthService.java index a1d610a5d..28e87a2f1 100644 --- a/backend/src/main/java/mouda/backend/auth/service/AuthService.java +++ b/backend/src/main/java/mouda/backend/auth/service/AuthService.java @@ -1,50 +1,101 @@ package mouda.backend.auth.service; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClient; + +import com.fasterxml.jackson.databind.ObjectMapper; + import mouda.backend.auth.dto.request.LoginRequest; import mouda.backend.auth.dto.response.LoginResponse; +import mouda.backend.auth.dto.response.OauthResponse; import mouda.backend.auth.exception.AuthErrorMessage; import mouda.backend.auth.exception.AuthException; import mouda.backend.member.domain.Member; import mouda.backend.member.repository.MemberRepository; import mouda.backend.security.JwtProvider; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Service; @Service public class AuthService { - private final JwtProvider jwtProvider; - - private final MemberRepository memberRepository; - - - public AuthService(JwtProvider jwtProvider, MemberRepository memberRepository) { - this.jwtProvider = jwtProvider; - this.memberRepository = memberRepository; - } - - public LoginResponse login(LoginRequest loginRequest) { - return memberRepository.findByNickname(loginRequest.nickname()) - .map(member -> { - String token = jwtProvider.createToken(member); - return new LoginResponse(token); - }) - .orElseGet(() -> { - Member newMember = new Member(loginRequest.nickname()); - memberRepository.save(newMember); - String token = jwtProvider.createToken(newMember); - return new LoginResponse(token); - }); - } - - public Member findMember(String token) { - long memberId = jwtProvider.extractMemberId(token); - return memberRepository.findById(memberId) - .orElseThrow( - () -> new AuthException(HttpStatus.UNAUTHORIZED, AuthErrorMessage.UNAUTHORIZED)); - } - - public void checkAuthentication(String token) { - jwtProvider.validateExpiration(token); - } + private final JwtProvider jwtProvider; + + private final MemberRepository memberRepository; + private final RestClient restClient; + + public AuthService(JwtProvider jwtProvider, MemberRepository memberRepository, RestClient restClient) { + this.jwtProvider = jwtProvider; + this.memberRepository = memberRepository; + this.restClient = restClient; + } + + public LoginResponse login(LoginRequest loginRequest) { + return memberRepository.findByNickname(loginRequest.nickname()) + .map(member -> { + String token = jwtProvider.createToken(member); + return new LoginResponse(token); + }) + .orElseGet(() -> { + Member newMember = new Member(loginRequest.nickname()); + memberRepository.save(newMember); + String token = jwtProvider.createToken(newMember); + return new LoginResponse(token); + }); + } + + public Member findMember(String token) { + long memberId = jwtProvider.extractMemberId(token); + return memberRepository.findById(memberId) + .orElseThrow( + () -> new AuthException(HttpStatus.UNAUTHORIZED, AuthErrorMessage.UNAUTHORIZED)); + } + + public void checkAuthentication(String token) { + jwtProvider.validateExpiration(token); + } + + public String oauthLogin(String code) { + OauthResponse oauthResponse = restClient.post() + .uri("/oauth/token") + .header("Content-type", "application/x-www-form-urlencoded;charset=utf-8") + .body(token(code)) + .retrieve() + .body(OauthResponse.class); + + String kakaoIdToken = oauthResponse.id_token(); + try { + // JWT는 점(.)으로 구분된 세 부분으로 이루어져 있다 + String[] parts = kakaoIdToken.split("\\."); + if (parts.length != 3) { + throw new IllegalArgumentException("Invalid JWT token format"); + } + // 두 번째 부분이 payload이다. + String payload = parts[1]; + // Base64Url 디코딩 (Base64Url은 표준 Base64와 약간 다름) + byte[] decodedBytes = Base64.getUrlDecoder().decode(payload); + // 디코딩된 바이트 배열을 문자열로 변환 + String decodedPayload = new String(decodedBytes); + // JSON 파싱하여 Map으로 변환 + ObjectMapper objectMapper = new ObjectMapper(); + Map payloadJson = objectMapper.readValue(decodedPayload, Map.class); + String nickname = payloadJson.get("nickname").toString(); + return nickname; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + private Map token(String code) { + HashMap map = new HashMap<>(); + map.put("grant_type", "authorization_code"); + map.put("client_id", "ca3adf9a52671fdbb847b809c0fdb980"); + map.put("redirect_uri", "https://dev.mouda.site/v1/auth/kakao/oauth"); + map.put("code", code); + return map; + } } diff --git a/backend/src/main/java/mouda/backend/config/RestClientConfig.java b/backend/src/main/java/mouda/backend/config/RestClientConfig.java new file mode 100644 index 000000000..87ba7c628 --- /dev/null +++ b/backend/src/main/java/mouda/backend/config/RestClientConfig.java @@ -0,0 +1,16 @@ +package mouda.backend.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestClient; + +@Configuration +public class RestClientConfig { + + @Bean + public RestClient tossPaymentRestClient() { + return RestClient.builder() + .baseUrl("https://kauth.kakao.com") + .build(); + } +} diff --git a/backend/src/main/java/mouda/backend/config/WebMvcConfig.java b/backend/src/main/java/mouda/backend/config/WebMvcConfig.java index 170595c31..636389d8e 100644 --- a/backend/src/main/java/mouda/backend/config/WebMvcConfig.java +++ b/backend/src/main/java/mouda/backend/config/WebMvcConfig.java @@ -26,10 +26,10 @@ public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(apiRequestLoggingInterceptor); - // - // registry.addInterceptor(authenticationCheckInterceptor) - // .addPathPatterns("/v1/**") - // .excludePathPatterns("/v1/auth/login"); + + registry.addInterceptor(authenticationCheckInterceptor) + .addPathPatterns("/v1/**") + .excludePathPatterns("/v1/auth/kakao/oauth"); } @Override diff --git a/backend/src/main/java/mouda/backend/member/domain/Member.java b/backend/src/main/java/mouda/backend/member/domain/Member.java index 448bb921f..059bdcf18 100644 --- a/backend/src/main/java/mouda/backend/member/domain/Member.java +++ b/backend/src/main/java/mouda/backend/member/domain/Member.java @@ -43,9 +43,9 @@ private void validateNickname(String nickname) { if (nickname.isBlank()) { throw new MoimException(HttpStatus.BAD_REQUEST, MoimErrorMessage.MEMBER_NICKNAME_NOT_EXISTS); } - // if (nickname.length() >= NICKNAME_MAX_LENGTH) { - // throw new MoimException(HttpStatus.BAD_REQUEST, MoimErrorMessage.MEMBER_NICKNAME_TOO_LONG); - // } + if (nickname.length() >= NICKNAME_MAX_LENGTH) { + throw new MoimException(HttpStatus.BAD_REQUEST, MoimErrorMessage.MEMBER_NICKNAME_TOO_LONG); + } } public void joinMoim(Moim moim) { diff --git a/backend/src/test/java/mouda/backend/member/domain/MemberTest.java b/backend/src/test/java/mouda/backend/member/domain/MemberTest.java index 38fffe4fd..f34f764ef 100644 --- a/backend/src/test/java/mouda/backend/member/domain/MemberTest.java +++ b/backend/src/test/java/mouda/backend/member/domain/MemberTest.java @@ -25,12 +25,12 @@ void failToCreateMemberWhenNicknameIsBlank() { .build()); } - // @DisplayName("닉네임이 제한 길이를 초과하여 회원 생성에 실패한다.") - // @Test - // void failToCreateMemberWhenNicknameIsTooLong() { - // String longNickname = "a".repeat(11); - // Assertions.assertThrows(MoimException.class, () -> Member.builder() - // .nickname(longNickname) - // .build()); - // } + @DisplayName("닉네임이 제한 길이를 초과하여 회원 생성에 실패한다.") + @Test + void failToCreateMemberWhenNicknameIsTooLong() { + String longNickname = "a".repeat(11); + Assertions.assertThrows(MoimException.class, () -> Member.builder() + .nickname(longNickname) + .build()); + } } From de6c33a0251c2b91e688f70967f0ab4e734942b8 Mon Sep 17 00:00:00 2001 From: hoyeonyy Date: Thu, 15 Aug 2024 16:32:48 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/java/mouda/backend/config/WebMvcConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/mouda/backend/config/WebMvcConfig.java b/backend/src/main/java/mouda/backend/config/WebMvcConfig.java index 636389d8e..6b6514cd4 100644 --- a/backend/src/main/java/mouda/backend/config/WebMvcConfig.java +++ b/backend/src/main/java/mouda/backend/config/WebMvcConfig.java @@ -29,7 +29,7 @@ public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authenticationCheckInterceptor) .addPathPatterns("/v1/**") - .excludePathPatterns("/v1/auth/kakao/oauth"); + .excludePathPatterns("/v1/auth/kakao/oauth", "/v1/auth/login"); } @Override