diff --git a/build.gradle b/build.gradle index d33e61a4..7ca3092f 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { } group = 'com.numberone.backend' -version = '0.0.1-SNAPSHOT' +version = '0.0.2-SNAPSHOT' allprojects { apply plugin: 'java' @@ -19,7 +19,7 @@ allprojects { } ext { - set('springCloudVersion', "2021.0.1") + set('springCloudVersion', "2022.0.5") } subprojects { diff --git a/daepiro-api/src/main/java/com/numberone/backend/domain/article/service/ArticleService.java b/daepiro-api/src/main/java/com/numberone/backend/domain/article/service/ArticleService.java index b60d83ee..e85d9a6a 100644 --- a/daepiro-api/src/main/java/com/numberone/backend/domain/article/service/ArticleService.java +++ b/daepiro-api/src/main/java/com/numberone/backend/domain/article/service/ArticleService.java @@ -65,8 +65,8 @@ public class ArticleService { @Transactional public UploadArticleResponse uploadArticle(UploadArticleRequest request) { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member owner = memberRepository.findByEmail(principal) + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member owner = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); // 1. 게시글 생성 ( 제목, 내용, 작성자 아이디, 태그) @@ -148,8 +148,8 @@ public DeleteArticleResponse deleteArticle(Long articleId) { } public GetArticleDetailResponse getArticleDetail(Long articleId) { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member member = memberRepository.findByEmail(principal) // 회원 + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(id) // 회원 .orElseThrow(NotFoundMemberException::new); Article article = articleRepository.findById(articleId) .orElseThrow(NotFoundArticleException::new); @@ -179,8 +179,8 @@ public GetArticleDetailResponse getArticleDetail(Long articleId) { } public Slice getArticleListPaging(ArticleSearchParameter param, Pageable pageable) { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member member = memberRepository.findByEmail(principal) + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); List memberLikedArticleIdList = articleLikeRepository.findByMember(member) .stream().map(ArticleLike::getArticleId) @@ -207,8 +207,8 @@ public void updateArticleInfo(GetArticleListResponse articleInfo, List mem @Transactional public CreateCommentResponse createComment(Long articleId, CreateCommentRequest request) { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member member = memberRepository.findByEmail(principal) + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); Article article = articleRepository.findById(articleId) .orElseThrow(NotFoundArticleException::new); @@ -236,8 +236,8 @@ public CreateCommentResponse createComment(Long articleId, CreateCommentRequest @Transactional public ModifyArticleResponse modifyArticle(Long articleId, ModifyArticleRequest request) { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member member = memberRepository.findByEmail(principal) + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); Article article = articleRepository.findById(articleId) .orElseThrow(NotFoundArticleException::new); diff --git a/daepiro-api/src/main/java/com/numberone/backend/domain/comment/service/CommentService.java b/daepiro-api/src/main/java/com/numberone/backend/domain/comment/service/CommentService.java index 2e7ea551..ab9d268f 100644 --- a/daepiro-api/src/main/java/com/numberone/backend/domain/comment/service/CommentService.java +++ b/daepiro-api/src/main/java/com/numberone/backend/domain/comment/service/CommentService.java @@ -49,8 +49,8 @@ public CreateChildCommentResponse createChildComment( Long parentCommentId, CreateChildCommentRequest request) { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member member = memberRepository.findByEmail(principal) + long principal = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(principal) .orElseThrow(NotFoundMemberException::new); Article article = articleRepository.findById(articleId) .orElseThrow(NotFoundArticleException::new); @@ -78,8 +78,8 @@ public CreateChildCommentResponse createChildComment( } public List getCommentsByArticle(Long articleId) { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member member = memberRepository.findByEmail(principal) + long principal = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(principal) .orElseThrow(NotFoundMemberException::new); Article article = articleRepository.findById(articleId) .orElseThrow(NotFoundArticleException::new); diff --git a/daepiro-api/src/main/java/com/numberone/backend/domain/conversation/controller/ConversationController.java b/daepiro-api/src/main/java/com/numberone/backend/domain/conversation/controller/ConversationController.java index 87e9d93b..cb2851b6 100644 --- a/daepiro-api/src/main/java/com/numberone/backend/domain/conversation/controller/ConversationController.java +++ b/daepiro-api/src/main/java/com/numberone/backend/domain/conversation/controller/ConversationController.java @@ -59,14 +59,14 @@ public void delete(@PathVariable Long conversationId) { """) @PostMapping("/like/{conversationId}") public void increaseLike(Authentication authentication, @PathVariable Long conversationId) { - conversationService.increaseLike(authentication.getName(), conversationId); + conversationService.increaseLike(conversationId); } @Operation(summary = "대화 좋아요 취소하기", description = """ 사용자가 대화의 좋아요를 취소할 때 대화 id를 파라미터로 전달해주세요. """) @DeleteMapping("/like/{conversationId}") - public void decreaseLike(Authentication authentication, @PathVariable Long conversationId) { - conversationService.decreaseLike(authentication.getName(), conversationId); + public void decreaseLike(@PathVariable Long conversationId) { + conversationService.decreaseLike(conversationId); } } diff --git a/daepiro-api/src/main/java/com/numberone/backend/domain/conversation/service/ConversationService.java b/daepiro-api/src/main/java/com/numberone/backend/domain/conversation/service/ConversationService.java index 7881d49a..3adfb3e5 100644 --- a/daepiro-api/src/main/java/com/numberone/backend/domain/conversation/service/ConversationService.java +++ b/daepiro-api/src/main/java/com/numberone/backend/domain/conversation/service/ConversationService.java @@ -15,6 +15,7 @@ import com.numberone.backend.exception.conflict.AlreadyUnLikedException; import com.numberone.backend.exception.notfound.NotFoundConversationException; import com.numberone.backend.exception.notfound.NotFoundDisasterException; +import com.numberone.backend.provider.security.SecurityContextProvider; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -33,7 +34,8 @@ public class ConversationService { @Transactional public void createConversation(String email, CreateConversationRequest createConversationRequest) { - Member member = memberService.findByEmail(email); + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberService.findById(id); Disaster disaster = disasterRepository.findById(createConversationRequest.getDisasterId()) .orElseThrow(NotFoundDisasterException::new); Conversation conversation = Conversation.of( @@ -46,7 +48,8 @@ public void createConversation(String email, CreateConversationRequest createCon @Transactional public void createChildConversation(String email, CreateChildConversationRequest createConversationRequest, Long conversationId) { - Member member = memberService.findByEmail(email); + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberService.findById(id); Conversation parent = conversationRepository.findById(conversationId) .orElseThrow(NotFoundConversationException::new); Conversation conversation = Conversation.childOf( @@ -73,10 +76,11 @@ private boolean checkLike(Member member, Conversation conversation) { return false; } - public GetConversationResponse get(String email, Long conversationId) { + public GetConversationResponse get(Long conversationId) { Conversation conversation = conversationRepository.findById(conversationId) .orElseThrow(NotFoundConversationException::new); - Member member = memberService.findByEmail(email); + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberService.findById(id); List childs = new ArrayList<>(); List childConversations = conversationRepository.findAllByParentOrderByCreatedAt(conversation); for (Conversation child : childConversations) { @@ -94,10 +98,11 @@ public GetConversationResponse get(String email, Long conversationId) { childs); } - public GetConversationResponse getExceptChild(String email, Long conversationId) { + public GetConversationResponse getExceptChild(Long conversationId) { Conversation conversation = conversationRepository.findById(conversationId) .orElseThrow(NotFoundConversationException::new); - Member member = memberService.findByEmail(email); + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberService.findById(id); return GetConversationResponse.of( conversation, checkLike(member, conversation), @@ -106,8 +111,9 @@ public GetConversationResponse getExceptChild(String email, Long conversationId) } @Transactional - public void increaseLike(String email, Long conversationId) { - Member member = memberService.findByEmail(email); + public void increaseLike(Long conversationId) { + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberService.findById(id); Conversation conversation = conversationRepository.findById(conversationId) .orElseThrow(NotFoundConversationException::new); conversationLikeRepository.findByMemberAndConversation(member, conversation) @@ -120,8 +126,9 @@ public void increaseLike(String email, Long conversationId) { } @Transactional - public void decreaseLike(String email, Long conversationId) { - Member member = memberService.findByEmail(email); + public void decreaseLike(Long conversationId) { + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberService.findById(id); Conversation conversation = conversationRepository.findById(conversationId) .orElseThrow(NotFoundConversationException::new); ConversationLike conversationLike = conversationLikeRepository.findByMemberAndConversation(member, conversation) diff --git a/daepiro-api/src/main/java/com/numberone/backend/domain/disaster/controller/DisasterController.java b/daepiro-api/src/main/java/com/numberone/backend/domain/disaster/controller/DisasterController.java index c9c42fc4..6e871954 100644 --- a/daepiro-api/src/main/java/com/numberone/backend/domain/disaster/controller/DisasterController.java +++ b/daepiro-api/src/main/java/com/numberone/backend/domain/disaster/controller/DisasterController.java @@ -29,16 +29,16 @@ public class DisasterController { 유저가 등록한 지역은 유저가 인증을 위해 같이 보내야하는 jwt 토큰으로부터 알아서 추출해서 처리할 것입니다. """) @PostMapping("/latest") - public ResponseEntity getLatestDisaster(Authentication authentication, @Valid @RequestBody LatestDisasterRequest latestDisasterRequest) { - return ResponseEntity.ok(disasterService.getLatestDisaster(authentication.getName(), latestDisasterRequest)); + public ResponseEntity getLatestDisaster(@Valid @RequestBody LatestDisasterRequest latestDisasterRequest) { + return ResponseEntity.ok(disasterService.getLatestDisaster(latestDisasterRequest)); } @Operation(summary = "재난상황 커뮤니티 데이터 가져오기", description = """ 재난상황 페이지에서 필요한 재난목록과 그와 관련된 대화(댓글)들을 가져옵니다. """) @GetMapping("/situation") - public ResponseEntity getSituationHome(Authentication authentication) { - return ResponseEntity.ok(disasterService.getSituationHome(authentication.getName())); + public ResponseEntity getSituationHome() { + return ResponseEntity.ok(disasterService.getSituationHome()); } @Operation(summary = "해당 재난과 관련된 모든 커뮤니티 대화 가져오기", description = """ @@ -47,7 +47,7 @@ public ResponseEntity getSituationHome(Authentication aut 커뮤니티-재난상황-댓글더보기 페이지에서 사용하는 API입니다. """) @GetMapping("/{sort}/{disasterId}") - public ResponseEntity getSituationDetail(Authentication authentication, @PathVariable Long disasterId, @PathVariable String sort) { - return ResponseEntity.ok(disasterService.getSituationDetail(authentication.getName(), disasterId, sort)); + public ResponseEntity getSituationDetail(@PathVariable Long disasterId, @PathVariable String sort) { + return ResponseEntity.ok(disasterService.getSituationDetail(disasterId, sort)); } } diff --git a/daepiro-api/src/main/java/com/numberone/backend/domain/disaster/service/DisasterService.java b/daepiro-api/src/main/java/com/numberone/backend/domain/disaster/service/DisasterService.java index b489a151..1877756d 100644 --- a/daepiro-api/src/main/java/com/numberone/backend/domain/disaster/service/DisasterService.java +++ b/daepiro-api/src/main/java/com/numberone/backend/domain/disaster/service/DisasterService.java @@ -21,6 +21,7 @@ import com.numberone.backend.exception.badrequest.BadRequestConversationSortException; import com.numberone.backend.exception.notfound.NotFoundDisasterException; import com.numberone.backend.provider.location.LocationProvider; +import com.numberone.backend.provider.security.SecurityContextProvider; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEventPublisher; @@ -45,11 +46,12 @@ public class DisasterService { private final ApplicationEventPublisher eventPublisher; @Transactional - public LatestDisasterResponse getLatestDisaster(String email, LatestDisasterRequest latestDisasterRequest) { + public LatestDisasterResponse getLatestDisaster(LatestDisasterRequest latestDisasterRequest) { String address = locationProvider.pos2address(latestDisasterRequest.getLatitude(), latestDisasterRequest.getLongitude()); LocalDateTime time = LocalDateTime.now().minusDays(1); Set disasters = new HashSet<>(disasterRepository.findDisastersInAddressAfterTime(address, time)); - Member member = memberService.findByEmail(email); + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberService.findById(id); member.updateGps(latestDisasterRequest.getLatitude(), latestDisasterRequest.getLongitude(), address); String[] locationTokens = member.getLocation().split(" "); switch (locationTokens.length) { @@ -109,9 +111,10 @@ private boolean isValidDisasterType(DisasterType disasterType, List disasters = new HashSet<>(); - Member member = memberService.findByEmail(email); + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberService.findById(id); LocalDateTime time = LocalDateTime.now().minusDays(1); for (NotificationRegion notificationRegion : member.getNotificationRegions()) { disasters.addAll(disasterRepository.findDisastersInAddressAfterTime(notificationRegion.getLocation(), time)); @@ -126,7 +129,7 @@ public SituationHomeResponse getSituationHome(String email) { conversationCnt += conversationRepository.countByDisaster(disaster); List conversations = conversationRepository.findAllByDisasterOrderByLikeCntDesc(disaster, PageRequest.of(0, 3)); for (Conversation conversation : conversations) { - conversationResponses.add(conversationService.getExceptChild(email, conversation.getId())); + conversationResponses.add(conversationService.getExceptChild(conversation.getId())); conversationCnt += conversationRepository.countByParent(conversation); } situationResponses.add(SituationResponse.of(disaster, conversationResponses, conversationCnt)); @@ -135,7 +138,7 @@ public SituationHomeResponse getSituationHome(String email) { return SituationHomeResponse.of(situationResponses); } - public SituationDetailResponse getSituationDetail(String email, Long disasterId, String sort) { + public SituationDetailResponse getSituationDetail(Long disasterId, String sort) { Disaster disaster = disasterRepository.findById(disasterId) .orElseThrow(NotFoundDisasterException::new); List conversationResponses = new ArrayList<>(); @@ -147,7 +150,7 @@ else if (sort.equals("time")) else throw new BadRequestConversationSortException(); for (Conversation conversation : conversations) { - conversationResponses.add(conversationService.get(email, conversation.getId())); + conversationResponses.add(conversationService.get(conversation.getId())); } return SituationDetailResponse.of(conversationResponses); } diff --git a/daepiro-api/src/main/java/com/numberone/backend/domain/friendship/service/FriendshipService.java b/daepiro-api/src/main/java/com/numberone/backend/domain/friendship/service/FriendshipService.java index a55475c6..a39fb076 100644 --- a/daepiro-api/src/main/java/com/numberone/backend/domain/friendship/service/FriendshipService.java +++ b/daepiro-api/src/main/java/com/numberone/backend/domain/friendship/service/FriendshipService.java @@ -35,8 +35,8 @@ public class FriendshipService { @Transactional public InviteFriendResponse inviteFriend(Long memberId) { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member invitedMember = memberRepository.findByEmail(principal) + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member invitedMember = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); Member member = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); @@ -55,8 +55,8 @@ public InviteFriendResponse inviteFriend(Long memberId) { } public List getFriends() { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member member = memberRepository.findByEmail(principal) + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); return member.getFriendships() .stream().map(friendship -> { @@ -67,8 +67,8 @@ public List getFriends() { @Transactional public FriendResponse deleteFriend(Long memberId) { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member me = memberRepository.findByEmail(principal) + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member me = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); Member friend = memberRepository.findById(memberId) .orElseThrow(NotFoundMemberException::new); @@ -83,8 +83,8 @@ public FriendResponse deleteFriend(Long memberId) { @Transactional public SendFcmFriendResponse sendFcmToFriend(Long friendId) { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member member = memberRepository.findByEmail(principal) + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); Member friend = memberRepository.findById(friendId) .orElseThrow(NotFoundMemberException::new); diff --git a/daepiro-api/src/main/java/com/numberone/backend/domain/like/service/LikeService.java b/daepiro-api/src/main/java/com/numberone/backend/domain/like/service/LikeService.java index f8a30c9a..8a97a5f7 100644 --- a/daepiro-api/src/main/java/com/numberone/backend/domain/like/service/LikeService.java +++ b/daepiro-api/src/main/java/com/numberone/backend/domain/like/service/LikeService.java @@ -45,8 +45,8 @@ public class LikeService { @Transactional public Integer increaseArticleLike(Long articleId) { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member member = memberRepository.findByEmail(principal) + long principal = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(principal) .orElseThrow(NotFoundMemberException::new); Article article = articleRepository.findById(articleId) .orElseThrow(NotFoundApiException::new); @@ -75,8 +75,8 @@ public Integer increaseArticleLike(Long articleId) { @Transactional public Integer decreaseArticleLike(Long articleId) { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member member = memberRepository.findByEmail(principal) + long principal = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(principal) .orElseThrow(NotFoundMemberException::new); Article article = articleRepository.findById(articleId) .orElseThrow(NotFoundApiException::new); @@ -95,8 +95,8 @@ public Integer decreaseArticleLike(Long articleId) { @Transactional public Integer increaseCommentLike(Long commentId) { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member member = memberRepository.findByEmail(principal) + long principal = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(principal) .orElseThrow(NotFoundMemberException::new); CommentEntity commentEntity = commentRepository.findById(commentId) .orElseThrow(NotFoundCommentException::new); @@ -127,8 +127,8 @@ public Integer increaseCommentLike(Long commentId) { @Transactional public Integer decreaseCommentLike(Long commentId) { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member member = memberRepository.findByEmail(principal) + long principal = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(principal) .orElseThrow(NotFoundMemberException::new); CommentEntity commentEntity = commentRepository.findById(commentId) .orElseThrow(NotFoundCommentException::new); diff --git a/daepiro-api/src/main/java/com/numberone/backend/domain/member/controller/MemberController.java b/daepiro-api/src/main/java/com/numberone/backend/domain/member/controller/MemberController.java index 415a940c..a6e9f0a6 100644 --- a/daepiro-api/src/main/java/com/numberone/backend/domain/member/controller/MemberController.java +++ b/daepiro-api/src/main/java/com/numberone/backend/domain/member/controller/MemberController.java @@ -53,24 +53,24 @@ public ResponseEntity uploadMemberProfileImage(@Requ response 에는 구입한 후에 사용자의 현재 마음 갯수가 저장되어 있습니다. """) - public ResponseEntity buyHeart(@RequestBody @Valid BuyHeartRequest buyHeartRequest, Authentication authentication) { - return ResponseEntity.status(HttpStatus.CREATED).body(memberService.buyHeart(buyHeartRequest, authentication.getName())); + public ResponseEntity buyHeart(@RequestBody @Valid BuyHeartRequest buyHeartRequest) { + return ResponseEntity.status(HttpStatus.CREATED).body(memberService.buyHeart(buyHeartRequest)); } @GetMapping("/heart") @Operation(summary = "사용자의 현재 마음 갯수 가져오기", description = """ 사용자의 현재 마음 갯수가 response로 전달됩니다. """) - public ResponseEntity getHeart(Authentication authentication) { - return ResponseEntity.ok(memberService.getHeart(authentication.getName())); + public ResponseEntity getHeart() { + return ResponseEntity.ok(memberService.getHeart()); } @Operation(summary = "온보딩시 사용자 초기 데이터 설정하기", description = """ 온보딩에서 선택한 닉네임, 재난유형, 알림지역 데이터를 body에 담아 전달해주세요. """) @PostMapping("/onboarding") - public void initMemberData(Authentication authentication, @Valid @RequestBody OnboardingRequest onboardingRequest) { - memberService.initMemberData(authentication.getName(), onboardingRequest); + public void initMemberData(@Valid @RequestBody OnboardingRequest onboardingRequest) { + memberService.initMemberData(onboardingRequest); } @Operation(summary = "사용자가 온보딩 시 추가한 지역 리스트 가져오기", description = """ @@ -83,37 +83,37 @@ public ResponseEntity getNotificationRegions() { @Operation(summary = "온라인 전환 API", description = """ 사용자가 어플을 시작할 때 이 API를 호출해 온라인 상태가 되었음을 서버에 알려주세요. - + 또한 가족 초대를 위해 필요한 사용자의 id값을 이때 응답으로 반환해줍니다. """) @GetMapping("/online") - public ResponseEntity online(Authentication authentication){ - return ResponseEntity.ok(memberService.online(authentication.getName())); + public ResponseEntity online() { + return ResponseEntity.ok(memberService.online()); } @Operation(summary = "오프라인 전환 API", description = """ 사용자가 어플을 종료할 때 이 API를 호출해 온라인 상태가 되었음을 서버에 알려주세요. """) @GetMapping("/offline") - public void offline(Authentication authentication){ - memberService.offline(authentication.getName()); + public void offline() { + memberService.offline(); } @Operation(summary = "사용자 GPS 위치 변경", description = """ 사용자의 GPS 위치(위도, 경도) 정보를 갱신할 때 사용해주세요. - + 위도, 경도를 body에 담아 전달해주세요. """) @PostMapping("/gps") - public void updateGps(Authentication authentication, @Valid @RequestBody UpdateGpsRequest updateGpsRequest){ - memberService.updateGps(authentication.getName(), updateGpsRequest); + public void updateGps(@Valid @RequestBody UpdateGpsRequest updateGpsRequest) { + memberService.updateGps(updateGpsRequest); } @Operation(summary = "사용자 안전상태 업데이트", description = """ 사용자의 안전상태를 업데이트 하는 API 입니다. """) @PutMapping("/safety") - public ResponseEntity updateSafety(@Valid @RequestBody UpdateSafetyRequest request){ + public ResponseEntity updateSafety(@Valid @RequestBody UpdateSafetyRequest request) { memberService.updateSafety(request); return ResponseEntity.ok().build(); } diff --git a/daepiro-api/src/main/java/com/numberone/backend/domain/member/service/MemberService.java b/daepiro-api/src/main/java/com/numberone/backend/domain/member/service/MemberService.java index 1fa37dba..ed770b2a 100644 --- a/daepiro-api/src/main/java/com/numberone/backend/domain/member/service/MemberService.java +++ b/daepiro-api/src/main/java/com/numberone/backend/domain/member/service/MemberService.java @@ -33,14 +33,33 @@ public class MemberService { private final NotificationRegionRepository notificationRegionRepository; private final LocationProvider locationProvider; - public Member findByEmail(String email) { - return memberRepository.findByEmail(email) + public Member findById(long id) { + return memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); } @Transactional - public void initMemberData(String email, OnboardingRequest onboardingRequest) { - Member member = memberRepository.findByEmail(email) + public Member create(long socialId) { + return memberRepository.save(Member.of(socialId)); + } + + public Member findBySocialId(long socialId) { + return memberRepository.findBySocialId(socialId) + .orElse(null); + } + + public boolean existsBySocialId(long socialId) { + return memberRepository.existsBySocialId(socialId); + } + + public boolean existsById(long id){ + return memberRepository.existsById(id); + } + + @Transactional + public void initMemberData(OnboardingRequest onboardingRequest) { + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); notificationDisasterRepository.deleteAllByMemberId(member.getId()); member.setOnboardingData(onboardingRequest.getRealname(), onboardingRequest.getNickname(), onboardingRequest.getFcmToken()); @@ -62,23 +81,25 @@ public void initMemberData(String email, OnboardingRequest onboardingRequest) { } @Transactional - public HeartCntResponse buyHeart(BuyHeartRequest buyHeartRequest, String email) { - Member member = memberRepository.findByEmail(email) + public HeartCntResponse buyHeart(BuyHeartRequest buyHeartRequest) { + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); member.plusHeart(buyHeartRequest.getHeartCnt()); return HeartCntResponse.of(member); } - public HeartCntResponse getHeart(String email) { - Member member = memberRepository.findByEmail(email) + public HeartCntResponse getHeart() { + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); return HeartCntResponse.of(member); } @Transactional public UploadProfileImageResponse uploadProfileImage(MultipartFile image) { - String email = SecurityContextProvider.getAuthenticatedUserEmail(); - Member member = memberRepository.findByEmail(email) + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); String imageUrl = s3Provider.uploadImage(image); @@ -90,28 +111,31 @@ public UploadProfileImageResponse uploadProfileImage(MultipartFile image) { } public GetNotificationRegionResponse getNotificationRegionLv2() { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member member = memberRepository.findByEmail(principal) + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); return GetNotificationRegionResponse.of(member.getNotificationRegions()); } @Transactional - public MemberIdResponse online(String email) { - Member member = findByEmail(email); + public MemberIdResponse online() { + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = findById(id); member.updateSession(true); return MemberIdResponse.of(member); } @Transactional - public void offline(String email) { - Member member = findByEmail(email); + public void offline() { + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = findById(id); member.updateSession(false); } @Transactional - public void updateGps(String email, UpdateGpsRequest updateGpsRequest) { - Member member = findByEmail(email); + public void updateGps(UpdateGpsRequest updateGpsRequest) { + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = findById(id); Double latitude = updateGpsRequest.getLatitude(); Double longitude = updateGpsRequest.getLongitude(); String location = locationProvider.pos2address(latitude, longitude); @@ -139,8 +163,8 @@ public void updateGps(String email, UpdateGpsRequest updateGpsRequest) { @Transactional public void updateSafety(UpdateSafetyRequest request) { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member member = memberRepository.findByEmail(principal) + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); member.updateSafety(request.getIsSafety()); } diff --git a/daepiro-api/src/main/java/com/numberone/backend/domain/notification/service/NotificationService.java b/daepiro-api/src/main/java/com/numberone/backend/domain/notification/service/NotificationService.java index ca724f96..867d6d37 100644 --- a/daepiro-api/src/main/java/com/numberone/backend/domain/notification/service/NotificationService.java +++ b/daepiro-api/src/main/java/com/numberone/backend/domain/notification/service/NotificationService.java @@ -23,8 +23,8 @@ public class NotificationService { private final NotificationRepository notificationRepository; public Slice getNotificationTabPagesByMember(NotificationSearchParameter param, Pageable pageable) { - String principal = SecurityContextProvider.getAuthenticatedUserEmail(); - Member member = memberRepository.findByEmail(principal) + long principal = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(principal) .orElseThrow(NotFoundMemberException::new); return notificationRepository.getNotificationTabPagesNoOffSetByMember(param, member.getId(), pageable); } diff --git a/daepiro-api/src/main/java/com/numberone/backend/domain/sponsor/controller/SponsorController.java b/daepiro-api/src/main/java/com/numberone/backend/domain/sponsor/controller/SponsorController.java index 2116bd96..d165e135 100644 --- a/daepiro-api/src/main/java/com/numberone/backend/domain/sponsor/controller/SponsorController.java +++ b/daepiro-api/src/main/java/com/numberone/backend/domain/sponsor/controller/SponsorController.java @@ -39,8 +39,8 @@ public ResponseEntity getSponsorDetail(@PathVariable Long spons 후원 목록 가져오기 API에서 반환된 응답에서 얻은 후원 id중 가져오고 싶은 후원의 id를 같이 전달해주세요. """) @GetMapping("/latest") - public ResponseEntity getSponsorHomeLatest(Authentication authentication){ - return ResponseEntity.ok(sponsorService.getSponsorHomeLatest(authentication.getName())); + public ResponseEntity getSponsorHomeLatest(){ + return ResponseEntity.ok(sponsorService.getSponsorHomeLatest()); } @Operation(summary = "후원 목록 인기순 가져오기", description = @@ -52,7 +52,7 @@ public ResponseEntity getSponsorHomeLatest(Authentication a 후원 목록 가져오기 API에서 반환된 응답에서 얻은 후원 id중 가져오고 싶은 후원의 id를 같이 전달해주세요. """) @GetMapping("/popular") - public ResponseEntity getSponsorHomePopular(Authentication authentication){ - return ResponseEntity.ok(sponsorService.getSponsorHomePopular(authentication.getName())); + public ResponseEntity getSponsorHomePopular(){ + return ResponseEntity.ok(sponsorService.getSponsorHomePopular()); } } diff --git a/daepiro-api/src/main/java/com/numberone/backend/domain/sponsor/service/SponsorService.java b/daepiro-api/src/main/java/com/numberone/backend/domain/sponsor/service/SponsorService.java index a8042e43..bb4aa843 100644 --- a/daepiro-api/src/main/java/com/numberone/backend/domain/sponsor/service/SponsorService.java +++ b/daepiro-api/src/main/java/com/numberone/backend/domain/sponsor/service/SponsorService.java @@ -10,6 +10,7 @@ import com.numberone.backend.domain.support.repository.SupportRepository; import com.numberone.backend.exception.notfound.NotFoundMemberException; import com.numberone.backend.exception.notfound.NotFoundSponsorException; +import com.numberone.backend.provider.security.SecurityContextProvider; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -34,9 +35,10 @@ public SponsorResponse getSponsorDetail(Long sponsorId) { ); } - public SponsorHomeResponse getSponsorHomeLatest(String email) { + public SponsorHomeResponse getSponsorHomeLatest() { List sponsorResponses = new ArrayList<>(); - Member member = memberRepository.findByEmail(email) + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); List sponsors = sponsorRepository.findAllByOrderByStartDateDesc(); for (Sponsor sponsor : sponsors) { @@ -65,9 +67,10 @@ public SponsorHomeResponse getSponsorHomeLatest(String email) { ); } - public SponsorHomeResponse getSponsorHomePopular(String email) { + public SponsorHomeResponse getSponsorHomePopular() { List sponsorResponses = new ArrayList<>(); - Member member = memberRepository.findByEmail(email) + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); List sponsors = sponsorRepository.findAllByOrderByCurrentHeartDesc(); for (Sponsor sponsor : sponsors) { diff --git a/daepiro-api/src/main/java/com/numberone/backend/domain/support/service/SupportService.java b/daepiro-api/src/main/java/com/numberone/backend/domain/support/service/SupportService.java index e4f9463b..1402df52 100644 --- a/daepiro-api/src/main/java/com/numberone/backend/domain/support/service/SupportService.java +++ b/daepiro-api/src/main/java/com/numberone/backend/domain/support/service/SupportService.java @@ -12,6 +12,7 @@ import com.numberone.backend.exception.badrequest.BadRequestHeartException; import com.numberone.backend.exception.notfound.NotFoundMemberException; import com.numberone.backend.exception.notfound.NotFoundSupportException; +import com.numberone.backend.provider.security.SecurityContextProvider; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -35,7 +36,8 @@ public void editMessage(EditMessageRequest editMessageRequest, Long supportId) { public CreateSupportResponse create(CreateSupportRequest createSupportRequest, String email) { Sponsor sponsor = sponsorRepository.findById(createSupportRequest.getSponsorId()) .orElseThrow(NotFoundSupportException::new); - Member member = memberRepository.findByEmail(email) + long id = SecurityContextProvider.getAuthenticatedUserId(); + Member member = memberRepository.findById(id) .orElseThrow(NotFoundMemberException::new); if (member.getHeartCnt() < createSupportRequest.getHeartCnt()) throw new BadRequestHeartException(); diff --git a/daepiro-app/build.gradle b/daepiro-app/build.gradle index d2cb9145..1bf41517 100644 --- a/daepiro-app/build.gradle +++ b/daepiro-app/build.gradle @@ -9,6 +9,9 @@ dependencies { implementation project(':daepiro-api') implementation project(':daepiro-crawler') implementation project(':daepiro-auth') + + // feign client + implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' } // system environment variables diff --git a/daepiro-app/src/main/java/com/numberone/backend/BackendApplication.java b/daepiro-app/src/main/java/com/numberone/backend/BackendApplication.java index 3fb21b3a..4872e8fb 100644 --- a/daepiro-app/src/main/java/com/numberone/backend/BackendApplication.java +++ b/daepiro-app/src/main/java/com/numberone/backend/BackendApplication.java @@ -1,14 +1,19 @@ package com.numberone.backend; import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.cloud.openfeign.FeignAutoConfiguration; @SpringBootApplication @ServletComponentScan +@EnableFeignClients +@ImportAutoConfiguration({FeignAutoConfiguration.class})//https://stackoverflow.com/questions/74593433/consider-defining-a-bean-of-type-org-springframework-cloud-openfeign-feignconte public class BackendApplication { - public static void main(String[] args) { - SpringApplication.run(BackendApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(BackendApplication.class, args); + } } diff --git a/daepiro-auth/build.gradle b/daepiro-auth/build.gradle index 569d8f09..1f7a8026 100644 --- a/daepiro-auth/build.gradle +++ b/daepiro-auth/build.gradle @@ -2,16 +2,20 @@ dependencies { implementation project(':daepiro-core') implementation project(':daepiro-redis') implementation project(':daepiro-common') + implementation project(':daepiro-api') // oauth implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' - // jwt - implementation 'io.jsonwebtoken:jjwt-api:0.11.5' - runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' - runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + // security + implementation 'org.springframework.boot:spring-boot-starter-security' + + // FeignClient + implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' + // jwt + implementation 'io.jsonwebtoken:jjwt:0.12.3' } bootJar { diff --git a/daepiro-auth/src/main/java/com/numberone/backend/config/SecurityConfig.java b/daepiro-auth/src/main/java/com/numberone/backend/config/SecurityConfig.java new file mode 100644 index 00000000..87f810e5 --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/config/SecurityConfig.java @@ -0,0 +1,53 @@ +package com.numberone.backend.config; + +import com.numberone.backend.filter.JwtAccessFilter; +import com.numberone.backend.filter.JwtExceptionFilter; +import com.numberone.backend.filter.JwtRefreshFilter; +import com.numberone.backend.filter.SocialAuthenticationFilter; +import com.numberone.backend.handler.CustomAccessDeniedHandler; +import com.numberone.backend.handler.CustomAuthenticationEntryPoint; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.access.ExceptionTranslationFilter; +import org.springframework.security.web.authentication.logout.LogoutFilter; + +@Configuration +@RequiredArgsConstructor +public class SecurityConfig { + private final SocialAuthenticationFilter socialAuthenticationFilter; + private final JwtAccessFilter jwtAccessFilter; + private final JwtRefreshFilter jwtRefreshFilter; + private final JwtExceptionFilter jwtExceptionFilter; + private final CustomAccessDeniedHandler customAccessDeniedHandler; + private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint; + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .csrf(c -> c.disable()) + .authorizeHttpRequests(c -> c + .requestMatchers("/h2-console/**", + "/favicon.ico", + "/error", + "/swagger-ui/**", + "/swagger-resources/**", + "/v3/api-docs/**", + "/token/**", + "/notification/send-fcm", + "/hello").permitAll() + .anyRequest().authenticated()) + .addFilterBefore(socialAuthenticationFilter, ExceptionTranslationFilter.class) + .addFilterBefore(jwtAccessFilter, SocialAuthenticationFilter.class) + .addFilterBefore(jwtRefreshFilter, JwtAccessFilter.class) + .addFilterBefore(jwtExceptionFilter, JwtRefreshFilter.class) + .exceptionHandling(c -> c + .authenticationEntryPoint(customAuthenticationEntryPoint) + .accessDeniedHandler(customAccessDeniedHandler)) + .sessionManagement(c -> c.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + return http.build(); + } +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/config/SecurityConfig.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/config/SecurityConfig.java index 6d88f871..e69de29b 100644 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/config/SecurityConfig.java +++ b/daepiro-auth/src/main/java/com/numberone/backend/domain/config/SecurityConfig.java @@ -1,60 +0,0 @@ -package com.numberone.backend.domain.config; - -import com.numberone.backend.domain.filter.JwtFilter; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint; -import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; -import org.springframework.web.servlet.handler.HandlerMappingIntrospector; - -@Slf4j -@Configuration -@EnableWebSecurity -@RequiredArgsConstructor -public class SecurityConfig { - private final JwtFilter jwtFilter; - - @Bean - public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception { - MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector); - http - .httpBasic(httpBasic -> httpBasic.disable()) - .csrf(csrf -> csrf.disable()) - .cors(Customizer.withDefaults()) - .authorizeHttpRequests((auth) -> auth - .anyRequest().authenticated() // 모든 요청에 대한 인증 처리하도록 설정 - ) - .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) - .exceptionHandling(exception -> exception - .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint()) - .accessDeniedHandler(new BearerTokenAccessDeniedHandler())); - ; - return http.build(); - } - - @Bean - public WebSecurityCustomizer webSecurityCustomizer() { - return (web) -> web.ignoring() - .requestMatchers("/h2-console/**", - "/favicon.ico", - "/error", - "/swagger-ui/**", - "/swagger-resources/**", - "/v3/api-docs/**", - "/token/**", - "/notification/send-fcm", - "/hello"); - //.requestMatchers("/**"); // 인증 처리 하지 않을 케이스 - } -} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/filter/JwtFilter.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/filter/JwtFilter.java deleted file mode 100644 index 496c011a..00000000 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/filter/JwtFilter.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.numberone.backend.domain.filter; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.numberone.backend.domain.member.entity.Member; -import com.numberone.backend.domain.member.repository.MemberRepository; -import com.numberone.backend.domain.service.JwtProvider; -import com.numberone.backend.exception.context.ExceptionContext; -import com.numberone.backend.exception.dto.ErrorResponse; -import com.numberone.backend.exception.notfound.NotFoundMemberException; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; -import java.util.Collections; - -import static com.numberone.backend.exception.context.CustomExceptionContext.*; - -@RequiredArgsConstructor -@Component -public class JwtFilter extends OncePerRequestFilter { - - private final JwtProvider jwtProvider; - private final MemberRepository memberRepository; - - @Override - protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { - String path = request.getRequestURI(); - return !path.startsWith("/api/"); - // /api로 시작하는 경로에 대해서만 jwt 인증을 진행합니다. 이렇게 안하면 security에서 무시한 경로라도 모든 경로에 대해서 이 필터를 거치네요.. - // jwt인증이 필요한 api에 대해서는 /api/apple 처럼 /api로 시작하게 만들어야 될것같아요! - } - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION); - if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) { - setErrorResponse(response, WRONG_ACCESS_TOKEN); - return; - } - String token = authorizationHeader.split(" ")[1]; - if (!jwtProvider.isValid(token)) { - setErrorResponse(response, WRONG_ACCESS_TOKEN); - return; - } - if (jwtProvider.isExpired(token)) { - setErrorResponse(response, EXPIRED_ACCESS_TOKEN); - return; - } - - String email = jwtProvider.getEmail(token); - try { - Member member = memberRepository.findByEmail(email) - .orElseThrow(NotFoundMemberException::new); - UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( - member.getEmail(), null, Collections.emptyList()); - authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - - SecurityContextHolder.getContext().setAuthentication(authenticationToken); - filterChain.doFilter(request, response); - } catch (NotFoundMemberException e) { - setErrorResponse(response, NOT_FOUND_MEMBER); - return; - } - } - - private void setErrorResponse( - HttpServletResponse response, - ExceptionContext context - ) { - ObjectMapper objectMapper = new ObjectMapper(); - response.setStatus(HttpStatus.FORBIDDEN.value()); - response.setContentType("application/json;charset=UTF-8"); - ErrorResponse errorResponse = new ErrorResponse(context.getCode(), context.getMessage()); - try { - response.getWriter().write(objectMapper.writeValueAsString(errorResponse)); - } catch (IOException e) { - e.printStackTrace(); - } - } -} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/properties/JwtProperties.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/properties/JwtProperties.java deleted file mode 100644 index ed78b8ca..00000000 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/properties/JwtProperties.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.numberone.backend.domain.properties; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -@Data -@Component -@ConfigurationProperties(prefix = "spring.jwt") -public class JwtProperties { - private String secret; -} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/properties/KakaoProperties.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/properties/KakaoProperties.java deleted file mode 100644 index a05d969b..00000000 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/properties/KakaoProperties.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.numberone.backend.domain.properties; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -@Data -@Component -@ConfigurationProperties(prefix = "spring.kakao") -public class KakaoProperties { - private String client_id; - private String redirect_uri; - private String token_api_url; - private String user_api_url; - private String mapApiUrl; -} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/properties/NaverProperties.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/properties/NaverProperties.java deleted file mode 100644 index a562af92..00000000 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/properties/NaverProperties.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.numberone.backend.domain.properties; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -@Data -@Component -@ConfigurationProperties(prefix = "spring.naver") -public class NaverProperties { - private String client_id; - private String client_secret; - private String redirect_uri; - private String token_api_url; - private String user_api_url; -} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/properties/TokenPeriodProperties.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/properties/TokenPeriodProperties.java deleted file mode 100644 index 5cb32d67..00000000 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/properties/TokenPeriodProperties.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.numberone.backend.domain.properties; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -@Data -@Component -@ConfigurationProperties(prefix = "spring.token") -public class TokenPeriodProperties { - private Long access; - private Long refresh; -} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/service/JwtProvider.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/service/JwtProvider.java deleted file mode 100644 index 36cf39af..00000000 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/service/JwtProvider.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.numberone.backend.domain.service; - -import com.numberone.backend.domain.properties.JwtProperties; -import io.jsonwebtoken.*; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import java.util.Date; - -@Slf4j -@Component -@RequiredArgsConstructor -public class JwtProvider { - private final JwtProperties jwtProperties; - - public String createToken(String email, long period) { - Claims claims = Jwts.claims().setSubject(email); - Date now = new Date(); - return Jwts.builder() - .setClaims(claims) - .setIssuedAt(now) - .setExpiration(new Date(now.getTime() + period)) - .signWith(SignatureAlgorithm.HS256, jwtProperties.getSecret().getBytes()) - .compact(); - } - - - public boolean isValid(String token) { - try { - getClaims(token); - return true; - } catch (ExpiredJwtException e) { - return true; - } catch (Exception e) { - return false; - } - } - - public boolean isExpired(String token) { - try { - Jws claims = Jwts.parser().setSigningKey(jwtProperties.getSecret().getBytes()).parseClaimsJws(token); - return getClaims(token).getExpiration().before(new Date()); - } catch (ExpiredJwtException e) { - return true; - } - } - - public String getEmail(String token) { - return getClaims(token).getSubject(); - } - - private Claims getClaims(String token) { - return Jwts.parser().setSigningKey(jwtProperties.getSecret().getBytes()).parseClaimsJws(token).getBody(); - } -} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/service/TokenService.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/service/TokenService.java index 097086d1..e69de29b 100644 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/service/TokenService.java +++ b/daepiro-auth/src/main/java/com/numberone/backend/domain/service/TokenService.java @@ -1,111 +0,0 @@ -package com.numberone.backend.domain.service; - -import com.numberone.backend.domain.member.entity.Member; -import com.numberone.backend.domain.member.repository.MemberRepository; -import com.numberone.backend.domain.properties.KakaoProperties; -import com.numberone.backend.domain.properties.NaverProperties; -import com.numberone.backend.domain.properties.TokenPeriodProperties; -import com.numberone.backend.domain.token.dto.request.GetTokenRequest; -import com.numberone.backend.domain.token.dto.request.RefreshTokenRequest; -import com.numberone.backend.domain.token.dto.response.GetTokenResponse; -import com.numberone.backend.domain.token.dto.response.KakaoInfoResponse; -import com.numberone.backend.domain.token.dto.response.NaverInfoResponse; -import com.numberone.backend.domain.token.dto.response.RefreshTokenResponse; -import com.numberone.backend.domain.token.entity.Token; -import com.numberone.backend.domain.token.repository.TokenRepository; -import com.numberone.backend.exception.badrequest.BadRequestSocialTokenException; -import com.numberone.backend.exception.forbidden.WrongAccessTokenException; -import com.numberone.backend.exception.notfound.NotFoundRefreshTokenException; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.*; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.client.RestTemplate; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -@Slf4j -public class TokenService { - - private final JwtProvider jwtProvider; - - private final RestTemplate restTemplate; - - private final KakaoProperties kakaoProperties; - private final NaverProperties naverProperties; - private final TokenPeriodProperties tokenPeriodProperties; - - private final MemberRepository repository; - private final TokenRepository tokenRepository; - private final MemberRepository memberRepository; - - @Transactional - public GetTokenResponse loginKakao(GetTokenRequest tokenRequest) { - HttpHeaders headers = new HttpHeaders(); - headers.add("Content-type", "application/x-www-form-urlencoded; charset=utf-8"); - headers.add("Authorization", "Bearer " + tokenRequest.getToken()); - - try { - ResponseEntity response = restTemplate.exchange(kakaoProperties.getUser_api_url(), HttpMethod.GET, new HttpEntity<>(null, headers), KakaoInfoResponse.class); - String email = response.getBody().getKakao_account().getEmail(); -// String realName = "실명을 가져올 수 없습니다."; -// if(response.getBody().getKakao_account().getProfile() != null){ -// realName = response.getBody().getKakao_account().getProfile().getNickname(); -// } - return getTokenResponse(email); - } catch (Exception e) { - throw new BadRequestSocialTokenException(); - } - } - - @Transactional - public GetTokenResponse loginNaver(GetTokenRequest tokenRequest) { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - headers.add("Authorization", "Bearer " + tokenRequest.getToken()); - - try { - ResponseEntity response = restTemplate.exchange(naverProperties.getUser_api_url(), HttpMethod.GET, new HttpEntity<>(null, headers), NaverInfoResponse.class); - String email = response.getBody().getResponse().getEmail(); -// String realName = Optional.ofNullable(response.getBody().getResponse().getName()) -// .orElse("실명을 가져올 수 없습니다."); - return getTokenResponse(email); - } catch (Exception e) { - throw new BadRequestSocialTokenException(); - } - } - - @Transactional - public RefreshTokenResponse refresh(RefreshTokenRequest tokenRequest) { - if (!jwtProvider.isValid(tokenRequest.getToken())) - throw new NotFoundRefreshTokenException(); - Token token = tokenRepository.findByRefreshToken(tokenRequest.getToken()) - .orElseThrow(NotFoundRefreshTokenException::new); - String email = jwtProvider.getEmail(tokenRequest.getToken()); - String newAccessToken = jwtProvider.createToken(email, tokenPeriodProperties.getAccess()); - String newRefreshToken = jwtProvider.createToken(email, tokenPeriodProperties.getRefresh()); - token.update(newAccessToken, newRefreshToken); - tokenRepository.save(token);//redis의 경우 jpa와 달리 transactional을 이용해도 데이터 수정시에 명시적으로 save를 해줘야 함 - return RefreshTokenResponse.of(newAccessToken, newRefreshToken); - } - - private GetTokenResponse getTokenResponse(String email) { - Boolean isNewMember = false; - if (!memberRepository.existsByEmail(email)) { - memberRepository.save(Member.of(email)); - isNewMember = true; - } - if (tokenRepository.existsById(email)) { - Token token = tokenRepository.findById(email) - .orElseThrow(WrongAccessTokenException::new); - return GetTokenResponse.of(token.getAccessToken(), token.getRefreshToken(), isNewMember); - } else { - String refreshToken = jwtProvider.createToken(email, tokenPeriodProperties.getRefresh()); - String accessToken = jwtProvider.createToken(email, tokenPeriodProperties.getAccess()); - Token token = tokenRepository.save(Token.of(email, accessToken, refreshToken)); - return GetTokenResponse.of(token.getAccessToken(), token.getRefreshToken(), isNewMember); - } - } -} \ No newline at end of file diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/service/UserAuthService.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/service/UserAuthService.java deleted file mode 100644 index e3531613..00000000 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/service/UserAuthService.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.numberone.backend.domain.service; - -public class UserAuthService { - // todo: api 모듈의 token service 를 auth 모듈로 이관 -} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/controller/TokenController.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/token/controller/TokenController.java deleted file mode 100644 index 8ff421ba..00000000 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/controller/TokenController.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.numberone.backend.domain.token.controller; - -import com.numberone.backend.domain.service.TokenService; -import com.numberone.backend.domain.token.dto.request.GetTokenRequest; -import com.numberone.backend.domain.token.dto.request.RefreshTokenRequest; -import com.numberone.backend.domain.token.dto.response.GetTokenResponse; -import com.numberone.backend.domain.token.dto.response.RefreshTokenResponse; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; -import lombok.RequiredArgsConstructor; -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.RestController; - -@Tag(name = "token", description = "토큰 관련 API") -@RestController -@RequiredArgsConstructor -@RequestMapping("/token") -public class TokenController { - private final TokenService tokenService; - - @Operation(summary = "카카오 토큰을 이용하여 서버 JWT 토큰 발급받기", description = - """ - 카카오 토큰을 body 에 담아서 post 요청 해주세요. - - 앞으로 서버 요청 시에 사용할 수 있는 JWT 토큰이 발급됩니다. - - 이후 서버에 API 요청시 이 JWT 토큰을 같이 담아서 요청해야 정상적으로 API가 호출 됩니다. - """) - @PostMapping("/kakao") - public GetTokenResponse loginKakao(@RequestBody @Valid GetTokenRequest tokenRequest) { - return tokenService.loginKakao(tokenRequest); - } - - @Operation(summary = "네이버 토큰을 이용하여 서버 JWT 토큰 발급받기", description = - """ - 네이버 토큰을 body 에 담아서 post 요청 해주세요. - - 앞으로 서버 요청 시에 사용할 수 있는 JWT 토큰이 발급됩니다. - - 이후 서버에 API 요청시 이 JWT 토큰을 같이 담아서 요청해야 정상적으로 API가 호출 됩니다. - """) - @PostMapping("/naver") - public GetTokenResponse loginNaver(@RequestBody @Valid GetTokenRequest tokenRequest) { - return tokenService.loginNaver(tokenRequest); - } - - @Operation(summary = "만료된 JWT 토큰 갱신하기", description = - """ - 만료된 JWT 토큰을 body 에 담아서 post 요청 해주세요. - - 새로 사용할 수 있는 JWT 토큰이 발급됩니다. - """) - @PostMapping("/refresh") - public RefreshTokenResponse refresh(@RequestBody @Valid RefreshTokenRequest tokenRequest) { - return tokenService.refresh(tokenRequest); - } -} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/request/GetTokenRequest.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/request/GetTokenRequest.java deleted file mode 100644 index 5bed0f3b..00000000 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/request/GetTokenRequest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.numberone.backend.domain.token.dto.request; - -import jakarta.validation.constraints.NotNull; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; -import org.hibernate.annotations.Comment; - -@ToString -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class GetTokenRequest { - @NotNull(message = "토큰이 NULL 입니다.") - @Comment("카카오 또는 네이버에서 발급된 Access 토큰") - private String token; -} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/request/RefreshTokenRequest.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/request/RefreshTokenRequest.java deleted file mode 100644 index db30d8e9..00000000 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/request/RefreshTokenRequest.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.numberone.backend.domain.token.dto.request; - -import jakarta.validation.constraints.NotNull; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.hibernate.annotations.Comment; - -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class RefreshTokenRequest { - @NotNull(message = "토큰이 NULL 입니다.") - @Comment("서버에서 발급받은 Refresh 토큰") - private String token; -} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/GetTokenResponse.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/GetTokenResponse.java deleted file mode 100644 index 39992a95..00000000 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/GetTokenResponse.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.numberone.backend.domain.token.dto.response; - -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class GetTokenResponse { - private String accessToken; - private String refreshToken; - private Boolean isNewMember; - - @Builder - public GetTokenResponse(String accessToken, String refreshToken, Boolean isNewMember) { - this.accessToken = accessToken; - this.refreshToken = refreshToken; - this.isNewMember = isNewMember; - } - - public static GetTokenResponse of(String accessToken, String refreshToken, Boolean isNewMember) { - return GetTokenResponse.builder() - .accessToken(accessToken) - .refreshToken(refreshToken) - .isNewMember(isNewMember) - .build(); - } -} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/KakaoInfoResponse.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/KakaoInfoResponse.java deleted file mode 100644 index ec15a41f..00000000 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/KakaoInfoResponse.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.numberone.backend.domain.token.dto.response; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -@ToString -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@JsonIgnoreProperties(ignoreUnknown = true) -public class KakaoInfoResponse { - private Long id; - private String connected_at; - private Properties properties; - private KakaoAccount kakao_account; - - @ToString - @Getter - @NoArgsConstructor - @JsonIgnoreProperties(ignoreUnknown = true) - public class Properties { - private String nickname; - private String profile_image; - private String thumbnail_image; - } - - @ToString - @Getter - @NoArgsConstructor - @JsonIgnoreProperties(ignoreUnknown = true) - public class KakaoAccount { - @Getter - @NoArgsConstructor - @ToString - @JsonIgnoreProperties(ignoreUnknown = true) - public class profile { - private String nickname; - private String thumbnail_image_url; - private String profile_image_url; - private String is_default_image; - } - - private Boolean profile_nickname_needs_agreement; - private Boolean profile_image_needs_agreement; - private profile profile; - private Boolean has_email; - private Boolean email_needs_agreement; - private Boolean is_email_valid; - private Boolean is_email_verified; - private String email; - } -} - - - diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/KakaoTokenResponse.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/KakaoTokenResponse.java deleted file mode 100644 index c634c489..00000000 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/KakaoTokenResponse.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.numberone.backend.domain.token.dto.response; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -@ToString -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class KakaoTokenResponse { - private String token_type; - private String access_token; - private String id_token; - private String refresh_token; - private Integer expires_in; - private Integer refresh_token_expires_in; - private String scope; -} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/NaverInfoResponse.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/NaverInfoResponse.java deleted file mode 100644 index c91273e2..00000000 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/NaverInfoResponse.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.numberone.backend.domain.token.dto.response; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -@ToString -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@JsonIgnoreProperties(ignoreUnknown = true) -public class NaverInfoResponse { - private String resultcode; - private String message; - private Response response; - - @ToString - @Getter - @NoArgsConstructor - @JsonIgnoreProperties(ignoreUnknown = true) - public class Response { - private String id; - private String nickname; - private String email; - private String name; - } -} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/NaverTokenResponse.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/NaverTokenResponse.java deleted file mode 100644 index 47993aab..00000000 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/NaverTokenResponse.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.numberone.backend.domain.token.dto.response; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -@ToString -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class NaverTokenResponse { - private String access_token; - private String refresh_token; - private String token_type; - private String expires_in; -} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/RefreshTokenResponse.java b/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/RefreshTokenResponse.java deleted file mode 100644 index 1423570d..00000000 --- a/daepiro-auth/src/main/java/com/numberone/backend/domain/token/dto/response/RefreshTokenResponse.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.numberone.backend.domain.token.dto.response; - -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@Getter -public class RefreshTokenResponse { - private String accessToken; - private String refreshToken; - - @Builder - public RefreshTokenResponse(String accessToken,String refreshToken) { - this.accessToken = accessToken; - this.refreshToken= refreshToken; - } - - public static RefreshTokenResponse of(String accessToken, String refreshToken) { - return RefreshTokenResponse.builder() - .accessToken(accessToken) - .refreshToken(refreshToken) - .build(); - } -} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/feign/KakaoFeign.java b/daepiro-auth/src/main/java/com/numberone/backend/feign/KakaoFeign.java new file mode 100644 index 00000000..75ca072a --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/feign/KakaoFeign.java @@ -0,0 +1,11 @@ +package com.numberone.backend.feign; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; + +@FeignClient(name = "kakaoFeign", url = "https://kapi.kakao.com") +public interface KakaoFeign { + @GetMapping(value = "/v1/user/access_token_info") + KakaoIdDto getUserData(@RequestHeader(name = "Authorization") String token); +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/feign/KakaoIdDto.java b/daepiro-auth/src/main/java/com/numberone/backend/feign/KakaoIdDto.java new file mode 100644 index 00000000..b9c279cc --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/feign/KakaoIdDto.java @@ -0,0 +1,9 @@ +package com.numberone.backend.feign; + +import lombok.*; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class KakaoIdDto { + private Long id; +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/filter/JwtAccessFilter.java b/daepiro-auth/src/main/java/com/numberone/backend/filter/JwtAccessFilter.java new file mode 100644 index 00000000..8814cd69 --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/filter/JwtAccessFilter.java @@ -0,0 +1,39 @@ +package com.numberone.backend.filter; + +import com.numberone.backend.provider.JwtProvider; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpHeaders; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +@Component +@RequiredArgsConstructor +public class JwtAccessFilter extends OncePerRequestFilter { + private final JwtProvider jwtProvider; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String jwt = request.getHeader(HttpHeaders.AUTHORIZATION); + if (jwt != null) { + Long id = jwtProvider.checkToken(jwt, "access", request); + Authentication authentication = UsernamePasswordAuthenticationToken.authenticated(id, null, null); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + filterChain.doFilter(request, response); + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + return request.getServletPath().equals("/token/refresh"); + } +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/filter/JwtExceptionFilter.java b/daepiro-auth/src/main/java/com/numberone/backend/filter/JwtExceptionFilter.java new file mode 100644 index 00000000..1ddb3817 --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/filter/JwtExceptionFilter.java @@ -0,0 +1,35 @@ +package com.numberone.backend.filter; + +import com.numberone.backend.exception.context.CustomExceptionContext; +import com.numberone.backend.provider.HttpResponseProvider; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.JwtException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +import static com.numberone.backend.exception.context.CustomExceptionContext.EXPIRED_TOKEN; +import static com.numberone.backend.exception.context.CustomExceptionContext.INVALID_TOKEN; + +@Component +@RequiredArgsConstructor +public class JwtExceptionFilter extends OncePerRequestFilter { + private final HttpResponseProvider httpResponseProvider; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + try { + filterChain.doFilter(request, response); + } catch (ExpiredJwtException e){ + httpResponseProvider.setErrorResponse(response,HttpServletResponse.SC_FORBIDDEN, EXPIRED_TOKEN);//to do: 프론트와 협의 후 403에서 401로 수정 + }catch (JwtException e){ + httpResponseProvider.setErrorResponse(response,HttpServletResponse.SC_UNAUTHORIZED, INVALID_TOKEN); + } + } +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/filter/JwtRefreshFilter.java b/daepiro-auth/src/main/java/com/numberone/backend/filter/JwtRefreshFilter.java new file mode 100644 index 00000000..048d0fc1 --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/filter/JwtRefreshFilter.java @@ -0,0 +1,48 @@ +package com.numberone.backend.filter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.numberone.backend.domain.token.entity.RefreshToken; +import com.numberone.backend.domain.token.service.RefreshTokenService; +import com.numberone.backend.provider.HttpResponseProvider; +import com.numberone.backend.provider.JwtProvider; +import io.jsonwebtoken.JwtException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.util.StreamUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +@Component +@RequiredArgsConstructor +public class JwtRefreshFilter extends OncePerRequestFilter { + private final JwtProvider jwtProvider; + private final ObjectMapper objectMapper; + private final HttpResponseProvider httpResponseProvider; + private final RefreshTokenService refreshTokenService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String requestBody = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8); + Map requestBodyMap = objectMapper.readValue(requestBody, Map.class); + String jwt = requestBodyMap.get("token"); + if (jwt != null) { + RefreshToken refreshToken = refreshTokenService.findByToken(jwt) + .orElseThrow(() -> new JwtException("유효하지 않은 토큰입니다.")); + long id = jwtProvider.checkToken(refreshToken.getToken(), "refresh", request); + httpResponseProvider.setJwtResponse(response, id); + } + filterChain.doFilter(request, response); + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { + return !request.getServletPath().equals("/token/refresh"); + } +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/filter/SocialAuthenticationFilter.java b/daepiro-auth/src/main/java/com/numberone/backend/filter/SocialAuthenticationFilter.java new file mode 100644 index 00000000..15921c28 --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/filter/SocialAuthenticationFilter.java @@ -0,0 +1,46 @@ +package com.numberone.backend.filter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.numberone.backend.handler.SocialAuthenticationFailureHandler; +import com.numberone.backend.handler.SocialAuthenticationSuccessHandler; +import com.numberone.backend.provider.SocialAuthenticationProvider; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.stereotype.Component; +import org.springframework.util.StreamUtils; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +@Component +public class SocialAuthenticationFilter extends AbstractAuthenticationProcessingFilter { + private final ObjectMapper objectMapper; + + public SocialAuthenticationFilter(SocialAuthenticationProvider socialAuthenticationProvider, + SocialAuthenticationSuccessHandler socialAuthenticationSuccessHandler, + SocialAuthenticationFailureHandler socialAuthenticationFailureHandler, + ObjectMapper objectMapper) { + super(new AntPathRequestMatcher("/token/kakao", "POST")); + setAuthenticationManager(new ProviderManager(socialAuthenticationProvider)); + setAuthenticationSuccessHandler(socialAuthenticationSuccessHandler); + setAuthenticationFailureHandler(socialAuthenticationFailureHandler); + this.objectMapper = objectMapper; + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { + String requestBody = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8); + Map requestBodyMap = objectMapper.readValue(requestBody, Map.class); + String token = requestBodyMap.get("token"); + Authentication authentication = UsernamePasswordAuthenticationToken.unauthenticated(token, null); + return getAuthenticationManager().authenticate(authentication); + } +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/handler/CustomAccessDeniedHandler.java b/daepiro-auth/src/main/java/com/numberone/backend/handler/CustomAccessDeniedHandler.java new file mode 100644 index 00000000..e57dde4f --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/handler/CustomAccessDeniedHandler.java @@ -0,0 +1,32 @@ +package com.numberone.backend.handler; + + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.numberone.backend.provider.HttpResponseProvider; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerExceptionResolver; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import static com.numberone.backend.exception.context.CustomExceptionContext.NOT_AUTHENTICATION; +import static com.numberone.backend.exception.context.CustomExceptionContext.NOT_AUTHORIZATION; + +@Component +@RequiredArgsConstructor +public class CustomAccessDeniedHandler implements AccessDeniedHandler { + private final HttpResponseProvider httpResponseProvider; + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { + httpResponseProvider.setErrorResponse(response, HttpServletResponse.SC_FORBIDDEN, NOT_AUTHORIZATION); + } +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/handler/CustomAuthenticationEntryPoint.java b/daepiro-auth/src/main/java/com/numberone/backend/handler/CustomAuthenticationEntryPoint.java new file mode 100644 index 00000000..d29469c3 --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/handler/CustomAuthenticationEntryPoint.java @@ -0,0 +1,28 @@ +package com.numberone.backend.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.numberone.backend.exception.context.CustomExceptionContext; +import com.numberone.backend.provider.HttpResponseProvider; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerExceptionResolver; + +import java.io.IOException; + +import static com.numberone.backend.exception.context.CustomExceptionContext.NOT_AUTHENTICATION; + +@Component +@RequiredArgsConstructor +public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { + private final HttpResponseProvider httpResponseProvider; + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + httpResponseProvider.setErrorResponse(response,HttpServletResponse.SC_UNAUTHORIZED, NOT_AUTHENTICATION); + } +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/handler/SocialAuthenticationFailureHandler.java b/daepiro-auth/src/main/java/com/numberone/backend/handler/SocialAuthenticationFailureHandler.java new file mode 100644 index 00000000..07a9e3f1 --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/handler/SocialAuthenticationFailureHandler.java @@ -0,0 +1,26 @@ +package com.numberone.backend.handler; + +import com.numberone.backend.provider.HttpResponseProvider; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +import static com.numberone.backend.exception.context.CustomExceptionContext.LOGIN_FAILURE; +import static com.numberone.backend.exception.context.CustomExceptionContext.NOT_AUTHENTICATION; + +@Component +@RequiredArgsConstructor +public class SocialAuthenticationFailureHandler implements AuthenticationFailureHandler { + private final HttpResponseProvider httpResponseProvider; + + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { + httpResponseProvider.setErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, LOGIN_FAILURE); + } +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/handler/SocialAuthenticationSuccessHandler.java b/daepiro-auth/src/main/java/com/numberone/backend/handler/SocialAuthenticationSuccessHandler.java new file mode 100644 index 00000000..cc8da0d8 --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/handler/SocialAuthenticationSuccessHandler.java @@ -0,0 +1,25 @@ +package com.numberone.backend.handler; + +import com.numberone.backend.domain.member.service.MemberService; +import com.numberone.backend.provider.HttpResponseProvider; +import com.numberone.backend.provider.JwtProvider; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +@RequiredArgsConstructor +public class SocialAuthenticationSuccessHandler implements AuthenticationSuccessHandler { + private final HttpResponseProvider httpResponseProvider; + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { + httpResponseProvider.setLoginResponse(response, (long) authentication.getPrincipal()); + } +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/provider/HttpResponseProvider.java b/daepiro-auth/src/main/java/com/numberone/backend/provider/HttpResponseProvider.java new file mode 100644 index 00000000..b1b10f59 --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/provider/HttpResponseProvider.java @@ -0,0 +1,53 @@ +package com.numberone.backend.provider; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.numberone.backend.domain.member.service.MemberService; +import com.numberone.backend.exception.context.CustomExceptionContext; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +@Component +@RequiredArgsConstructor +public class HttpResponseProvider { + private final ObjectMapper objectMapper; + private final JwtProvider jwtProvider; + private final MemberService memberService; + + public void setJwtResponse(HttpServletResponse response, long id) throws IOException { + String accessToken = jwtProvider.createAccessToken(id); + String refreshToken = jwtProvider.createRefreshToken(id); + Map responseBody = new HashMap<>(); + response.setStatus(HttpServletResponse.SC_OK); + response.setContentType("application/json;charset=UTF-8"); + responseBody.put("accessToken", accessToken); + responseBody.put("refreshToken", refreshToken); + objectMapper.writeValue(response.getWriter(), responseBody); + } + + public void setLoginResponse(HttpServletResponse response, long id) throws IOException { + String accessToken = jwtProvider.createAccessToken(id); + String refreshToken = jwtProvider.createRefreshToken(id); + boolean isOnboarding = memberService.findById(id).getIsOnboarding(); + Map responseBody = new HashMap<>(); + response.setStatus(HttpServletResponse.SC_OK); + response.setContentType("application/json;charset=UTF-8"); + responseBody.put("accessToken", accessToken); + responseBody.put("refreshToken", refreshToken); + responseBody.put("isOnboarding", isOnboarding); + objectMapper.writeValue(response.getWriter(), responseBody); + } + + public void setErrorResponse(HttpServletResponse response, int statusCode, CustomExceptionContext exception) throws IOException { + Map responseBody = new HashMap<>(); + response.setStatus(statusCode); + response.setContentType("application/json;charset=UTF-8"); + responseBody.put("code", exception.getCode()); + responseBody.put("message", exception.getMessage()); + objectMapper.writeValue(response.getWriter(), responseBody); + } +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/provider/JwtProvider.java b/daepiro-auth/src/main/java/com/numberone/backend/provider/JwtProvider.java new file mode 100644 index 00000000..827c9719 --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/provider/JwtProvider.java @@ -0,0 +1,70 @@ +package com.numberone.backend.provider; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.numberone.backend.domain.member.repository.MemberRepository; +import com.numberone.backend.domain.member.service.MemberService; +import com.numberone.backend.domain.token.service.RefreshTokenService; +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Component +@RequiredArgsConstructor +public class JwtProvider { + @Value("${spring.jwt.secret}") + private String secretKey; + private final MemberService memberService; + private final RefreshTokenService refreshTokenService; + + public String createAccessToken(long id) { + long accessTokenPeroid = 1000L * 60 * 30; + return Jwts.builder() + .claim("id", id) + .claim("type", "access") + .issuedAt(new Date()) + .expiration(new Date((new Date()).getTime() + accessTokenPeroid)) + .signWith(Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8))) + .compact(); + } + + public String createRefreshToken(long id) { + long refreshTokenPeroid = 1000L * 60 * 60 * 24 * 14; + String token = Jwts.builder() + .claim("id", id) + .claim("type", "refresh") + .issuedAt(new Date()) + .expiration(new Date((new Date()).getTime() + refreshTokenPeroid)) + .signWith(Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8))) + .compact(); + refreshTokenService.update(id, token); + return token; + } + + public Long checkToken(String jwt, String tokenType, HttpServletRequest request) { + if (tokenType.equals("access")) {//나중에는 refresh token도 이렇게 처리하도록 수정할 것임 + if (!jwt.startsWith("Bearer ")) + throw new JwtException("유효하지 않은 토큰입니다."); + jwt = jwt.substring("Bearer ".length()); + } + Claims claims = Jwts.parser() + .verifyWith(Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8))) + .build() + .parseSignedClaims(jwt) + .getPayload(); + long id = ((Number) claims.get("id")).longValue(); + String type = (String) claims.get("type"); + if (!memberService.existsById(id) || !type.equals(tokenType)) + throw new JwtException("유효하지 않은 토큰입니다."); + return id; + } +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/provider/SocialAuthenticationProvider.java b/daepiro-auth/src/main/java/com/numberone/backend/provider/SocialAuthenticationProvider.java new file mode 100644 index 00000000..40375a33 --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/provider/SocialAuthenticationProvider.java @@ -0,0 +1,42 @@ +package com.numberone.backend.provider; + +import com.numberone.backend.domain.member.service.MemberService; +import com.numberone.backend.feign.KakaoFeign; +import com.numberone.backend.domain.member.entity.Member; +import com.numberone.backend.domain.member.repository.MemberRepository; +import feign.FeignException; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class SocialAuthenticationProvider implements AuthenticationProvider { + private final MemberService memberService; + private final KakaoFeign kakaoFeign; + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + String token = (String) authentication.getPrincipal(); + Long socialId; + try { + socialId = kakaoFeign.getUserData("Bearer " + token).getId(); + } catch (Exception e) { + throw new BadCredentialsException("유효하지 않은 OAuth 토큰입니다."); + } + Member member = memberService.findBySocialId(socialId); + if (member == null) + member = memberService.create(socialId); + return UsernamePasswordAuthenticationToken.authenticated(member.getId(), null, null); + } + + @Override + public boolean supports(Class authentication) { + return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)); + } +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/swagger/SwaggerDocumentController.java b/daepiro-auth/src/main/java/com/numberone/backend/swagger/SwaggerDocumentController.java new file mode 100644 index 00000000..785912e7 --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/swagger/SwaggerDocumentController.java @@ -0,0 +1,56 @@ +package com.numberone.backend.swagger; + + +import com.numberone.backend.swagger.dto.request.JwtRefreshRequest; +import com.numberone.backend.swagger.dto.request.LoginRequest; +import com.numberone.backend.swagger.dto.response.JwtRefreshResponse; +import com.numberone.backend.swagger.dto.response.LoginResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +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.RestController; + +@Tag(name = "login", description = "인증 관련 API") +@RestController +@RequestMapping("/token") +public class SwaggerDocumentController {// 필터에서 처리하는 api의 경우 swagger에 등록되지 않아서 오로지 swagger 문서화를 위한 컨트롤러입니다. + + @Operation(summary = "카카오 토큰을 이용하여 서버 JWT 토큰 발급받기", description = + """ + 카카오 토큰을 body 에 담아서 post 요청 해주세요. + + 앞으로 서버 요청 시에 사용할 수 있는 JWT 토큰이 발급됩니다. + + 이후 서버에 API 요청시 이 JWT 토큰을 같이 담아서 요청해야 정상적으로 API가 호출 됩니다. + """) + @PostMapping("/kakao") + public LoginResponse loginKakao(@RequestBody LoginRequest loginRequest) { + return null; + } + +// @Operation(summary = "네이버 토큰을 이용하여 서버 JWT 토큰 발급받기", description = +// """ +// 네이버 토큰을 body 에 담아서 post 요청 해주세요. +// +// 앞으로 서버 요청 시에 사용할 수 있는 JWT 토큰이 발급됩니다. +// +// 이후 서버에 API 요청시 이 JWT 토큰을 같이 담아서 요청해야 정상적으로 API가 호출 됩니다. +// """) +// @PostMapping("/naver") +// public GetTokenResponse loginNaver(@RequestBody @Valid GetTokenRequest tokenRequest) { +// return tokenService.loginNaver(tokenRequest); +// } + + @Operation(summary = "만료된 JWT 토큰 갱신하기", description = + """ + 만료된 JWT 토큰을 body 에 담아서 post 요청 해주세요. + + 새로 사용할 수 있는 JWT 토큰이 발급됩니다. + """) + @PostMapping("/refresh") + public JwtRefreshResponse refresh(@RequestBody JwtRefreshRequest jwtRefreshRequest) { + return null; + } +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/swagger/dto/request/JwtRefreshRequest.java b/daepiro-auth/src/main/java/com/numberone/backend/swagger/dto/request/JwtRefreshRequest.java new file mode 100644 index 00000000..2fe035b6 --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/swagger/dto/request/JwtRefreshRequest.java @@ -0,0 +1,11 @@ +package com.numberone.backend.swagger.dto.request; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class JwtRefreshRequest { + private String token; +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/swagger/dto/request/LoginRequest.java b/daepiro-auth/src/main/java/com/numberone/backend/swagger/dto/request/LoginRequest.java new file mode 100644 index 00000000..b759d285 --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/swagger/dto/request/LoginRequest.java @@ -0,0 +1,11 @@ +package com.numberone.backend.swagger.dto.request; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class LoginRequest { + private String token; +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/swagger/dto/response/JwtRefreshResponse.java b/daepiro-auth/src/main/java/com/numberone/backend/swagger/dto/response/JwtRefreshResponse.java new file mode 100644 index 00000000..4cb69d9e --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/swagger/dto/response/JwtRefreshResponse.java @@ -0,0 +1,9 @@ +package com.numberone.backend.swagger.dto.response; + +import lombok.Getter; + +@Getter +public class JwtRefreshResponse { + private String accessToken; + private String refreshToken; +} diff --git a/daepiro-auth/src/main/java/com/numberone/backend/swagger/dto/response/LoginResponse.java b/daepiro-auth/src/main/java/com/numberone/backend/swagger/dto/response/LoginResponse.java new file mode 100644 index 00000000..9ecae5ea --- /dev/null +++ b/daepiro-auth/src/main/java/com/numberone/backend/swagger/dto/response/LoginResponse.java @@ -0,0 +1,10 @@ +package com.numberone.backend.swagger.dto.response; + +import lombok.Getter; + +@Getter +public class LoginResponse { + private String accessToken; + private String refreshToken; + private Boolean isOnboarding; +} diff --git a/daepiro-common/src/main/java/com/numberone/backend/exception/badrequest/BadRequestSocialTokenException.java b/daepiro-common/src/main/java/com/numberone/backend/exception/badrequest/BadRequestSocialTokenException.java deleted file mode 100644 index 75549086..00000000 --- a/daepiro-common/src/main/java/com/numberone/backend/exception/badrequest/BadRequestSocialTokenException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.numberone.backend.exception.badrequest; - -import static com.numberone.backend.exception.context.CustomExceptionContext.BAD_REQUEST_SOCIAL_TOKEN; - -public class BadRequestSocialTokenException extends BadRequestException{ - public BadRequestSocialTokenException() { - super(BAD_REQUEST_SOCIAL_TOKEN); - } -} diff --git a/daepiro-common/src/main/java/com/numberone/backend/exception/context/CustomExceptionContext.java b/daepiro-common/src/main/java/com/numberone/backend/exception/context/CustomExceptionContext.java index 52b21339..d05dc48b 100644 --- a/daepiro-common/src/main/java/com/numberone/backend/exception/context/CustomExceptionContext.java +++ b/daepiro-common/src/main/java/com/numberone/backend/exception/context/CustomExceptionContext.java @@ -9,12 +9,13 @@ public enum CustomExceptionContext implements ExceptionContext { // MEMBER 관련 예외 NOT_FOUND_MEMBER("존재하지 않는 회원을 조회할 수 없습니다.", 1000), - // TOKEN 관련 예외 - WRONG_ACCESS_TOKEN("존재하지 않는 액세스 토큰입니다.", 2001), - EXPIRED_ACCESS_TOKEN("만료된 액세스 토큰입니다. 리프레쉬 토큰을 이용하여 갱신해주세요.", 2002), - WRONG_REFRESH_TOKEN("존재하지 않거나 만료된 리프레쉬 토큰입니다. 다시 리프레쉬 토큰을 발급받아주세요.", 2003), - BAD_REQUEST_SOCIAL_TOKEN("요청하신 네이버 또는 카카오 소셜 토큰이 유효하지 않습니다.", 2004), - BAD_USER_AUTHENTICATION("해당 토큰의 인증 정보가 유효하지 않습니다.", 2005), + // 인증,인가 관련 예외 + INVALID_TOKEN("유효하지 않은 토큰입니다.", 2001), + EXPIRED_TOKEN("만료된 토큰입니다.", 2002), + LOGIN_FAILURE("로그인에 실패했습니다.", 2003), + NOT_AUTHENTICATION("인증이 필요합니다.",2004), + NOT_AUTHORIZATION("권한이 필요합니다.",2005), + BAD_USER_AUTHENTICATION("해당 토큰의 인증 정보가 유효하지 않습니다.", 2006), // SHELTER 관련 예외 NOT_FOUND_SHELTER("주변에 가까운 대피소가 존재하지 않습니다.", 3000), diff --git a/daepiro-common/src/main/java/com/numberone/backend/exception/forbidden/ExpiredAccessTokenException.java b/daepiro-common/src/main/java/com/numberone/backend/exception/forbidden/ExpiredAccessTokenException.java deleted file mode 100644 index ade0d66b..00000000 --- a/daepiro-common/src/main/java/com/numberone/backend/exception/forbidden/ExpiredAccessTokenException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.numberone.backend.exception.forbidden; - -import static com.numberone.backend.exception.context.CustomExceptionContext.EXPIRED_ACCESS_TOKEN; - -public class ExpiredAccessTokenException extends ForbiddenException{ - public ExpiredAccessTokenException() { - super(EXPIRED_ACCESS_TOKEN); - } -} diff --git a/daepiro-common/src/main/java/com/numberone/backend/exception/forbidden/ForbiddenException.java b/daepiro-common/src/main/java/com/numberone/backend/exception/forbidden/ForbiddenException.java deleted file mode 100644 index f168673f..00000000 --- a/daepiro-common/src/main/java/com/numberone/backend/exception/forbidden/ForbiddenException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.numberone.backend.exception.forbidden; - -import com.numberone.backend.exception.NumberOneException; -import com.numberone.backend.exception.context.ExceptionContext; -import org.springframework.http.HttpStatus; - -public class ForbiddenException extends NumberOneException { - public ForbiddenException(ExceptionContext context) { - super(HttpStatus.FORBIDDEN,context.getMessage(),context.getCode()); - } -} diff --git a/daepiro-common/src/main/java/com/numberone/backend/exception/forbidden/WrongAccessTokenException.java b/daepiro-common/src/main/java/com/numberone/backend/exception/forbidden/WrongAccessTokenException.java deleted file mode 100644 index d72b162e..00000000 --- a/daepiro-common/src/main/java/com/numberone/backend/exception/forbidden/WrongAccessTokenException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.numberone.backend.exception.forbidden; - -import static com.numberone.backend.exception.context.CustomExceptionContext.WRONG_ACCESS_TOKEN; - -public class WrongAccessTokenException extends ForbiddenException { - public WrongAccessTokenException() { - super(WRONG_ACCESS_TOKEN); - } -} diff --git a/daepiro-common/src/main/java/com/numberone/backend/exception/notfound/NotFoundRefreshTokenException.java b/daepiro-common/src/main/java/com/numberone/backend/exception/notfound/NotFoundRefreshTokenException.java deleted file mode 100644 index ad057c7b..00000000 --- a/daepiro-common/src/main/java/com/numberone/backend/exception/notfound/NotFoundRefreshTokenException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.numberone.backend.exception.notfound; - -import static com.numberone.backend.exception.context.CustomExceptionContext.WRONG_REFRESH_TOKEN; - -public class NotFoundRefreshTokenException extends NotFoundException { - public NotFoundRefreshTokenException() { - super(WRONG_REFRESH_TOKEN); - } -} diff --git a/daepiro-common/src/main/java/com/numberone/backend/provider/security/SecurityContextProvider.java b/daepiro-common/src/main/java/com/numberone/backend/provider/security/SecurityContextProvider.java index 2e4697ae..497fb0fe 100644 --- a/daepiro-common/src/main/java/com/numberone/backend/provider/security/SecurityContextProvider.java +++ b/daepiro-common/src/main/java/com/numberone/backend/provider/security/SecurityContextProvider.java @@ -7,13 +7,12 @@ import java.util.Objects; public class SecurityContextProvider { - - public static String getAuthenticatedUserEmail(){ + public static Long getAuthenticatedUserId() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Object principal = authentication.getPrincipal(); - if (Objects.isNull(principal)){ + if (Objects.isNull(principal)) { throw new BadUserAuthenticationException(); } - return (String) principal; + return (Long) principal; } } diff --git a/daepiro-core/src/main/java/com/numberone/backend/domain/member/entity/Member.java b/daepiro-core/src/main/java/com/numberone/backend/domain/member/entity/Member.java index 0a699a1b..deedd662 100644 --- a/daepiro-core/src/main/java/com/numberone/backend/domain/member/entity/Member.java +++ b/daepiro-core/src/main/java/com/numberone/backend/domain/member/entity/Member.java @@ -31,8 +31,8 @@ public class Member extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Comment("이메일") - private String email; + @Comment("소셜 id") + private Long socialId; @Comment("닉네임") private String nickName; @@ -97,13 +97,16 @@ public class Member extends BaseTimeEntity { @Comment("동/읍/면") private String lv3; + @Comment("온보딩 완료 유무") + private Boolean isOnboarding; + public void updateSession(Boolean session) { this.session = session; } @Builder - public Member(String email, String nickName, String realName, Integer heartCnt, String fcmToken, Boolean session) { - this.email = email; + public Member(Long socialId, String nickName, String realName, Integer heartCnt, String fcmToken, Boolean session) { + this.socialId = socialId; this.nickName = nickName; this.realName = realName; this.heartCnt = heartCnt; @@ -111,11 +114,12 @@ public Member(String email, String nickName, String realName, Integer heartCnt, this.session = true; this.latitude = 0D; this.longitude = 0D; + isOnboarding = false; } - public static Member of(String email) { + public static Member of(Long socialId) { return Member.builder() - .email(email) + .socialId(socialId) .heartCnt(0) .build(); } @@ -128,6 +132,7 @@ public void setOnboardingData(String realName, String nickName, String fcmToken) this.realName = realName; this.nickName = nickName; this.fcmToken = fcmToken; + isOnboarding = true; } public void plusHeart(int heart) { diff --git a/daepiro-core/src/main/java/com/numberone/backend/domain/member/repository/MemberRepository.java b/daepiro-core/src/main/java/com/numberone/backend/domain/member/repository/MemberRepository.java index f19f86e5..039664af 100644 --- a/daepiro-core/src/main/java/com/numberone/backend/domain/member/repository/MemberRepository.java +++ b/daepiro-core/src/main/java/com/numberone/backend/domain/member/repository/MemberRepository.java @@ -8,9 +8,13 @@ import java.util.Optional; public interface MemberRepository extends JpaRepository, MemberRepositoryCustom { - Optional findByEmail(String email); - boolean existsByEmail(String email); + Optional findBySocialId(Long socialId); + + boolean existsById(Long id); + + boolean existsBySocialId(Long socialId); List findByLv1(String Lv1); + List findByLv2(String Lv2); } diff --git a/daepiro-redis/src/main/java/com/numberone/backend/domain/token/entity/RefreshToken.java b/daepiro-redis/src/main/java/com/numberone/backend/domain/token/entity/RefreshToken.java new file mode 100644 index 00000000..f44ac76d --- /dev/null +++ b/daepiro-redis/src/main/java/com/numberone/backend/domain/token/entity/RefreshToken.java @@ -0,0 +1,32 @@ +package com.numberone.backend.domain.token.entity; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.index.Indexed; + +import java.io.Serializable; + +@Getter +@RedisHash(value = "refreshToken", timeToLive = 60 * 60 * 24 * 14) +public class RefreshToken implements Serializable { + @Id + private Long userId; + + @Indexed + private String token; + + @Builder + public RefreshToken(Long userId, String token) { + this.userId = userId; + this.token = token; + } + + public static RefreshToken of(Long userId, String token) { + return RefreshToken.builder() + .userId(userId) + .token(token) + .build(); + } +} diff --git a/daepiro-redis/src/main/java/com/numberone/backend/domain/token/entity/Token.java b/daepiro-redis/src/main/java/com/numberone/backend/domain/token/entity/Token.java deleted file mode 100644 index 6f1cdbea..00000000 --- a/daepiro-redis/src/main/java/com/numberone/backend/domain/token/entity/Token.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.numberone.backend.domain.token.entity; - -import lombok.Builder; -import lombok.Getter; -import org.springframework.data.annotation.Id; -import org.springframework.data.redis.core.RedisHash; -import org.springframework.data.redis.core.index.Indexed; - -import java.io.Serializable; - -@Getter -@RedisHash(value = "token", timeToLive = 60 * 60 * 24 * 14)//@Entity가 RDBMS에 테이블을 생성했다면 @RedisHash는 Redis에 테이블(?)을 생성 -public class Token implements Serializable { - @Id - private String email; - - @Indexed//특정 컬럼을 인덱스(보조 id)로 지정하여 효율적인 검색이 가능하게 함 - private String refreshToken; - - private String accessToken; - - @Builder - public Token(String email, String accessToken, String refreshToken) { - this.email = email; - this.accessToken = accessToken; - this.refreshToken = refreshToken; - } - - public static Token of(String email, String accessToken, String refreshToken) { - return Token.builder() - .email(email) - .accessToken(accessToken) - .refreshToken(refreshToken) - .build(); - } - - public void update(String accessToken, String refreshToken) { - this.accessToken = accessToken; - this.refreshToken = refreshToken; - } -} diff --git a/daepiro-redis/src/main/java/com/numberone/backend/domain/token/repository/RefreshTokenRepository.java b/daepiro-redis/src/main/java/com/numberone/backend/domain/token/repository/RefreshTokenRepository.java new file mode 100644 index 00000000..131887fb --- /dev/null +++ b/daepiro-redis/src/main/java/com/numberone/backend/domain/token/repository/RefreshTokenRepository.java @@ -0,0 +1,10 @@ +package com.numberone.backend.domain.token.repository; + +import com.numberone.backend.domain.token.entity.RefreshToken; +import org.springframework.data.repository.CrudRepository; + +import java.util.Optional; + +public interface RefreshTokenRepository extends CrudRepository { + Optional findByToken(String token); +} diff --git a/daepiro-redis/src/main/java/com/numberone/backend/domain/token/repository/TokenRepository.java b/daepiro-redis/src/main/java/com/numberone/backend/domain/token/repository/TokenRepository.java deleted file mode 100644 index ae624042..00000000 --- a/daepiro-redis/src/main/java/com/numberone/backend/domain/token/repository/TokenRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.numberone.backend.domain.token.repository; - -import com.numberone.backend.domain.token.entity.Token; -import org.springframework.data.repository.CrudRepository; - -import java.util.Optional; - -public interface TokenRepository extends CrudRepository { - Optional findByRefreshToken(String refreshToken); -} diff --git a/daepiro-redis/src/main/java/com/numberone/backend/domain/token/service/RefreshTokenService.java b/daepiro-redis/src/main/java/com/numberone/backend/domain/token/service/RefreshTokenService.java new file mode 100644 index 00000000..b81f9a4c --- /dev/null +++ b/daepiro-redis/src/main/java/com/numberone/backend/domain/token/service/RefreshTokenService.java @@ -0,0 +1,24 @@ +package com.numberone.backend.domain.token.service; + +import com.numberone.backend.domain.token.entity.RefreshToken; +import com.numberone.backend.domain.token.repository.RefreshTokenRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class RefreshTokenService { + private final RefreshTokenRepository refreshTokenRepository; + + public void update(long userId, String token) { + if (refreshTokenRepository.existsById(userId)) + refreshTokenRepository.deleteById(userId); + refreshTokenRepository.save(RefreshToken.of(userId, token)); + } + + public Optional findByToken(String token) { + return refreshTokenRepository.findByToken(token); + } +}