diff --git a/src/main/java/com/jangburich/domain/menu/domain/controller/MenuController.java b/src/main/java/com/jangburich/domain/menu/domain/controller/MenuController.java index 95f55c7..6a2fd03 100644 --- a/src/main/java/com/jangburich/domain/menu/domain/controller/MenuController.java +++ b/src/main/java/com/jangburich/domain/menu/domain/controller/MenuController.java @@ -2,6 +2,7 @@ import java.util.List; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -16,9 +17,9 @@ import com.jangburich.domain.menu.domain.MenuGetResponseDTO; import com.jangburich.domain.menu.domain.MenuUpdateRequestDTO; import com.jangburich.domain.menu.domain.service.MenuService; -import com.jangburich.global.GetAuthorization; import com.jangburich.global.payload.Message; import com.jangburich.global.payload.ResponseCustom; +import com.jangburich.utils.parser.AuthenticationParser; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -33,31 +34,31 @@ public class MenuController { @PostMapping("/register") public ResponseCustom registerMenu( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader, + Authentication authentication, @RequestBody MenuCreateRequestDTO menuCreateRequestDTO) { - menuService.registerMenu(GetAuthorization.getUserId(authorizationHeader), menuCreateRequestDTO); + menuService.registerMenu(AuthenticationParser.parseUserId(authentication), menuCreateRequestDTO); return ResponseCustom.OK(Message.builder().message("success").build()); } @PatchMapping("/update/{id}") public ResponseCustom updateMenu( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader, @PathVariable Long id, + Authentication authentication, @PathVariable Long id, @RequestBody MenuUpdateRequestDTO menuUpdateRequestDTO) { - menuService.updateMenu(GetAuthorization.getUserId(authorizationHeader), id, menuUpdateRequestDTO); + menuService.updateMenu(AuthenticationParser.parseUserId(authentication), id, menuUpdateRequestDTO); return ResponseCustom.OK(Message.builder().message("success").build()); } @DeleteMapping("/{id}") public ResponseCustom deleteMenu( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader, @PathVariable Long id) { - menuService.deleteMenu(GetAuthorization.getUserId(authorizationHeader), id); + Authentication authentication, @PathVariable Long id) { + menuService.deleteMenu(AuthenticationParser.parseUserId(authentication), id); return ResponseCustom.OK(Message.builder().message("success").build()); } @GetMapping("") public ResponseCustom> getMenu( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader) { - List menu = menuService.getMenu(GetAuthorization.getUserId(authorizationHeader)); + Authentication authentication) { + List menu = menuService.getMenu(AuthenticationParser.parseUserId(authentication)); return ResponseCustom.OK(menu); } } diff --git a/src/main/java/com/jangburich/domain/owner/domain/controller/OwnerController.java b/src/main/java/com/jangburich/domain/owner/domain/controller/OwnerController.java index bf7f07a..9843766 100644 --- a/src/main/java/com/jangburich/domain/owner/domain/controller/OwnerController.java +++ b/src/main/java/com/jangburich/domain/owner/domain/controller/OwnerController.java @@ -1,18 +1,17 @@ package com.jangburich.domain.owner.domain.controller; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.jangburich.domain.oauth.domain.CustomOAuthUser; import com.jangburich.domain.owner.domain.OwnerCreateReqDTO; import com.jangburich.domain.owner.domain.OwnerGetResDTO; import com.jangburich.domain.owner.domain.service.OwnerService; -import com.jangburich.global.GetAuthorization; import com.jangburich.global.payload.Message; import com.jangburich.global.payload.ResponseCustom; +import com.jangburich.utils.parser.AuthenticationParser; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -29,9 +28,9 @@ public class OwnerController { @Operation(summary = "사장님 정보 등록", description = "사장님 상세 정보를 등록합니다.") @PostMapping("/register") public ResponseCustom registerOwner( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader, + Authentication authentication, OwnerCreateReqDTO ownerCreateReqDTO) { - ownerService.registerOwner(GetAuthorization.getUserId(authorizationHeader), ownerCreateReqDTO); + ownerService.registerOwner(AuthenticationParser.parseUserId(authentication), ownerCreateReqDTO); return ResponseCustom.OK(Message.builder() .message("success") .build()); @@ -40,7 +39,7 @@ public ResponseCustom registerOwner( @Operation(summary = "사장님 정보 조회", description = "사장님 정보를 조회합니다.") @GetMapping("") public ResponseCustom getOwnerInfo( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader) { - return ResponseCustom.OK(ownerService.getOwnerInfo(GetAuthorization.getUserId(authorizationHeader))); + Authentication authentication) { + return ResponseCustom.OK(ownerService.getOwnerInfo(AuthenticationParser.parseUserId(authentication))); } } diff --git a/src/main/java/com/jangburich/domain/payment/presentation/PaymentController.java b/src/main/java/com/jangburich/domain/payment/presentation/PaymentController.java index 3989c07..050a5df 100644 --- a/src/main/java/com/jangburich/domain/payment/presentation/PaymentController.java +++ b/src/main/java/com/jangburich/domain/payment/presentation/PaymentController.java @@ -1,8 +1,8 @@ package com.jangburich.domain.payment.presentation; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -14,8 +14,8 @@ import com.jangburich.domain.payment.dto.response.ReadyResponse; import com.jangburich.domain.payment.exception.PaymentCancellationException; import com.jangburich.domain.payment.exception.PaymentFailedException; -import com.jangburich.global.GetAuthorization; import com.jangburich.global.payload.ResponseCustom; +import com.jangburich.utils.parser.AuthenticationParser; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -32,10 +32,10 @@ public class PaymentController { @Operation(summary = "결제 준비", description = "카카오페이 등 결제 수단을 준비한다.") @PostMapping("/ready") public ResponseCustom payReady( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader, + Authentication authentication, @RequestBody PayRequest payRequest) { return ResponseCustom.OK( - paymentProcessingService.processPayment(GetAuthorization.getUserId(authorizationHeader), payRequest)); + paymentProcessingService.processPayment(AuthenticationParser.parseUserId(authentication), payRequest)); } @Operation(summary = "결제 성공", description = "결제 성공") diff --git a/src/main/java/com/jangburich/domain/store/domain/controller/StoreController.java b/src/main/java/com/jangburich/domain/store/domain/controller/StoreController.java index f76f3a8..e8da9bf 100644 --- a/src/main/java/com/jangburich/domain/store/domain/controller/StoreController.java +++ b/src/main/java/com/jangburich/domain/store/domain/controller/StoreController.java @@ -2,6 +2,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PatchMapping; @@ -24,9 +25,9 @@ import com.jangburich.domain.store.domain.dto.response.PaymentGroupDetailResponse; import com.jangburich.domain.store.domain.dto.response.SearchStoresResponse; import com.jangburich.domain.store.domain.service.StoreService; -import com.jangburich.global.GetAuthorization; import com.jangburich.global.payload.Message; import com.jangburich.global.payload.ResponseCustom; +import com.jangburich.utils.parser.AuthenticationParser; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -64,18 +65,18 @@ public ResponseCustom> searchStores( @Operation(summary = "가게 등록", description = "신규 파트너 가게를 등록합니다.") @PostMapping("/create") public ResponseCustom createStore( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader, + Authentication authentication, @RequestBody StoreCreateRequestDTO storeCreateRequestDTO) { - storeService.createStore(GetAuthorization.getUserId(authorizationHeader), storeCreateRequestDTO); + storeService.createStore(AuthenticationParser.parseUserId(authentication), storeCreateRequestDTO); return ResponseCustom.OK(Message.builder().message("success").build()); } @Operation(summary = "가게 추가정보 저장", description = "예약 가능 여부, 최소 선결제 금액, 선결제 사용 기간을 저장합니다.") @PostMapping("/create/additionalInfo") public ResponseCustom createAdditionalInfo( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader, + Authentication authentication, @RequestBody StoreAdditionalInfoCreateRequestDTO storeAdditionalInfoCreateRequestDTO) { - storeService.createAdditionalInfo(GetAuthorization.getUserId(authorizationHeader), + storeService.createAdditionalInfo(AuthenticationParser.parseUserId(authentication), storeAdditionalInfoCreateRequestDTO); return ResponseCustom.OK(Message.builder().message("success").build()); } @@ -83,43 +84,43 @@ public ResponseCustom createAdditionalInfo( @Operation(summary = "가게 정보 수정", description = "가게 정보를 수정합니다.") @PatchMapping("/update") public ResponseCustom updateStore( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader, + Authentication authentication, @RequestBody StoreUpdateRequestDTO storeUpdateRequestDTO) { - storeService.updateStore(GetAuthorization.getUserId(authorizationHeader), storeUpdateRequestDTO); + storeService.updateStore(AuthenticationParser.parseUserId(authentication), storeUpdateRequestDTO); return ResponseCustom.OK(Message.builder().message("success").build()); } @Operation(summary = "가게 정보 조회", description = "가게 상세 정보를 조회합니다.") @GetMapping("") public ResponseCustom getStoreInfo( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader) { - return ResponseCustom.OK(storeService.getStoreInfo(GetAuthorization.getUserId(authorizationHeader))); + Authentication authentication) { + return ResponseCustom.OK(storeService.getStoreInfo(AuthenticationParser.parseUserId(authentication))); } @Operation(summary = "결제 그룹 조회", description = "장부 결제 그룹을 조회합니다.") @GetMapping("/payment_group") public ResponseCustom> getPaymentGroup( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader, + Authentication authentication, Pageable pageable) { return ResponseCustom.OK( - storeService.getPaymentGroup(GetAuthorization.getUserId(authorizationHeader), pageable)); + storeService.getPaymentGroup(AuthenticationParser.parseUserId(authentication), pageable)); } @Operation(summary = "결제 그룹 상세 조회", description = "장부 결제 그룹을 상세 조회합니다.") @GetMapping("/payment_group/{teamId}") public ResponseCustom getPaymentGroupDetail( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader, @PathVariable Long teamId, + Authentication authentication, @PathVariable Long teamId, Pageable pageable) { return ResponseCustom.OK( - storeService.getPaymentGroupDetail(GetAuthorization.getUserId(authorizationHeader), teamId, pageable)); + storeService.getPaymentGroupDetail(AuthenticationParser.parseUserId(authentication), teamId, pageable)); } @Operation(summary = "결제 내역 조회", description = "가게에서 일어난 결제 내역을 조회합니다.") @GetMapping("/payment_history") public ResponseCustom getPaymentHistory( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader, + Authentication authentication, Pageable pageable) { return ResponseCustom.OK( - storeService.getPaymentHistory(GetAuthorization.getUserId(authorizationHeader), pageable)); + storeService.getPaymentHistory(AuthenticationParser.parseUserId(authentication), pageable)); } } \ No newline at end of file diff --git a/src/main/java/com/jangburich/domain/store/domain/service/StoreService.java b/src/main/java/com/jangburich/domain/store/domain/service/StoreService.java index 665c969..0589a00 100644 --- a/src/main/java/com/jangburich/domain/store/domain/service/StoreService.java +++ b/src/main/java/com/jangburich/domain/store/domain/service/StoreService.java @@ -28,7 +28,6 @@ import com.jangburich.domain.team.domain.repository.TeamRepository; import com.jangburich.domain.user.domain.User; import com.jangburich.domain.user.repository.UserRepository; -import com.jangburich.global.GetAuthorization; import com.jangburich.global.error.DefaultNullPointerException; import com.jangburich.global.payload.ErrorCode; @@ -173,7 +172,7 @@ public Page getPaymentGroup(String userId, Pageable pageab public Page searchByCategory(final String authentication, final Integer searchRadius, final Category category, final StoreSearchCondition storeSearchCondition, final Pageable pageable) { - User user = userRepository.findByProviderId(GetAuthorization.getUserId(authentication)) + User user = userRepository.findByProviderId(authentication) .orElseThrow(() -> new DefaultNullPointerException(ErrorCode.INVALID_AUTHENTICATION)); return storeRepository.findStoresByCategory(user.getUserId(), searchRadius, category, storeSearchCondition, pageable); @@ -181,7 +180,7 @@ public Page searchByCategory(final String authentication, public Page searchStores(final String authentication, final String keyword, final StoreSearchConditionWithType storeSearchConditionWithType, final Pageable pageable) { - User user = userRepository.findByProviderId(GetAuthorization.getUserId(authentication)) + User user = userRepository.findByProviderId(authentication) .orElseThrow(() -> new DefaultNullPointerException(ErrorCode.INVALID_AUTHENTICATION)); return storeRepository.findStores(user.getUserId(), keyword, storeSearchConditionWithType, pageable); } diff --git a/src/main/java/com/jangburich/domain/team/application/TeamService.java b/src/main/java/com/jangburich/domain/team/application/TeamService.java index 0a05daa..52f0fdc 100644 --- a/src/main/java/com/jangburich/domain/team/application/TeamService.java +++ b/src/main/java/com/jangburich/domain/team/application/TeamService.java @@ -12,7 +12,6 @@ import com.jangburich.domain.team.dto.request.RegisterTeamRequest; import com.jangburich.domain.user.domain.User; import com.jangburich.domain.user.repository.UserRepository; -import com.jangburich.global.GetAuthorization; import com.jangburich.global.payload.Message; import lombok.RequiredArgsConstructor; @@ -30,8 +29,7 @@ public class TeamService { @Transactional public Message registerTeam(String userId, RegisterTeamRequest registerTeamRequest) { - - User user = userRepository.findByProviderId(GetAuthorization.getUserId(userId)) + User user = userRepository.findByProviderId(userId) .orElseThrow(() -> new NullPointerException()); Team team = Team.builder() @@ -57,7 +55,7 @@ public Message registerTeam(String userId, RegisterTeamRequest registerTeamReque @Transactional public Message joinTeam(String userId, String joinCode) { - User user = userRepository.findByProviderId(GetAuthorization.getUserId(userId)) + User user = userRepository.findByProviderId(userId) .orElseThrow(() -> new NullPointerException()); Team team = teamRepository.findBySecretCode(joinCode) diff --git a/src/main/java/com/jangburich/domain/team/presentation/TeamController.java b/src/main/java/com/jangburich/domain/team/presentation/TeamController.java index e9dc726..e79ae71 100644 --- a/src/main/java/com/jangburich/domain/team/presentation/TeamController.java +++ b/src/main/java/com/jangburich/domain/team/presentation/TeamController.java @@ -3,7 +3,6 @@ import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -29,19 +28,19 @@ public class TeamController { @Operation(summary = "팀 생성", description = "팀을 생성한다. 팀 리더는 생성자") @PostMapping public ResponseCustom registerTeam( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader, + Authentication authentication, @RequestBody RegisterTeamRequest registerTeamRequest ) { return ResponseCustom.OK( - teamService.registerTeam(authorizationHeader, registerTeamRequest)); + teamService.registerTeam(AuthenticationParser.parseUserId(authentication), registerTeamRequest)); } @Operation(summary = "팀 가입", description = "비밀 코드를 입력하여, 팀에 가입한다.") @PostMapping("/join/{joinCode}") public ResponseCustom joinTeam( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader, + Authentication authentication, @PathVariable("joinCode") String joinCode ) { - return ResponseCustom.OK(teamService.joinTeam(authorizationHeader, joinCode)); + return ResponseCustom.OK(teamService.joinTeam(AuthenticationParser.parseUserId(authentication), joinCode)); } } diff --git a/src/main/java/com/jangburich/domain/user/controller/UserController.java b/src/main/java/com/jangburich/domain/user/controller/UserController.java index 6f9de28..25798c3 100644 --- a/src/main/java/com/jangburich/domain/user/controller/UserController.java +++ b/src/main/java/com/jangburich/domain/user/controller/UserController.java @@ -7,20 +7,21 @@ import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import com.jangburich.domain.user.domain.KakaoApiResponseDTO; +import com.jangburich.domain.user.domain.TokenResponseDTO; import com.jangburich.domain.user.service.UserService; -import com.jangburich.global.payload.Message; import com.jangburich.global.payload.ResponseCustom; +import com.jangburich.utils.parser.AuthenticationParser; import lombok.RequiredArgsConstructor; @@ -32,32 +33,36 @@ public class UserController { private final UserService userService; @PostMapping("/login") - public ResponseCustom> login( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader) { - - return ResponseCustom.OK(null); + public ResponseCustom login( + @RequestParam String authorizationHeader) { + TokenResponseDTO login = userService.login(authorizationHeader); + return ResponseCustom.OK(login); } @GetMapping("/user-info") public ResponseEntity getUserInfo( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader) { - KakaoApiResponseDTO userInfo = userService.getUserInfo(authorizationHeader); + Authentication authentication) { + KakaoApiResponseDTO userInfo = userService.getUserInfo(AuthenticationParser.parseUserId(authentication)); return ResponseEntity.ok(userInfo); } @PostMapping("/join/user") - public ResponseCustom joinUser( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader) { - userService.joinUser(authorizationHeader); - return ResponseCustom.OK(Message.builder().message("success").build()); + public ResponseCustom joinUser( + @RequestParam String authorizationHeader) { + return ResponseCustom.OK(userService.joinUser(authorizationHeader)); } @PostMapping("/join/owner") - public ResponseCustom joinOwner( - @RequestAttribute(value = "authorizationHeader") String authorizationHeader) { - userService.joinOwner(authorizationHeader); - return ResponseCustom.OK(Message.builder().message("success").build()); + public ResponseCustom joinOwner( + @RequestParam String authorizationHeader) { + return ResponseCustom.OK(userService.joinOwner(authorizationHeader)); + } + + @PostMapping("/token/reissue") + public ResponseEntity reissueAccessToken(@RequestParam String refreshToken) { + String newAccessToken = userService.reissueAccessToken(refreshToken); + return ResponseEntity.ok(newAccessToken); } @PostMapping("/token") diff --git a/src/main/java/com/jangburich/domain/user/domain/TokenResponseDTO.java b/src/main/java/com/jangburich/domain/user/domain/TokenResponseDTO.java new file mode 100644 index 0000000..766f90c --- /dev/null +++ b/src/main/java/com/jangburich/domain/user/domain/TokenResponseDTO.java @@ -0,0 +1,15 @@ +package com.jangburich.domain.user.domain; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@RequiredArgsConstructor +public class TokenResponseDTO { + private String accessToken; + private long accessTokenExpires; + private String refreshToken; + private long refreshTokenExpires; +} diff --git a/src/main/java/com/jangburich/domain/user/domain/User.java b/src/main/java/com/jangburich/domain/user/domain/User.java index b2593c3..8a39769 100644 --- a/src/main/java/com/jangburich/domain/user/domain/User.java +++ b/src/main/java/com/jangburich/domain/user/domain/User.java @@ -1,20 +1,12 @@ package com.jangburich.domain.user.domain; -import java.util.HashSet; -import java.util.Set; - import com.jangburich.domain.common.BaseEntity; -import com.jangburich.domain.team.domain.Team; + import jakarta.persistence.Column; import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.JoinTable; -import jakarta.persistence.ManyToMany; -import jakarta.persistence.ManyToOne; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -26,43 +18,49 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class User extends BaseEntity { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "id", updatable = false) - private Long userId; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", updatable = false) + private Long userId; + + @Column(name = "provider_id") + private String providerId; - @Column(name = "provider_id") - private String providerId; + @Column(name = "email") + private String email; - @Column(name = "email") - private String email; + @Column(name = "phone_number") + private String phoneNumber; - @Column(name = "phone_number") - private String phoneNumber; + @Column(name = "is_term_accepted") + private Boolean isTermAccepted; - @Column(name = "is_term_accepted") - private Boolean isTermAccepted; + @Column(name = "nickname", nullable = false, unique = true) + private String nickname; - @Column(name = "nickname", nullable = false, unique = true) - private String nickname; + @Column(name = "profile_image_url") + private String profileImageUrl; - @Column(name = "profile_image_url") - private String profileImageUrl; + @Column(name = "character_image_url") + private String characterImageUrl; - @Column(name = "character_image_url") - private String characterImageUrl; + @Column(name = "role") + private String role; - @Column(name = "role") - private String role; + @Column(name = "refresh_token") + private String refreshToken; + public void updateRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } - public static User create(String userId, String nickname, String email, String image, String role) { - User newUser = new User(); - newUser.setProviderId(userId); - newUser.setNickname(nickname); - newUser.setEmail(email); - newUser.setProfileImageUrl(image); - newUser.setRole(role); - return newUser; - } + public static User create(String userId, String nickname, String email, String image, String role) { + User newUser = new User(); + newUser.setProviderId(userId); + newUser.setNickname(nickname); + newUser.setEmail(email); + newUser.setProfileImageUrl(image); + newUser.setRole(role); + return newUser; + } } diff --git a/src/main/java/com/jangburich/domain/user/repository/UserRepository.java b/src/main/java/com/jangburich/domain/user/repository/UserRepository.java index 1db050d..a6facba 100644 --- a/src/main/java/com/jangburich/domain/user/repository/UserRepository.java +++ b/src/main/java/com/jangburich/domain/user/repository/UserRepository.java @@ -2,11 +2,14 @@ import java.util.Optional; -import com.jangburich.domain.user.domain.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import com.jangburich.domain.user.domain.User; + @Repository public interface UserRepository extends JpaRepository { Optional findByProviderId(String providerId); + + Optional findByRefreshToken(String token); } diff --git a/src/main/java/com/jangburich/domain/user/service/UserService.java b/src/main/java/com/jangburich/domain/user/service/UserService.java index 66fe4bb..2ff94f2 100644 --- a/src/main/java/com/jangburich/domain/user/service/UserService.java +++ b/src/main/java/com/jangburich/domain/user/service/UserService.java @@ -1,7 +1,6 @@ package com.jangburich.domain.user.service; -import java.util.Map; - +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -15,8 +14,10 @@ import com.jangburich.domain.store.domain.Store; import com.jangburich.domain.store.domain.repository.StoreRepository; import com.jangburich.domain.user.domain.KakaoApiResponseDTO; +import com.jangburich.domain.user.domain.TokenResponseDTO; import com.jangburich.domain.user.domain.User; import com.jangburich.domain.user.repository.UserRepository; +import com.jangburich.utils.JwtUtil; import lombok.RequiredArgsConstructor; @@ -27,6 +28,13 @@ public class UserService { private final UserRepository userRepository; private final OwnerRepository ownerRepository; private final StoreRepository storeRepository; + private final JwtUtil jwtUtil; + + @Value("${spring.jwt.access.expiration}") + private long accessTokenExpiration; + + @Value("${spring.jwt.refresh.expiration}") + private long refreshTokenExpiration; public KakaoApiResponseDTO getUserInfo(String accessToken) { String userInfoUrl = "https://kapi.kakao.com/v2/user/me"; @@ -43,28 +51,90 @@ public KakaoApiResponseDTO getUserInfo(String accessToken) { } @Transactional - public void joinUser(String accessToken) { - KakaoApiResponseDTO userInfo = getUserInfo(accessToken); + public TokenResponseDTO joinUser(String kakaoaccessToken) { + KakaoApiResponseDTO userInfo = getUserInfo(kakaoaccessToken); User user = userRepository.findByProviderId("kakao_" + userInfo.getId()).orElse(null); if (user == null) { - userRepository.save(User.create("kakao_" + userInfo.getId(), userInfo.getProperties().getNickname(), + user = userRepository.save(User.create("kakao_" + userInfo.getId(), userInfo.getProperties().getNickname(), userInfo.getKakaoAccount().getEmail(), userInfo.getProperties().getProfileImage(), "ROLE_USER")); } + + String accessToken = jwtUtil.createAccessToken(user.getProviderId(), user.getRole()); + String refreshToken = jwtUtil.createRefreshToken(); + + user.updateRefreshToken(refreshToken); + userRepository.save(user); + + TokenResponseDTO tokenResponseDTO = new TokenResponseDTO(); + tokenResponseDTO.setAccessToken(accessToken); + tokenResponseDTO.setRefreshToken(refreshToken); + tokenResponseDTO.setAccessTokenExpires(accessTokenExpiration); + tokenResponseDTO.setRefreshTokenExpires(refreshTokenExpiration); + + return tokenResponseDTO; } @Transactional - public void joinOwner(String accessToken) { - KakaoApiResponseDTO userInfo = getUserInfo(accessToken); + public TokenResponseDTO joinOwner(String kakaoAccessToken) { + KakaoApiResponseDTO userInfo = getUserInfo(kakaoAccessToken); User user = userRepository.findByProviderId("kakao_" + userInfo.getId()).orElse(null); if (user == null) { - User newUser = userRepository.save( + user = userRepository.save( User.create("kakao_" + userInfo.getId(), userInfo.getProperties().getNickname(), userInfo.getKakaoAccount().getEmail(), userInfo.getProperties().getProfileImage(), "ROLE_OWNER")); - Owner newOwner = ownerRepository.save(Owner.create(newUser)); + Owner newOwner = ownerRepository.save(Owner.create(user)); storeRepository.save(Store.create(newOwner)); } + + String accessToken = jwtUtil.createAccessToken(user.getProviderId(), user.getRole()); + String refreshToken = jwtUtil.createRefreshToken(); + + user.updateRefreshToken(refreshToken); + userRepository.save(user); + + TokenResponseDTO tokenResponseDTO = new TokenResponseDTO(); + tokenResponseDTO.setAccessToken(accessToken); + tokenResponseDTO.setRefreshToken(refreshToken); + tokenResponseDTO.setAccessTokenExpires(accessTokenExpiration); + tokenResponseDTO.setRefreshTokenExpires(refreshTokenExpiration); + + return tokenResponseDTO; + } + + @Transactional + public TokenResponseDTO login(String accessToken) { + KakaoApiResponseDTO userInfo = getUserInfo(accessToken); + + User user = userRepository.findByProviderId("kakao_" + userInfo.getId()) + .orElseThrow(() -> new IllegalArgumentException("사용자가 존재하지 않습니다.")); + + String newAccessToken = jwtUtil.createAccessToken(user.getProviderId(), user.getRole()); + String newRefreshToken = jwtUtil.createRefreshToken(); + + user.updateRefreshToken(newRefreshToken); + userRepository.save(user); + + TokenResponseDTO tokenResponseDTO = new TokenResponseDTO(); + tokenResponseDTO.setAccessToken(newAccessToken); + tokenResponseDTO.setRefreshToken(newRefreshToken); + tokenResponseDTO.setAccessTokenExpires(accessTokenExpiration); + tokenResponseDTO.setRefreshTokenExpires(refreshTokenExpiration); + + return tokenResponseDTO; + } + + @Transactional + public String reissueAccessToken(String refreshToken) { + User user = userRepository.findByRefreshToken(refreshToken) + .orElseThrow(() -> new IllegalArgumentException("유효하지 않은 Refresh Token입니다.")); + + if (!jwtUtil.isTokenExpired(refreshToken)) { + throw new IllegalArgumentException("Refresh Token이 만료되었습니다."); + } + + return jwtUtil.createAccessToken(user.getProviderId(), user.getRole()); } } diff --git a/src/main/java/com/jangburich/global/GetAuthorization.java b/src/main/java/com/jangburich/global/GetAuthorization.java deleted file mode 100644 index 689ce6d..0000000 --- a/src/main/java/com/jangburich/global/GetAuthorization.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.jangburich.global; - -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.RestTemplate; - -import com.jangburich.domain.user.domain.KakaoApiResponseDTO; - -public class GetAuthorization { - public static String getUserId(String accessToken) { - String userInfoUrl = "https://kapi.kakao.com/v2/user/me"; - - HttpHeaders headers = new HttpHeaders(); - headers.set("Authorization", "Bearer " + accessToken); - HttpEntity request = new HttpEntity<>(headers); - - RestTemplate restTemplate = new RestTemplate(); - ResponseEntity response = restTemplate.exchange(userInfoUrl, HttpMethod.GET, request, - KakaoApiResponseDTO.class); - - return "kakao_" + response.getBody().getId(); - } -} diff --git a/src/main/java/com/jangburich/global/config/security/SecurityConfig.java b/src/main/java/com/jangburich/global/config/security/SecurityConfig.java index c3238db..7909177 100644 --- a/src/main/java/com/jangburich/global/config/security/SecurityConfig.java +++ b/src/main/java/com/jangburich/global/config/security/SecurityConfig.java @@ -9,7 +9,8 @@ import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import com.jangburich.utils.AuthorizationHeaderFilter; +import com.jangburich.utils.JwtFilter; +import com.jangburich.utils.JwtUtil; import lombok.RequiredArgsConstructor; @@ -18,13 +19,16 @@ @EnableWebSecurity public class SecurityConfig { + private final JwtUtil jwtUtil; + @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf(AbstractHttpConfigurer::disable) .httpBasic(AbstractHttpConfigurer::disable) .formLogin(AbstractHttpConfigurer::disable) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .addFilterBefore(new AuthorizationHeaderFilter(), UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(new JwtFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class) + .authorizeHttpRequests( request -> request.requestMatchers("/**", "/oauth2/**", "/login/**", "/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/payments/**") diff --git a/src/main/java/com/jangburich/global/config/security/SwaggerConfig.java b/src/main/java/com/jangburich/global/config/security/SwaggerConfig.java index 15aa3f0..f34242f 100644 --- a/src/main/java/com/jangburich/global/config/security/SwaggerConfig.java +++ b/src/main/java/com/jangburich/global/config/security/SwaggerConfig.java @@ -23,12 +23,11 @@ public OpenAPI customOpenAPI() { return new OpenAPI() .info(info) .addSecurityItem( - new io.swagger.v3.oas.models.security.SecurityRequirement().addList("Custom String Authentication")) + new io.swagger.v3.oas.models.security.SecurityRequirement().addList("Bearer Authentication")) .components(new io.swagger.v3.oas.models.Components() - .addSecuritySchemes("Custom String Authentication", - new io.swagger.v3.oas.models.security.SecurityScheme() - .type(io.swagger.v3.oas.models.security.SecurityScheme.Type.APIKEY) // API Key 타입 설정 - .name("Authorization") - .in(io.swagger.v3.oas.models.security.SecurityScheme.In.HEADER))); // 헤더로 전달 + .addSecuritySchemes("Bearer Authentication", new io.swagger.v3.oas.models.security.SecurityScheme() + .type(io.swagger.v3.oas.models.security.SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT"))); } } diff --git a/src/main/java/com/jangburich/utils/AuthorizationHeaderFilter.java b/src/main/java/com/jangburich/utils/AuthorizationHeaderFilter.java deleted file mode 100644 index badd9dd..0000000 --- a/src/main/java/com/jangburich/utils/AuthorizationHeaderFilter.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.jangburich.utils; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; - -public class AuthorizationHeaderFilter extends OncePerRequestFilter { - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - // Authorization 헤더를 읽어서 Request 속성으로 설정 - String authorizationHeader = request.getHeader("Authorization"); - if (authorizationHeader != null && !authorizationHeader.isBlank()) { - // 속성으로 저장 - request.setAttribute("authorizationHeader", authorizationHeader); - } - - filterChain.doFilter(request, response); - } -} diff --git a/src/main/java/com/jangburich/utils/JwtFilter.java b/src/main/java/com/jangburich/utils/JwtFilter.java new file mode 100644 index 0000000..0be7452 --- /dev/null +++ b/src/main/java/com/jangburich/utils/JwtFilter.java @@ -0,0 +1,77 @@ +package com.jangburich.utils; + +import java.io.IOException; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import com.jangburich.domain.oauth.domain.CustomOAuthUser; +import com.jangburich.domain.oauth.domain.OAuthUserDTO; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class JwtFilter extends OncePerRequestFilter { + + private final JwtUtil jwtUtil; + + public JwtFilter(JwtUtil jwtUtil) { + this.jwtUtil = jwtUtil; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain) throws ServletException, IOException { + // Authorization 헤더를 찾음 + String authorizationHeader = request.getHeader("Authorization"); + + if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) { + filterChain.doFilter(request, response); + return; + } + + // "Bearer " 이후의 토큰 부분만 추출 + String token = authorizationHeader.substring(7); + + try { + // 토큰 소멸 시간 검증 + if (jwtUtil.isTokenExpired(token)) { + filterChain.doFilter(request, response); + return; + } + + // 토큰에서 사용자 정보 획득 + String userId = jwtUtil.getUserId(token); + String role = jwtUtil.getRole(token); + + // OAuth2UserDTO 생성 및 설정 + OAuthUserDTO userDTO = new OAuthUserDTO(); + userDTO.setUserId(userId); + userDTO.setRole(role); + + // CustomOAuth2User 생성 + CustomOAuthUser customOAuth2User = new CustomOAuthUser(userDTO); + + // 스프링 시큐리티 인증 토큰 생성 및 설정 + Authentication authToken = new UsernamePasswordAuthenticationToken(customOAuth2User, null, + customOAuth2User.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authToken); + + } catch (Exception e) { + // 예외 발생 시 로그 출력 및 인증 정보 제거 + SecurityContextHolder.clearContext(); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage()); + return; + } + + filterChain.doFilter(request, response); + } + +} \ No newline at end of file diff --git a/src/main/java/com/jangburich/utils/JwtUtil.java b/src/main/java/com/jangburich/utils/JwtUtil.java new file mode 100644 index 0000000..2124c03 --- /dev/null +++ b/src/main/java/com/jangburich/utils/JwtUtil.java @@ -0,0 +1,69 @@ +package com.jangburich.utils; + +import java.nio.charset.StandardCharsets; +import java.util.Date; + +import javax.crypto.SecretKey; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.Cookie; + +@Component +public class JwtUtil { + + private final SecretKey secretKey; + + @Value("${spring.jwt.access.expiration}") + private long accessTokenExpiration; + + @Value("${spring.jwt.refresh.expiration}") + private long refreshTokenExpiration; + + public JwtUtil(@Value("${spring.jwt.secret}") String secret) { + this.secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); + } + + private Claims extractAllClaims(String token) { + return Jwts.parser() + .setSigningKey(secretKey) + .build() + .parseClaimsJws(token) + .getBody(); + } + + public String getUserId(String token) { + return extractAllClaims(token).get("userId", String.class); + } + + public String getRole(String token) { + return extractAllClaims(token).get("role", String.class); + } + + public Boolean isTokenExpired(String token) { + return extractAllClaims(token).getExpiration().before(new Date()); + } + + public String createAccessToken(String userId, String role) { + return Jwts.builder() + .claim("userId", userId) + .claim("role", role) + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + accessTokenExpiration)) + .signWith(secretKey) + .compact(); + } + + public String createRefreshToken() { + return Jwts.builder() + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + refreshTokenExpiration)) + .signWith(secretKey) + .compact(); + } +} +