From f4ad8997faeaf09e124461ff257779f6c7e42254 Mon Sep 17 00:00:00 2001 From: hyeonda02 Date: Fri, 20 Dec 2024 09:27:34 +0900 Subject: [PATCH] =?UTF-8?q?Feat=20:=20=EB=84=A4=EC=9D=B4=EB=B2=84=20?= =?UTF-8?q?=EC=86=8C=EC=85=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= =?UTF-8?q?=20-=20=EB=84=A4=EC=9D=B4=EB=B2=84=20=EC=86=8C=EC=85=9C=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20-=20CI=5FMVP.yml=EC=97=90=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=86=8C=EC=85=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=ED=82=A4=20=EC=B6=94=EA=B0=80=20-=20=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=EC=97=90=20=EC=86=8C=EC=85=9C?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=83=80=EC=9E=85=20(SocialType)?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20-=20=EB=A9=A4=EB=B2=84=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=EC=97=90=20=EC=86=8C=EC=85=9C=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EB=94=94=20=ED=83=80=EC=9E=85=EC=9D=84=20Long=20?= =?UTF-8?q?=EC=97=90=EC=84=9C=20String=20=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20-=20=EC=86=8C=EC=85=9C=EC=95=84=EC=9D=B4=EB=94=94?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20JWT=20=EB=A1=9C=EC=A7=81=20=EC=9D=BC=EB=B6=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/CI_MVP.yml | 14 ++ .../auth/controller/NaverAuthController.java | 38 ++++++ .../server/auth/dto/NaverTokenResponse.java | 24 ++++ .../server/auth/dto/NaverUserResponse.java | 31 +++++ .../umc/kkijuk/server/auth/jwt/JwtFilter.java | 14 +- .../umc/kkijuk/server/auth/jwt/JwtUtil.java | 7 +- .../server/auth/service/KakaoAuthService.java | 4 +- .../server/auth/service/NaverAuthService.java | 126 ++++++++++++++++++ .../umc/kkijuk/server/common/LoginUser.java | 2 +- .../member/controller/MemberController.java | 6 +- .../response/MemberInfoResponse.java | 2 +- .../kkijuk/server/member/domain/Member.java | 8 +- .../server/member/domain/SocialType.java | 7 + .../repository/MemberJpaRepository.java | 2 +- .../member/repository/MemberRepository.java | 3 +- .../repository/MemberRepositoryImpl.java | 5 +- .../server/member/service/MemberService.java | 14 +- .../member/service/MemberServiceImpl.java | 76 ++++++++--- 18 files changed, 337 insertions(+), 46 deletions(-) create mode 100644 src/main/java/umc/kkijuk/server/auth/controller/NaverAuthController.java create mode 100644 src/main/java/umc/kkijuk/server/auth/dto/NaverTokenResponse.java create mode 100644 src/main/java/umc/kkijuk/server/auth/dto/NaverUserResponse.java create mode 100644 src/main/java/umc/kkijuk/server/auth/service/NaverAuthService.java create mode 100644 src/main/java/umc/kkijuk/server/member/domain/SocialType.java diff --git a/.github/workflows/CI_MVP.yml b/.github/workflows/CI_MVP.yml index b5129355..ed74200f 100644 --- a/.github/workflows/CI_MVP.yml +++ b/.github/workflows/CI_MVP.yml @@ -61,6 +61,20 @@ jobs: echo "spring.security.oauth2.client.provider.kakao.token-uri=${{ secrets.KAKAO_TOKEN_URI }}" >> ./src/main/resources/application.properties echo "spring.security.oauth2.client.provider.kakao.user-info-uri=${{ secrets.KAKAO_USER_INFO_URI }}" >> ./src/main/resources/application.properties echo "spring.security.oauth2.client.provider.kakao.user-name-attribute=${{ secrets.KAKAO_USER_NAME_ATTRIBUTE }}" >> ./src/main/resources/application.properties + + # Naver OAuth2 Configuration + echo "spring.security.oauth2.client.registration.naver.client-name=${{ secrets.NAVER_CLIENT_NAME }}" >> ./src/main/resources/application.properties + echo "spring.security.oauth2.client.registration.naver.client-id=${{ secrets.NAVER_CLIENT_ID }}" >> ./src/main/resources/application.properties + echo "spring.security.oauth2.client.registration.naver.client-secret=${{ secrets.NAVER_CLIENT_SECRET }}" >> ./src/main/resources/application.properties + echo "spring.security.oauth2.client.registration.naver.redirect-uri=${{ secrets.NAVER_REDIRECT_URI }}" >> ./src/main/resources/application.properties + echo "spring.security.oauth2.client.registration.naver.authorization-grant-type=${{ secrets.NAVER_AUTH_GRANT_TYPE }}" >> ./src/main/resources/application.properties + echo "spring.security.oauth2.client.registration.naver.scope=${{ secrets.NAVER_SCOPE }}" >> ./src/main/resources/application.properties + + echo "spring.security.oauth2.client.provider.naver.authorization-uri=${{ secrets.NAVER_AUTHORIZATION_URI }}" >> ./src/main/resources/application.properties + echo "spring.security.oauth2.client.provider.naver.token-uri=${{ secrets.NAVER_TOKEN_URI }}" >> ./src/main/resources/application.properties + echo "spring.security.oauth2.client.provider.naver.user-info-uri=${{ secrets.NAVER_USER_INFO_URI }}" >> ./src/main/resources/application.properties + echo "spring.security.oauth2.client.provider.naver.user-name-attribute=${{ secrets.NAVER_USER_NAME_ATTRIBUTE }}" >> ./src/main/resources/application.properties + - name: Build with Gradle Wrapper run: ./gradlew build diff --git a/src/main/java/umc/kkijuk/server/auth/controller/NaverAuthController.java b/src/main/java/umc/kkijuk/server/auth/controller/NaverAuthController.java new file mode 100644 index 00000000..2d44ece2 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/auth/controller/NaverAuthController.java @@ -0,0 +1,38 @@ +package umc.kkijuk.server.auth.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +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 umc.kkijuk.server.auth.service.NaverAuthService; +import umc.kkijuk.server.member.domain.Member; + +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@RestController +@RequiredArgsConstructor +public class NaverAuthController { + private final NaverAuthService naverAuthService; + + @GetMapping("/auth/naver/login") + public ResponseEntity naverCallback(@RequestParam("code") String code, + @RequestParam("state") String state){ + try{ + String naverAccessToken = naverAuthService.getNaverAccessToken(code, state); + Member member = naverAuthService.processNaverUser(naverAccessToken); + Map tokens = new HashMap<>(); + tokens.put("Token", naverAuthService.generateTokens(member)); + log.info("네이버 로그인 성공: 사용자 이름={}, 네이버 ID={}", member.getName(), member.getSocialId()); + return ResponseEntity.ok(tokens); + + }catch (Exception e){ + log.error("네이버 인증 처리 중 오류 발생 : {}",e.getMessage(),e); + return ResponseEntity.internalServerError().body(Map.of("error", "네이버 인증 처리 실패")); + } + } +} diff --git a/src/main/java/umc/kkijuk/server/auth/dto/NaverTokenResponse.java b/src/main/java/umc/kkijuk/server/auth/dto/NaverTokenResponse.java new file mode 100644 index 00000000..93ffd348 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/auth/dto/NaverTokenResponse.java @@ -0,0 +1,24 @@ +package umc.kkijuk.server.auth.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class NaverTokenResponse { + @JsonProperty("access_token") + private String accessToken; + @JsonProperty("refresh_token") + private String refreshToken; + @JsonProperty("token_type") + private String tokenType; + @JsonProperty("expires_in") + private String expiresIn; + @JsonProperty("error") + private String error; + @JsonProperty("error_description") + private String errorDescription; +} diff --git a/src/main/java/umc/kkijuk/server/auth/dto/NaverUserResponse.java b/src/main/java/umc/kkijuk/server/auth/dto/NaverUserResponse.java new file mode 100644 index 00000000..5030bd73 --- /dev/null +++ b/src/main/java/umc/kkijuk/server/auth/dto/NaverUserResponse.java @@ -0,0 +1,31 @@ +package umc.kkijuk.server.auth.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class NaverUserResponse { + @JsonProperty("resultcode") + private String resultCode; + @JsonProperty("message") + private String message; + @JsonProperty("response") + private NaverUserDetail naverUserDetail; + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class NaverUserDetail { + private String id; + private String name; + private String email; + private String birthday; + private String birthyear; + private String mobile; + + } + +} diff --git a/src/main/java/umc/kkijuk/server/auth/jwt/JwtFilter.java b/src/main/java/umc/kkijuk/server/auth/jwt/JwtFilter.java index ce008983..0f8230a6 100644 --- a/src/main/java/umc/kkijuk/server/auth/jwt/JwtFilter.java +++ b/src/main/java/umc/kkijuk/server/auth/jwt/JwtFilter.java @@ -36,24 +36,24 @@ protected void doFilterInternal( String requestUri = request.getRequestURI(); - // 카카오 로그인 경로 제외 - if (requestUri.startsWith("/auth/kakao/login")) { + // 카카오 로그인 경로 제외, 네이버 로그인 경로 제외 + if (requestUri.startsWith("/auth/kakao/login")||requestUri.startsWith("/auth/naver/login")) { chain.doFilter(request, response); return; } try { final String authorizationHeader = request.getHeader("Authorization"); - Long kakaoId = null; + String socialId = null; String jwt = null; if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { jwt = authorizationHeader.substring(7); - kakaoId = jwtUtil.extractSocialId(jwt); + socialId = jwtUtil.extractSocialId(jwt); } - if (kakaoId != null && SecurityContextHolder.getContext().getAuthentication() == null) { - Member member = memberRepository.findBySocialId(kakaoId).orElse(null); + if (socialId != null && SecurityContextHolder.getContext().getAuthentication() == null) { + Member member = memberRepository.findBySocialId(socialId).orElse(null); if (member != null && jwtUtil.validateToken(jwt, String.valueOf(member.getSocialId()))) { UserDetails userDetails = @@ -69,7 +69,7 @@ protected void doFilterInternal( SecurityContextHolder.getContext().setAuthentication(authentication); } else { - log.warn("Invalid JWT Token for member: {}", kakaoId); + log.warn("Invalid JWT Token for member: {}", socialId); } } } catch (Exception e) { diff --git a/src/main/java/umc/kkijuk/server/auth/jwt/JwtUtil.java b/src/main/java/umc/kkijuk/server/auth/jwt/JwtUtil.java index c5bed84e..1b94a01e 100644 --- a/src/main/java/umc/kkijuk/server/auth/jwt/JwtUtil.java +++ b/src/main/java/umc/kkijuk/server/auth/jwt/JwtUtil.java @@ -72,14 +72,13 @@ public boolean validateToken(String token, String socialId) { } } - public Long extractSocialId(String token) { - return Long.valueOf( - Jwts.parserBuilder() + public String extractSocialId(String token) { + return Jwts.parserBuilder() .setSigningKey(getSigningKey()) .build() .parseClaimsJws(token) .getBody() - .getId()); + .getId(); } } diff --git a/src/main/java/umc/kkijuk/server/auth/service/KakaoAuthService.java b/src/main/java/umc/kkijuk/server/auth/service/KakaoAuthService.java index e951b0ac..0eee8b05 100644 --- a/src/main/java/umc/kkijuk/server/auth/service/KakaoAuthService.java +++ b/src/main/java/umc/kkijuk/server/auth/service/KakaoAuthService.java @@ -91,7 +91,7 @@ public Map getKakaoUserInfo(String accessToken) { public Member processKakaoUser(String accessToken) { Map kakaoUserInfo = getKakaoUserInfo(accessToken); - Long kakaoId = Long.valueOf(kakaoUserInfo.get("id").toString()); + String kakaoId = kakaoUserInfo.get("id").toString(); String email = extractEmail(kakaoUserInfo); String name = extractName(kakaoUserInfo); String phoneNumber = extractPhoneNumber(kakaoUserInfo); @@ -99,7 +99,7 @@ public Member processKakaoUser(String accessToken) { log.info("카카오 사용자 정보 추출 - 이메일: {}, 이름: {}, 카카오 ID: {}, 전화번호: {}, 생년월일: {}", email, name, kakaoId, phoneNumber, birthDate); - return memberRepository.findBySocialId(kakaoId) + return memberRepository.findBySocialId(String.valueOf(kakaoId)) .orElseGet(() -> { log.info("신규 사용자 생성 - 카카오 ID: {}", kakaoId); return memberService.createUserWithKakaoId(kakaoId, kakaoUserInfo); diff --git a/src/main/java/umc/kkijuk/server/auth/service/NaverAuthService.java b/src/main/java/umc/kkijuk/server/auth/service/NaverAuthService.java new file mode 100644 index 00000000..69c6969c --- /dev/null +++ b/src/main/java/umc/kkijuk/server/auth/service/NaverAuthService.java @@ -0,0 +1,126 @@ +package umc.kkijuk.server.auth.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +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.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; +import umc.kkijuk.server.auth.dto.NaverTokenResponse; +import umc.kkijuk.server.auth.dto.NaverUserResponse; +import umc.kkijuk.server.auth.jwt.JwtUtil; +import umc.kkijuk.server.member.domain.Member; +import umc.kkijuk.server.member.repository.MemberRepository; +import umc.kkijuk.server.member.service.MemberService; + +import java.time.LocalDate; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@Service +@RequiredArgsConstructor +public class NaverAuthService { + private final MemberRepository memberRepository; + private final JwtUtil jwtUtil; + private final MemberService memberService; + private final RestTemplate restTemplate; + + @Value("${spring.security.oauth2.client.registration.naver.client-id}") + private String clientId; + @Value("${spring.security.oauth2.client.registration.naver.client-secret}") + private String clientSecret; + @Value("${spring.security.oauth2.client.provider.naver.token-uri}") + private String tokenUri; + @Value("${spring.security.oauth2.client.provider.naver.user-info-uri}") + private String userInfoUri; + @Value("${spring.security.oauth2.client.registration.naver.authorization-grant-type}") + private String grantType; + + public String getNaverAccessToken(String code, String state){ + String url = UriComponentsBuilder.fromHttpUrl(tokenUri) + .queryParam("grant_type",grantType) + .queryParam("client_id", clientId) + .queryParam("client_secret", clientSecret) + .queryParam("code", code) + .queryParam("state", state) + .build().toUriString(); + + + ResponseEntity response = + restTemplate.exchange(url, HttpMethod.GET, null, NaverTokenResponse.class); + + return response.getBody().getAccessToken(); + + } + + public NaverUserResponse.NaverUserDetail getNaverUserInfo(String naverAccessToken) { + HttpHeaders headers = new HttpHeaders(); + headers.setBearerAuth(naverAccessToken); + + HttpEntity> request = new HttpEntity<>(headers); + + ResponseEntity response = + restTemplate.exchange(userInfoUri, HttpMethod.GET, request, NaverUserResponse.class); + + return response.getBody().getNaverUserDetail(); + + } + public Member processNaverUser(String naverAccessToken) { + NaverUserResponse.NaverUserDetail naverUserInfo = getNaverUserInfo(naverAccessToken); + String naverId = naverUserInfo.getId (); + String email = naverUserInfo.getEmail(); + String name = naverUserInfo.getName(); + String phoneNumber = naverUserInfo.getMobile(); + LocalDate birthDate = extractBirthDate(naverUserInfo); + + log.info("네이버 사용자 정보 추출 - 이메일: {}, 이름: {}, 카카오 ID: {}, 전화번호: {}, 생년월일: {}", email, name, naverId, phoneNumber, birthDate); + + return memberRepository.findBySocialId(naverId) + .orElseGet(() -> { + log.info("신규 사용자 생성 - 네이버 ID: {}", naverId); + return memberService.createUserWithNaverId(naverId,naverUserInfo); + }); + } + + public Object generateTokens(Member member) { + String naverId = String.valueOf(member.getSocialId()); + String accessToken = jwtUtil.createAccessToken(naverId); + String refreshToken = jwtUtil.createRefreshToken(naverId); + + log.info("JWT Token 생성 완료 - 네이버 ID : {}, accessToken : {}, refreshToken : {}", naverId, accessToken, refreshToken); + + Map tokens = new HashMap<>(); + tokens.put("accessToken", accessToken); + tokens.put("refreshToken", refreshToken); + + return tokens; + } + + public LocalDate extractBirthDate(NaverUserResponse.NaverUserDetail naverUserInfo) { + String birthday = (String) naverUserInfo.getBirthday(); + String birthyear = (String) naverUserInfo.getBirthyear(); + log.info("Naver 생년월일 정보: {}, {}", birthday,birthyear ); + + + if (birthday == null || birthday.isEmpty()) { + return null; + } + + int year = (birthyear != null && !birthyear.isEmpty()) + ? Integer.parseInt(birthyear) + : LocalDate.now().getYear(); + + String[] dateParts = birthday.split("-"); + int month = Integer.parseInt(dateParts[0]); + int day = Integer.parseInt(dateParts[1]); + + + return LocalDate.of(year, month, day); + } +} diff --git a/src/main/java/umc/kkijuk/server/common/LoginUser.java b/src/main/java/umc/kkijuk/server/common/LoginUser.java index 4cf7476e..dae65274 100644 --- a/src/main/java/umc/kkijuk/server/common/LoginUser.java +++ b/src/main/java/umc/kkijuk/server/common/LoginUser.java @@ -31,7 +31,7 @@ public Long extractMemberId(String bearerToken) { } String token = bearerToken.substring(7); - Long socialId = jwtUtil.extractSocialId(token); + String socialId = jwtUtil.extractSocialId(token); if (socialId == null) { throw new IllegalArgumentException("유효하지 않은 토큰입니다."); diff --git a/src/main/java/umc/kkijuk/server/member/controller/MemberController.java b/src/main/java/umc/kkijuk/server/member/controller/MemberController.java index 4e37bf3f..5877ad89 100644 --- a/src/main/java/umc/kkijuk/server/member/controller/MemberController.java +++ b/src/main/java/umc/kkijuk/server/member/controller/MemberController.java @@ -137,7 +137,7 @@ public ResponseEntity getEmail() { public ResponseEntity refreshToken(@RequestBody RefreshTokenRequest request) { String refreshToken = request.getRefreshToken(); - Long kakaoId = jwtUtil.extractSocialId(refreshToken); + String kakaoId = jwtUtil.extractSocialId(refreshToken); AuthResponse response = memberService.refreshAuthToken(refreshToken, kakaoId); @@ -157,7 +157,7 @@ public ResponseEntity refreshToken(@RequestBody RefreshTokenReques @GetMapping("/myPage/info") public ResponseEntity getInfo(@RequestHeader("Authorization") String bearerToken) { Long loginUser = LoginUser.get().extractMemberId(bearerToken); - MemberInfoResponse memberInfoResponse = memberService.getMemberInfo(loginUser); + MemberInfoResponse memberInfoResponse = memberService.getMemberInfo(String.valueOf(loginUser)); return ResponseEntity .status(HttpStatus.OK) .body(memberInfoResponse); @@ -196,7 +196,7 @@ public ResponseEntity postField(@RequestBody MemberFieldDto memberField @Operation(summary = "로그아웃", description = "사용자 로그아웃") @PostMapping("/logout") public ResponseEntity logout(@RequestHeader("Authorization") String token) { - Long kakaoId = jwtUtil.extractSocialId(token.substring(7)); + String kakaoId = jwtUtil.extractSocialId(token.substring(7)); memberService.invalidateRefreshToken(kakaoId); return ResponseEntity.ok("로그아웃 완료"); } diff --git a/src/main/java/umc/kkijuk/server/member/controller/response/MemberInfoResponse.java b/src/main/java/umc/kkijuk/server/member/controller/response/MemberInfoResponse.java index ec027871..7842be3e 100644 --- a/src/main/java/umc/kkijuk/server/member/controller/response/MemberInfoResponse.java +++ b/src/main/java/umc/kkijuk/server/member/controller/response/MemberInfoResponse.java @@ -9,7 +9,7 @@ @Data @Builder public class MemberInfoResponse { - private Long kakaoId; + private String socialId; private String email; private String name; private String phoneNumber; diff --git a/src/main/java/umc/kkijuk/server/member/domain/Member.java b/src/main/java/umc/kkijuk/server/member/domain/Member.java index 0a0d3b7d..6f1515a6 100644 --- a/src/main/java/umc/kkijuk/server/member/domain/Member.java +++ b/src/main/java/umc/kkijuk/server/member/domain/Member.java @@ -22,7 +22,10 @@ public class Member extends BaseEntity { @Column(name = "member_id") private Long id; - private Long socialId; + private String socialId; + + @Enumerated(EnumType.STRING) + private SocialType socialType; @NotNull private String email; @@ -98,7 +101,7 @@ public void deleteRecruitTag(String tag) { this.recruitTags.remove(tag); } - public void setSocialId(Long kakaoId) { + public void setSocialId(String kakaoId) { this.socialId = kakaoId; } @@ -115,5 +118,6 @@ public void setBirthDate(LocalDate birthDate) { this.birthDate = birthDate; } public void setRefreshToken(String refreshToken){ this.refreshToken = refreshToken;} + public void setSocialType(SocialType type){this.socialType = type;} } diff --git a/src/main/java/umc/kkijuk/server/member/domain/SocialType.java b/src/main/java/umc/kkijuk/server/member/domain/SocialType.java new file mode 100644 index 00000000..f19f29bf --- /dev/null +++ b/src/main/java/umc/kkijuk/server/member/domain/SocialType.java @@ -0,0 +1,7 @@ +package umc.kkijuk.server.member.domain; + +public enum SocialType { + NAVER, + KAKAO; + +} diff --git a/src/main/java/umc/kkijuk/server/member/repository/MemberJpaRepository.java b/src/main/java/umc/kkijuk/server/member/repository/MemberJpaRepository.java index c8b2e902..783bcd1d 100644 --- a/src/main/java/umc/kkijuk/server/member/repository/MemberJpaRepository.java +++ b/src/main/java/umc/kkijuk/server/member/repository/MemberJpaRepository.java @@ -9,5 +9,5 @@ public interface MemberJpaRepository extends JpaRepository{ Optional findById(Long id); Optional findByEmail(String email); Optional findByPhoneNumber(String phoneNumber); - Optional findBySocialId(Long socialId); + Optional findBySocialId(String socialId); } diff --git a/src/main/java/umc/kkijuk/server/member/repository/MemberRepository.java b/src/main/java/umc/kkijuk/server/member/repository/MemberRepository.java index 358aaa95..636a2442 100644 --- a/src/main/java/umc/kkijuk/server/member/repository/MemberRepository.java +++ b/src/main/java/umc/kkijuk/server/member/repository/MemberRepository.java @@ -9,6 +9,7 @@ public interface MemberRepository { Optional findByEmail(String email); Member save(Member member); Optional findByPhoneNumber(String phoneNumber); - Optional findBySocialId(Long socialId); +// Optional findBySocialId(Long socialId); + Optional findBySocialId(String socialId); void deleteById(Long id); } \ No newline at end of file diff --git a/src/main/java/umc/kkijuk/server/member/repository/MemberRepositoryImpl.java b/src/main/java/umc/kkijuk/server/member/repository/MemberRepositoryImpl.java index 3944a8b2..4335b36c 100644 --- a/src/main/java/umc/kkijuk/server/member/repository/MemberRepositoryImpl.java +++ b/src/main/java/umc/kkijuk/server/member/repository/MemberRepositoryImpl.java @@ -28,11 +28,14 @@ public Member save(Member member) { public Optional findByPhoneNumber(String phoneNumber) { return memberJpaRepository.findByPhoneNumber(phoneNumber); } + @Override - public Optional findBySocialId(Long socialId) { + public Optional findBySocialId(String socialId) { return memberJpaRepository.findBySocialId(socialId); } + + @Override public void deleteById(Long id) { memberJpaRepository.deleteById(id); diff --git a/src/main/java/umc/kkijuk/server/member/service/MemberService.java b/src/main/java/umc/kkijuk/server/member/service/MemberService.java index 8ba18fdf..25b50d14 100644 --- a/src/main/java/umc/kkijuk/server/member/service/MemberService.java +++ b/src/main/java/umc/kkijuk/server/member/service/MemberService.java @@ -1,6 +1,7 @@ package umc.kkijuk.server.member.service; import umc.kkijuk.server.auth.dto.AuthResponse; +import umc.kkijuk.server.auth.dto.NaverUserResponse; import umc.kkijuk.server.member.controller.response.MemberEmailResponse; import umc.kkijuk.server.member.controller.response.MemberInfoResponse; import umc.kkijuk.server.member.controller.response.MemberStateResponse; @@ -28,12 +29,13 @@ public interface MemberService { /** * 소셜로그인 이후 추가된 기능 */ - Member createUserWithKakaoId(Long kakaoId, Map kakaoUserInfo); - MemberInfoResponse getMemberInfo(Long kakaoId); - void invalidateRefreshToken(Long kakaoId); - void updateRefreshToken(Long kakaoId, String refreshToken); + Member createUserWithKakaoId(String kakaoId, Map kakaoUserInfo); + MemberInfoResponse getMemberInfo(String kakaoId); + void invalidateRefreshToken(String kakaoId); + void updateRefreshToken(String kakaoId, String refreshToken); Long extractMemberId(String bearerToken); - Member findBySocialId(Long socialId); - AuthResponse refreshAuthToken(String refreshToken, Long kakaoId); + public Member findBySocialId(String SocialId); + AuthResponse refreshAuthToken(String refreshToken, String kakaoId); + Member createUserWithNaverId(String naverId, NaverUserResponse.NaverUserDetail naverUserInfo); } diff --git a/src/main/java/umc/kkijuk/server/member/service/MemberServiceImpl.java b/src/main/java/umc/kkijuk/server/member/service/MemberServiceImpl.java index 99d1aff8..f057c8e3 100644 --- a/src/main/java/umc/kkijuk/server/member/service/MemberServiceImpl.java +++ b/src/main/java/umc/kkijuk/server/member/service/MemberServiceImpl.java @@ -7,6 +7,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import umc.kkijuk.server.auth.dto.AuthResponse; +import umc.kkijuk.server.auth.dto.NaverUserResponse; import umc.kkijuk.server.auth.jwt.JwtUtil; import umc.kkijuk.server.common.domian.exception.*; import umc.kkijuk.server.member.controller.response.MemberEmailResponse; @@ -14,6 +15,7 @@ import umc.kkijuk.server.member.controller.response.MemberStateResponse; import umc.kkijuk.server.member.domain.Member; import umc.kkijuk.server.member.domain.Role; +import umc.kkijuk.server.member.domain.SocialType; import umc.kkijuk.server.member.domain.State; import umc.kkijuk.server.member.dto.*; import umc.kkijuk.server.member.repository.MemberRepository; @@ -208,7 +210,7 @@ public List deleteRecruitTag(Member member, String tag) { @Override @Transactional - public Member createUserWithKakaoId(Long kakaoId, Map kakaoUserInfo) { + public Member createUserWithKakaoId(String kakaoId, Map kakaoUserInfo) { Map kakaoAccount = (Map) kakaoUserInfo.get("kakao_account"); String email = (String) kakaoAccount.get("email"); String name = (String) kakaoAccount.get("name"); @@ -233,6 +235,8 @@ public Member createUserWithKakaoId(Long kakaoId, Map kakaoUserI newMember.setPhoneNumber(phoneNumber); newMember.setBirthDate(birthDate); newMember.setRole(Role.ROLE_USER); + newMember.setSocialType(SocialType.KAKAO); + log.info("신규 사용자 생성 - Kakao ID: {}, 이메일: {}, 이름: {}, 전화번호: {}, 생년월일: {}", kakaoId, email, name, phoneNumber, birthDate); return memberRepository.save(newMember); @@ -240,10 +244,48 @@ public Member createUserWithKakaoId(Long kakaoId, Map kakaoUserI @Override @Transactional - public MemberInfoResponse getMemberInfo(Long kakaoId) { - Member member = this.findBySocialId(kakaoId); + public Member createUserWithNaverId(String naverId, NaverUserResponse.NaverUserDetail naverUserInfo) { + String email = naverUserInfo.getEmail(); + String name = naverUserInfo.getName(); + String phoneNumber = naverUserInfo.getMobile(); + String birthday = (String) naverUserInfo.getBirthday(); + String birthyear = (String) naverUserInfo.getBirthyear(); + + LocalDate birthDate = null; + if (birthday != null && !birthday.isEmpty()) { + int year = (birthyear != null && !birthyear.isEmpty()) + ? Integer.parseInt(birthyear) + : LocalDate.now().getYear(); + String[] dateParts = birthday.split("-"); + int month = Integer.parseInt(dateParts[0]); + int day = Integer.parseInt(dateParts[1]); + + birthDate = LocalDate.of(year, month, day); + } + + Member newMember = new Member(); + newMember.setSocialId(naverId); + newMember.setEmail(email); + newMember.setName(name); + newMember.setPhoneNumber(phoneNumber); + newMember.setBirthDate(birthDate); + newMember.setRole(Role.ROLE_USER); + newMember.setSocialType(SocialType.NAVER); + + + log.info("신규 사용자 생성 - Naver ID: {}, 이메일: {}, 이름: {}, 전화번호: {}, 생년월일: {}", naverId, email, name, phoneNumber, birthDate); + return memberRepository.save(newMember); + } + + + + + @Override + @Transactional + public MemberInfoResponse getMemberInfo(String socialId) { + Member member = this.findBySocialId(socialId); return MemberInfoResponse.builder() - .kakaoId(member.getSocialId()) + .socialId(member.getSocialId()) .email(member.getEmail()) .name(member.getName()) .phoneNumber(member.getPhoneNumber()) @@ -255,18 +297,18 @@ public MemberInfoResponse getMemberInfo(Long kakaoId) { @Override @Transactional - public void invalidateRefreshToken(Long kakaoId) { - Member member = this.findBySocialId(kakaoId); + public void invalidateRefreshToken(String socialId) { + Member member = this.findBySocialId(socialId); member.setRefreshToken(null); - log.info("Refresh Token 삭제 완료 - Kakao ID: {}", kakaoId); + log.info("Refresh Token 삭제 완료 - Kakao ID: {}", socialId); } @Override @Transactional - public void updateRefreshToken(Long kakaoId, String refreshToken) { - Member member = this.findBySocialId(kakaoId); + public void updateRefreshToken(String socialId, String refreshToken) { + Member member = this.findBySocialId(socialId); member.setRefreshToken(refreshToken); - log.info("Refresh Token 업데이트 완료 - Kakao ID: {}, Refresh Token: {}", kakaoId, refreshToken); + log.info("Refresh Token 업데이트 완료 - Social ID: {}, Refresh Token: {}", socialId, refreshToken); } // @Override @@ -283,22 +325,22 @@ public Long extractMemberId(String bearerToken) { throw new IllegalArgumentException("Authorization 헤더에 올바른 토큰이 없습니다."); } - Long kakaoId = jwtUtil.extractSocialId(bearerToken.substring(7)); - return this.findBySocialId(kakaoId).getId(); + String kakaoId = jwtUtil.extractSocialId(bearerToken.substring(7)); + return this.findBySocialId(String.valueOf(kakaoId)).getId(); } @Override @Transactional - public Member findBySocialId(Long SocialId) { + public Member findBySocialId(String SocialId) { return memberRepository.findBySocialId(SocialId) .orElseThrow(() -> new RuntimeException("Member not found with Social ID: " + SocialId)); } @Override @Transactional - public AuthResponse refreshAuthToken(String refreshToken, Long kakaoId) { + public AuthResponse refreshAuthToken(String refreshToken, String kakaoId) { // Refresh Token 검증 - if (!jwtUtil.validateToken(refreshToken, String.valueOf(kakaoId))) { + if (!jwtUtil.validateToken(refreshToken, kakaoId)) { log.warn("유효하지 않은 Refresh Token - Social ID: {}", kakaoId); throw new IllegalArgumentException("유효하지 않은 Refresh Token입니다."); } @@ -308,8 +350,8 @@ public AuthResponse refreshAuthToken(String refreshToken, Long kakaoId) { .orElseThrow(() -> new RuntimeException("Member not found with Social ID: " + kakaoId)); // 새로운 Access Token과 Refresh Token 발급 (Refresh Token Rotation) - String newAccessToken = jwtUtil.createAccessToken(String.valueOf(kakaoId)); - String newRefreshToken = jwtUtil.createRefreshToken(String.valueOf(kakaoId)); + String newAccessToken = jwtUtil.createAccessToken(kakaoId); + String newRefreshToken = jwtUtil.createRefreshToken(kakaoId); // Refresh Token 업데이트 member.setRefreshToken(newRefreshToken);