From 720a76e700678b47a31885c4c9b5e461d07d35f4 Mon Sep 17 00:00:00 2001 From: suhaoh Date: Sat, 19 Oct 2024 19:21:18 +0900 Subject: [PATCH 1/4] =?UTF-8?q?[43]=20AOP=20=EB=A5=BC=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=9C=20=EA=B6=8C=ED=95=9C=20=EC=B2=B4=ED=81=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/auth/PartnerCheck.java | 11 ++++ .../common/auth/RoleCheckAspect.java | 66 +++++++++++++++++++ .../commerce_site/common/auth/UserCheck.java | 11 ++++ .../commerce_site/common/util/JwtUtil.java | 25 +++++++ .../config/JwtDecoderConfig.java | 14 ++++ .../commerce_site/config/SecurityConfig.java | 23 ------- .../address/AddressController.java | 10 +-- .../representation/cart/CartController.java | 7 +- .../representation/order/OrderController.java | 3 +- .../payment/PaymentController.java | 4 +- .../product/ProductController.java | 8 +-- 11 files changed, 145 insertions(+), 37 deletions(-) create mode 100644 src/main/java/org/example/commerce_site/common/auth/PartnerCheck.java create mode 100644 src/main/java/org/example/commerce_site/common/auth/RoleCheckAspect.java create mode 100644 src/main/java/org/example/commerce_site/common/auth/UserCheck.java create mode 100644 src/main/java/org/example/commerce_site/common/util/JwtUtil.java create mode 100644 src/main/java/org/example/commerce_site/config/JwtDecoderConfig.java diff --git a/src/main/java/org/example/commerce_site/common/auth/PartnerCheck.java b/src/main/java/org/example/commerce_site/common/auth/PartnerCheck.java new file mode 100644 index 0000000..1d5cef9 --- /dev/null +++ b/src/main/java/org/example/commerce_site/common/auth/PartnerCheck.java @@ -0,0 +1,11 @@ +package org.example.commerce_site.common.auth; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface PartnerCheck { +} diff --git a/src/main/java/org/example/commerce_site/common/auth/RoleCheckAspect.java b/src/main/java/org/example/commerce_site/common/auth/RoleCheckAspect.java new file mode 100644 index 0000000..22f2c60 --- /dev/null +++ b/src/main/java/org/example/commerce_site/common/auth/RoleCheckAspect.java @@ -0,0 +1,66 @@ +package org.example.commerce_site.common.auth; + +import java.util.List; +import java.util.Map; + +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.example.commerce_site.attribute.UserRoles; +import org.example.commerce_site.common.exception.CustomException; +import org.example.commerce_site.common.exception.ErrorCode; +import org.example.commerce_site.common.util.JwtUtil; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.stereotype.Component; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Aspect +@RequiredArgsConstructor +@Component +public class RoleCheckAspect { + + private static final String AUTHORIZATION = "Authorization"; + private static final String BEARER_PREFIX = "Bearer "; + private final HttpServletRequest httpServletRequest; + private final JwtUtil jwtUtil; + + @Around("@annotation(org.example.commerce_site.common.auth.PartnerCheck)") + public Object checkPartner(ProceedingJoinPoint joinPoint) throws Throwable { + return checkRole(joinPoint, UserRoles.ROLE_PARTNER); + } + + @Around("@annotation(org.example.commerce_site.common.auth.UserCheck)") + public Object checkUser(ProceedingJoinPoint joinPoint) throws Throwable { + return checkRole(joinPoint, UserRoles.ROLE_USER); + } + + private Object checkRole(ProceedingJoinPoint joinPoint, UserRoles requiredRole) throws Throwable { + String token = extractTokenFromRequest(); + Jwt jwt = jwtUtil.decodeToken(token); + List roleList = extractAuthorities(jwt); + + if (!roleList.contains(requiredRole.name())) { + throw new CustomException(ErrorCode.ACCESS_DENIED); + } + + return joinPoint.proceed(); + } + + private String extractTokenFromRequest() { + String authorizationHeader = httpServletRequest.getHeader(AUTHORIZATION); + if (authorizationHeader == null || !authorizationHeader.startsWith(BEARER_PREFIX)) { + throw new CustomException(ErrorCode.ACCESS_DENIED); + } + return authorizationHeader.substring(BEARER_PREFIX.length()); + } + + private List extractAuthorities(Jwt jwt) { + var resourceAccess = (Map)jwt.getClaim("resource_access"); + var roles = (Map)resourceAccess.get("oauth2-client-app"); + return (List)roles.get("roles"); + } +} diff --git a/src/main/java/org/example/commerce_site/common/auth/UserCheck.java b/src/main/java/org/example/commerce_site/common/auth/UserCheck.java new file mode 100644 index 0000000..9352f59 --- /dev/null +++ b/src/main/java/org/example/commerce_site/common/auth/UserCheck.java @@ -0,0 +1,11 @@ +package org.example.commerce_site.common.auth; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface UserCheck { +} diff --git a/src/main/java/org/example/commerce_site/common/util/JwtUtil.java b/src/main/java/org/example/commerce_site/common/util/JwtUtil.java new file mode 100644 index 0000000..0d9aafc --- /dev/null +++ b/src/main/java/org/example/commerce_site/common/util/JwtUtil.java @@ -0,0 +1,25 @@ +package org.example.commerce_site.common.util; + +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.jwt.JwtException; +import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; +import org.springframework.stereotype.Component; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +@RequiredArgsConstructor +public class JwtUtil { + private final NimbusJwtDecoder jwtDecoder; + + public Jwt decodeToken(String token) { + try { + return jwtDecoder.decode(token); + } catch (JwtException e) { + log.error(e.getMessage()); + throw new JwtException(e.getMessage()); + } + } +} diff --git a/src/main/java/org/example/commerce_site/config/JwtDecoderConfig.java b/src/main/java/org/example/commerce_site/config/JwtDecoderConfig.java new file mode 100644 index 0000000..ad8d3b1 --- /dev/null +++ b/src/main/java/org/example/commerce_site/config/JwtDecoderConfig.java @@ -0,0 +1,14 @@ +package org.example.commerce_site.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; + +@Configuration +public class JwtDecoderConfig { + @Bean + public NimbusJwtDecoder jwtDecoder() { + String jwkSetUri = "http://localhost:9090/realms/oauth2/protocol/openid-connect/certs"; + return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build(); + } +} diff --git a/src/main/java/org/example/commerce_site/config/SecurityConfig.java b/src/main/java/org/example/commerce_site/config/SecurityConfig.java index c68a9cb..ddbfa36 100644 --- a/src/main/java/org/example/commerce_site/config/SecurityConfig.java +++ b/src/main/java/org/example/commerce_site/config/SecurityConfig.java @@ -1,10 +1,5 @@ package org.example.commerce_site.config; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - import org.example.commerce_site.config.filter.UserIdFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -15,10 +10,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.session.SessionRegistryImpl; -import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @@ -46,7 +38,6 @@ protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter(); - jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(this::extractAuthorities); http .csrf(AbstractHttpConfigurer::disable) @@ -64,18 +55,4 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti return http.build(); } - - private Collection extractAuthorities(Jwt jwt) { - var resourceAccess = (Map)jwt.getClaim("resource_access"); - var roles = (Map)resourceAccess.get("oauth2-client-app"); - - if (roles != null) { - var roleList = (List)roles.get("roles"); - return roleList.stream() - .map(role -> new SimpleGrantedAuthority(role)) - .collect(Collectors.toList()); - } - - return List.of(); - } } diff --git a/src/main/java/org/example/commerce_site/representation/address/AddressController.java b/src/main/java/org/example/commerce_site/representation/address/AddressController.java index dc3fad7..0569371 100644 --- a/src/main/java/org/example/commerce_site/representation/address/AddressController.java +++ b/src/main/java/org/example/commerce_site/representation/address/AddressController.java @@ -1,10 +1,10 @@ package org.example.commerce_site.representation.address; import org.example.commerce_site.application.address.AddressFacade; +import org.example.commerce_site.common.auth.UserCheck; import org.example.commerce_site.common.response.ApiSuccessResponse; import org.example.commerce_site.representation.address.dto.AddressRequest; import org.example.commerce_site.representation.address.dto.AddressResponse; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -26,7 +26,7 @@ public class AddressController { private final AddressFacade addressFacade; - @PreAuthorize("hasAuthority('ROLE_USER')") + @UserCheck @PostMapping() public ApiSuccessResponse createAddrss( @Valid @RequestBody AddressRequest.Create request, @@ -35,7 +35,7 @@ public ApiSuccessResponse createAddrss( return ApiSuccessResponse.success(); } - @PreAuthorize("hasAuthority('ROLE_USER')") + @UserCheck @GetMapping("/{address_id}") public ApiSuccessResponse getAddress( @RequestAttribute("userId") String userAuthId, @@ -44,7 +44,7 @@ public ApiSuccessResponse getAddress( AddressResponse.Get.of(addressFacade.get(addressId, userAuthId))); } - @PreAuthorize("hasAuthority('ROLE_USER')") + @UserCheck @GetMapping() public ApiSuccessResponse.PageList getAddresses( @RequestAttribute("userId") String userAuthId, @@ -53,7 +53,7 @@ public ApiSuccessResponse.PageList getAddresses( return ApiSuccessResponse.success(AddressResponse.Get.of(addressFacade.getList(userAuthId, page - 1, size))); } - @PreAuthorize("hasAuthority('ROLE_USER')") + @UserCheck @DeleteMapping("/{address_id}") public ApiSuccessResponse deleteAddress( @RequestAttribute("userId") String userAuthId, diff --git a/src/main/java/org/example/commerce_site/representation/cart/CartController.java b/src/main/java/org/example/commerce_site/representation/cart/CartController.java index 0e44a4a..b0262a9 100644 --- a/src/main/java/org/example/commerce_site/representation/cart/CartController.java +++ b/src/main/java/org/example/commerce_site/representation/cart/CartController.java @@ -1,10 +1,10 @@ package org.example.commerce_site.representation.cart; import org.example.commerce_site.application.cart.CartFacade; +import org.example.commerce_site.common.auth.UserCheck; import org.example.commerce_site.common.response.ApiSuccessResponse; import org.example.commerce_site.representation.cart.dto.CartRequest; import org.example.commerce_site.representation.cart.dto.CartResponse; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -21,10 +21,10 @@ @RestController @RequiredArgsConstructor @RequestMapping("/carts") -@PreAuthorize("hasAuthority('ROLE_USER')") public class CartController { private final CartFacade cartFacade; + @UserCheck @PostMapping() public ApiSuccessResponse createCart( @RequestAttribute("userId") String userAuthId, @@ -33,6 +33,7 @@ public ApiSuccessResponse createCart( return ApiSuccessResponse.success(); } + @UserCheck @DeleteMapping() public ApiSuccessResponse deleteCart( @RequestAttribute("userId") String userAuthId, @@ -41,6 +42,7 @@ public ApiSuccessResponse deleteCart( return ApiSuccessResponse.success(); } + @UserCheck @PatchMapping() public ApiSuccessResponse updateCart( @RequestAttribute("userId") String userAuthId, @@ -50,6 +52,7 @@ public ApiSuccessResponse updateCart( return ApiSuccessResponse.success(); } + @UserCheck @GetMapping() public ApiSuccessResponse.PageList getCart( @RequestAttribute("userId") String userAuthId, diff --git a/src/main/java/org/example/commerce_site/representation/order/OrderController.java b/src/main/java/org/example/commerce_site/representation/order/OrderController.java index 4efeba5..9f5d2b5 100644 --- a/src/main/java/org/example/commerce_site/representation/order/OrderController.java +++ b/src/main/java/org/example/commerce_site/representation/order/OrderController.java @@ -1,9 +1,9 @@ package org.example.commerce_site.representation.order; import org.example.commerce_site.application.order.OrderFacade; +import org.example.commerce_site.common.auth.UserCheck; import org.example.commerce_site.common.response.ApiSuccessResponse; import org.example.commerce_site.representation.order.request.OrderRequest; -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; @@ -18,6 +18,7 @@ public class OrderController { private final OrderFacade orderFacade; + @UserCheck @PostMapping() public ApiSuccessResponse createOrder( @RequestAttribute("userId") String userAuthId, diff --git a/src/main/java/org/example/commerce_site/representation/payment/PaymentController.java b/src/main/java/org/example/commerce_site/representation/payment/PaymentController.java index 63bc706..830eaf9 100644 --- a/src/main/java/org/example/commerce_site/representation/payment/PaymentController.java +++ b/src/main/java/org/example/commerce_site/representation/payment/PaymentController.java @@ -1,9 +1,9 @@ package org.example.commerce_site.representation.payment; import org.example.commerce_site.application.payment.PaymentFacade; +import org.example.commerce_site.common.auth.UserCheck; import org.example.commerce_site.common.response.ApiSuccessResponse; import org.example.commerce_site.representation.payment.dto.PaymentRequest; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestAttribute; @@ -18,10 +18,10 @@ @RestController @RequiredArgsConstructor @RequestMapping("/payments") -@PreAuthorize("hasAuthority('ROLE_USER')") public class PaymentController { private final PaymentFacade paymentFacade; + @UserCheck @PostMapping("/{order_id}") public ApiSuccessResponse createPayment( @PathVariable("order_id") Long orderId, diff --git a/src/main/java/org/example/commerce_site/representation/product/ProductController.java b/src/main/java/org/example/commerce_site/representation/product/ProductController.java index b3a0487..63cd3e5 100644 --- a/src/main/java/org/example/commerce_site/representation/product/ProductController.java +++ b/src/main/java/org/example/commerce_site/representation/product/ProductController.java @@ -1,10 +1,10 @@ package org.example.commerce_site.representation.product; import org.example.commerce_site.application.product.ProductFacade; +import org.example.commerce_site.common.auth.PartnerCheck; import org.example.commerce_site.common.response.ApiSuccessResponse; import org.example.commerce_site.representation.product.request.ProductRequest; import org.example.commerce_site.representation.product.response.ProductResponse; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -25,7 +25,7 @@ public class ProductController { private final ProductFacade productFacade; - @PreAuthorize("hasAuthority('ROLE_PARTNER')") + @PartnerCheck @PostMapping() public ApiSuccessResponse createProduct( @Valid @RequestBody ProductRequest.Create request, @@ -34,7 +34,7 @@ public ApiSuccessResponse createProduct( return ApiSuccessResponse.success(); } - @PreAuthorize("hasAuthority('ROLE_PARTNER')") + @PartnerCheck @PatchMapping("/{product_id}") public ApiSuccessResponse updateProduct( @PathVariable(name = "product_id") Long productId, @@ -45,7 +45,7 @@ public ApiSuccessResponse updateProduct( return ApiSuccessResponse.success(); } - @PreAuthorize("hasAuthority('ROLE_PARTNER')") + @PartnerCheck @DeleteMapping("/{product_id}") public ApiSuccessResponse deleteProduct( @PathVariable(name = "product_id") Long productId, From 82151e6b57c8d267b08843258a006df8edaac999 Mon Sep 17 00:00:00 2001 From: suhaoh Date: Sun, 20 Oct 2024 22:35:53 +0900 Subject: [PATCH 2/4] =?UTF-8?q?[43]=20jwt=20decoding=20exception=20?= =?UTF-8?q?=EC=9D=84=20custom=20exception=20=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/example/commerce_site/common/exception/ErrorCode.java | 3 +++ .../java/org/example/commerce_site/common/util/JwtUtil.java | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/example/commerce_site/common/exception/ErrorCode.java b/src/main/java/org/example/commerce_site/common/exception/ErrorCode.java index d7608f7..f5cd576 100644 --- a/src/main/java/org/example/commerce_site/common/exception/ErrorCode.java +++ b/src/main/java/org/example/commerce_site/common/exception/ErrorCode.java @@ -16,6 +16,9 @@ public enum ErrorCode { INVALID_PARAM(HttpStatus.BAD_REQUEST, 400, "잘못된 parameter 입니다."), ACCESS_DENIED(HttpStatus.FORBIDDEN, 403, "권한이 부족합니다."), METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, 405, "허용되지 않은 메소드 입니다."), + //auth + JWT_DECODING_ERROR(HttpStatus.NOT_FOUND, 500, "JWT Decoding error"), + //address ADDRESS_NOT_FOUND(HttpStatus.NOT_FOUND, 404, "배송지 정보를 찾을 수 없습니다."), diff --git a/src/main/java/org/example/commerce_site/common/util/JwtUtil.java b/src/main/java/org/example/commerce_site/common/util/JwtUtil.java index 0d9aafc..bc8c85a 100644 --- a/src/main/java/org/example/commerce_site/common/util/JwtUtil.java +++ b/src/main/java/org/example/commerce_site/common/util/JwtUtil.java @@ -1,5 +1,7 @@ package org.example.commerce_site.common.util; +import org.example.commerce_site.common.exception.CustomException; +import org.example.commerce_site.common.exception.ErrorCode; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtException; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; @@ -19,7 +21,7 @@ public Jwt decodeToken(String token) { return jwtDecoder.decode(token); } catch (JwtException e) { log.error(e.getMessage()); - throw new JwtException(e.getMessage()); + throw new CustomException(ErrorCode.JWT_DECODING_ERROR); } } } From 995116f2127bdf25151f74436db191ee14489c3e Mon Sep 17 00:00:00 2001 From: suhaoh Date: Sat, 26 Oct 2024 16:53:55 +0900 Subject: [PATCH 3/4] =?UTF-8?q?[43]=20jwt=20=EC=98=88=EC=99=B8=EB=A5=BC=20?= =?UTF-8?q?401=20=EB=A1=9C=20=EC=BD=94=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/example/commerce_site/common/exception/ErrorCode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/example/commerce_site/common/exception/ErrorCode.java b/src/main/java/org/example/commerce_site/common/exception/ErrorCode.java index f5cd576..e353e97 100644 --- a/src/main/java/org/example/commerce_site/common/exception/ErrorCode.java +++ b/src/main/java/org/example/commerce_site/common/exception/ErrorCode.java @@ -17,7 +17,7 @@ public enum ErrorCode { ACCESS_DENIED(HttpStatus.FORBIDDEN, 403, "권한이 부족합니다."), METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, 405, "허용되지 않은 메소드 입니다."), //auth - JWT_DECODING_ERROR(HttpStatus.NOT_FOUND, 500, "JWT Decoding error"), + JWT_DECODING_ERROR(HttpStatus.NOT_FOUND, 401, "JWT Decoding error"), //address ADDRESS_NOT_FOUND(HttpStatus.NOT_FOUND, 404, "배송지 정보를 찾을 수 없습니다."), From fb0599ad6b0641547f562085dce31522f2501db2 Mon Sep 17 00:00:00 2001 From: suhaoh Date: Sat, 26 Oct 2024 21:15:16 +0900 Subject: [PATCH 4/4] =?UTF-8?q?[43]=20HttpStatus=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/example/commerce_site/common/exception/ErrorCode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/example/commerce_site/common/exception/ErrorCode.java b/src/main/java/org/example/commerce_site/common/exception/ErrorCode.java index e353e97..9c85942 100644 --- a/src/main/java/org/example/commerce_site/common/exception/ErrorCode.java +++ b/src/main/java/org/example/commerce_site/common/exception/ErrorCode.java @@ -17,7 +17,7 @@ public enum ErrorCode { ACCESS_DENIED(HttpStatus.FORBIDDEN, 403, "권한이 부족합니다."), METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, 405, "허용되지 않은 메소드 입니다."), //auth - JWT_DECODING_ERROR(HttpStatus.NOT_FOUND, 401, "JWT Decoding error"), + JWT_DECODING_ERROR(HttpStatus.UNAUTHORIZED, 401, "JWT Decoding error"), //address ADDRESS_NOT_FOUND(HttpStatus.NOT_FOUND, 404, "배송지 정보를 찾을 수 없습니다."),