Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

9주차 미션 / 서버 1조 김상준 #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package kuit3.backend.common.argument_resolver;

import jakarta.servlet.http.HttpServletRequest;
import kuit3.backend.jwt.JwtUtil;
import kuit3.backend.service.AuthService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.security.access.prepost.PreAuthorize;
Expand All @@ -12,10 +15,13 @@

@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthHandlerArgumentResolver implements HandlerMethodArgumentResolver {
private final JwtUtil jwtTokenUtil;
private final AuthService authService;
@Override
public boolean supportsParameter(MethodParameter parameter) {
boolean hasAnnotation = parameter.hasParameterAnnotation(PreAuthorize.class);
boolean hasAnnotation = parameter.hasParameterAnnotation(JwtAuthrize.class);
boolean hasType = long.class.isAssignableFrom(parameter.getParameterType());
log.info("hasAnnotation={}, hasType={}, hasAnnotation && hasType={}", hasAnnotation, hasType, hasAnnotation&&hasType);
return hasAnnotation && hasType;
Expand All @@ -25,6 +31,16 @@ public boolean supportsParameter(MethodParameter parameter) {
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
log.info("userId={}", request.getAttribute("userId"));
return request.getAttribute("userId");

String accessToken = jwtTokenUtil.resolveAccessToken(request);
jwtTokenUtil.validateAccessToken(accessToken);
String email = jwtTokenUtil.getEmail(accessToken);
jwtTokenUtil.validatePayload(email);
long userId = authService.getUserIdByEmail(email);
Copy link
Contributor

@jaeuk520 jaeuk520 May 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

토큰을 생성할 때부터 payload에 userId를 담아 생성한다면 ArgumentResolver에서 Service를 호출하는 행위를 방지할 수 있을 것 같아요:) 레이어드 아키텍처의 의존 방향을 생각해보시면 좋을 것 같습니다! (사실 9주차 미션 제공 코드에는 이미 payload에 userId를 담아둡니다..!! 서비스 단으로 갈 필요 없이 바로 추출해서 사용하면 돼요)


// HttpServletRequest에 userId 속성 설정
//HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
request.setAttribute("userId", userId);
return userId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package kuit3.backend.common.argument_resolver;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface JwtAuthrize {
}
22 changes: 22 additions & 0 deletions src/main/java/kuit3/backend/common/exception/ShopException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package kuit3.backend.common.exception;


import kuit3.backend.common.response.status.ResponseStatus;
import lombok.Getter;

@Getter
public class ShopException extends RuntimeException {

private final ResponseStatus exceptionStatus;

public ShopException(ResponseStatus exceptionStatus) {
super(exceptionStatus.getMessage());
this.exceptionStatus = exceptionStatus;
}

public ShopException(ResponseStatus exceptionStatus, String message) {
super(message);
this.exceptionStatus = exceptionStatus;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import kuit3.backend.common.exception.jwt.bad_request.JwtNoTokenException;
import kuit3.backend.common.exception.jwt.bad_request.JwtUnsupportedTokenException;
import kuit3.backend.jwt.JwtProvider;
import kuit3.backend.jwt.JwtUtil;
import kuit3.backend.service.AuthService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -24,47 +25,48 @@ public class JwtAuthInterceptor implements HandlerInterceptor {
private static final String JWT_TOKEN_PREFIX = "Bearer ";

private final JwtProvider jwtProvider;
private final JwtUtil jwtUtil;
private final AuthService authService;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

String accessToken = resolveAccessToken(request);
validateAccessToken(accessToken);
String accessToken = jwtUtil.resolveAccessToken(request);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

jwtUtil.validateAccessToken(accessToken);

String email = jwtProvider.getPrincipal(accessToken);
validatePayload(email);
jwtUtil.validatePayload(email);

long userId = authService.getUserIdByEmail(email);
request.setAttribute("userId", userId);
return true;
}

private String resolveAccessToken(HttpServletRequest request) {
String token = request.getHeader(HttpHeaders.AUTHORIZATION);
validateToken(token);
return token.substring(JWT_TOKEN_PREFIX.length());
}

private void validateToken(String token) {
if (token == null) {
throw new JwtNoTokenException(TOKEN_NOT_FOUND);
}
if (!token.startsWith(JWT_TOKEN_PREFIX)) {
throw new JwtUnsupportedTokenException(UNSUPPORTED_TOKEN_TYPE);
}
}

private void validateAccessToken(String accessToken) {
if (jwtProvider.isExpiredToken(accessToken)) {
throw new JwtExpiredTokenException(EXPIRED_TOKEN);
}
}

private void validatePayload(String email) {
if (email == null) {
throw new JwtInvalidTokenException(INVALID_TOKEN);
}
}
// private String resolveAccessToken(HttpServletRequest request) {
// String token = request.getHeader(HttpHeaders.AUTHORIZATION);
// validateToken(token);
// return token.substring(JWT_TOKEN_PREFIX.length());
// }
//
// private void validateToken(String token) {
// if (token == null) {
// throw new JwtNoTokenException(TOKEN_NOT_FOUND);
// }
// if (!token.startsWith(JWT_TOKEN_PREFIX)) {
// throw new JwtUnsupportedTokenException(UNSUPPORTED_TOKEN_TYPE);
// }
// }
//
// private void validateAccessToken(String accessToken) {
// if (jwtProvider.isExpiredToken(accessToken)) {
// throw new JwtExpiredTokenException(EXPIRED_TOKEN);
// }
// }
//
// private void validatePayload(String email) {
// if (email == null) {
// throw new JwtInvalidTokenException(INVALID_TOKEN);
// }
// }

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,15 @@ public enum BaseExceptionResponseStatus implements ResponseStatus {
USER_NOT_FOUND(4003, HttpStatus.BAD_REQUEST.value(), "존재하지 않는 회원입니다."),
PASSWORD_NO_MATCH(4004, HttpStatus.BAD_REQUEST.value(), "비밀번호가 일치하지 않습니다."),
INVALID_USER_STATUS(4005, HttpStatus.BAD_REQUEST.value(), "잘못된 회원 status 값입니다."),
EMAIL_NOT_FOUND(4006, HttpStatus.BAD_REQUEST.value(), "존재하지 않는 이메일입니다.");

EMAIL_NOT_FOUND(4006, HttpStatus.BAD_REQUEST.value(), "존재하지 않는 이메일입니다."),
DUPLICATE_SHOP_NAME(4007,HttpStatus.BAD_REQUEST.value(),"이미 존재하는 가게 이름 입니다."),
FOODCATEGORY_NOT_MATCH(4008,HttpStatus.BAD_REQUEST.value(),"없는 카테고리 입니다."),
//WISH_LIST_INVALID(4009,HttpStatus.BAD_REQUEST.value(),"찜 목록 "),
INVALID_SHOP_VALUE(5000, HttpStatus.BAD_REQUEST.value(), "가게 등록 요청에서 잘못된 값이 존재합니다."),
INVALID_USER_ID(4009, HttpStatus.BAD_REQUEST.value(), "유효하지 않은 사용자 Id입니다"),
INVALID_SHOP_ID(4010, HttpStatus.BAD_REQUEST.value(), "유효하지 않는 가게 Id입니다."),
DUPLICATED_USER_ADDRESS(4011, HttpStatus.BAD_REQUEST.value(), "이미 존재하는 주소 입니다"),
INVALID_ADDRESS_INPUT(4010, HttpStatus.BAD_REQUEST.value(), "유효하지 않는 가게 Id입니다."),;
private final int code;
private final int status;
private final String message;
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/kuit3/backend/config/WebConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ public class WebConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtAuthenticationInterceptor)
.order(1)
.addPathPatterns("/auth/test");
//.addPathPatterns("/auth/test")
.addPathPatterns("/auth/user-info")
.addPathPatterns("/users/**")
.excludePathPatterns("/users");
}

@Override
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/kuit3/backend/controller/AuthController.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package kuit3.backend.controller;

import kuit3.backend.common.argument_resolver.JwtAuthrize;
import kuit3.backend.common.argument_resolver.PreAuthorize;
import kuit3.backend.common.exception.UserException;
import kuit3.backend.common.response.BaseResponse;
Expand Down Expand Up @@ -41,5 +42,17 @@ public BaseResponse<LoginResponse> login(@Validated @RequestBody LoginRequest au
public BaseResponse<String> checkAuthorization(@PreAuthorize Long userId) {
return new BaseResponse<>("userId=" + userId);
}
@GetMapping("/test-jwt")
public BaseResponse<String> checkJwtAuthorization(@JwtAuthrize long userId) {
return new BaseResponse<>("userId=" + userId);
}
@GetMapping("/user-info")
public BaseResponse<String> getUserInfo(@RequestAttribute("userId") Long userId) {
// userId를 이용하여 비즈니스 로직 수행
// 예: 사용자 정보를 데이터베이스에서 조회
//authService.findUserById(userId);

return new BaseResponse<>("userId=" + userId);
}

}
77 changes: 77 additions & 0 deletions src/main/java/kuit3/backend/controller/ShopController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package kuit3.backend.controller;


import com.fasterxml.jackson.databind.annotation.JsonValueInstantiator;
import kuit3.backend.common.exception.ShopException;
import kuit3.backend.common.response.BaseResponse;
import kuit3.backend.dto.shop.*;
import kuit3.backend.service.ShopService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.constraints.Range;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;

import static kuit3.backend.common.response.status.BaseExceptionResponseStatus.INVALID_SHOP_VALUE;
import static kuit3.backend.util.BindingResultUtils.getErrorMessages;

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/shop")
public class ShopController {
private final ShopService shopService;

/**
* shop 추가
* */
@PostMapping("")
public BaseResponse<PostShopResponse> signUp(@Validated @RequestBody PostShopRequest postShopRequest, BindingResult bindingResult) {
log.info("[ShopController.addShop]");
if (bindingResult.hasErrors()) {
throw new ShopException(INVALID_SHOP_VALUE, getErrorMessages(bindingResult));
}
return new BaseResponse<>(shopService.addShop(postShopRequest));
}
@GetMapping("/list")
public BaseResponse<List<Shop>> getShops(@RequestParam(required = false) String category, @RequestParam(required = false) String address) {
if (category != null && address != null) {
// 카테고리와 주소로 가게 검색
return new BaseResponse<>(shopService.getShopsByCategoryAndAddress(category, address));

} else if (category != null) {
// 카테고리로 가게 검색
return new BaseResponse<>(shopService.getShopsByCategory(category));
} else if (address != null) {
// 주소로 가게 검색
return new BaseResponse<>(shopService.getShopsByAddress(address));
} else {
// 카테고리와 주소가 주어지지 않은 경우 모든 가게 목록 반환
return new BaseResponse<>( shopService.getAllShops());
}
}
@GetMapping("/list-page")
public BaseResponse<GetShopListResponse> getShopPage(@Validated @RequestBody GetShopListRequest getShopListRequest) {
return new BaseResponse<>(shopService.getShopList(getShopListRequest));
}
@GetMapping("/detail")
public BaseResponse<List<Shop>> getShopDetail(@Validated @RequestParam long shopId, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
throw new ShopException(INVALID_SHOP_VALUE, getErrorMessages(bindingResult));
}
return new BaseResponse<>(shopService.getShopById(shopId));
}

@GetMapping("/food-categories")
public List<FoodCategory> getAllFoodCategories() {
return shopService.getAllFoodCategories();
}

@PostMapping("/food-categories/{foodCategory}")
public BaseResponse<String> postAllFoodCategories(@Range(min=1,max=10) @PathVariable String foodCategory) {
return new BaseResponse<>(shopService.addFoodCategory(foodCategory));
}
}
36 changes: 31 additions & 5 deletions src/main/java/kuit3/backend/controller/UserController.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
package kuit3.backend.controller;

import jakarta.validation.constraints.NotNull;
import kuit3.backend.common.argument_resolver.JwtAuthrize;
import kuit3.backend.common.exception.BadRequestException;
import kuit3.backend.common.exception.UserException;
import kuit3.backend.common.response.BaseErrorResponse;
import kuit3.backend.common.response.BaseResponse;
import kuit3.backend.dto.user.*;
import kuit3.backend.dto.user.address.GetUserAddressResponse;
import kuit3.backend.dto.user.address.PostUserAddressRequest;
import kuit3.backend.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.websocket.AuthenticationException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;

import static kuit3.backend.common.response.status.BaseExceptionResponseStatus.INVALID_USER_STATUS;
import static kuit3.backend.common.response.status.BaseExceptionResponseStatus.INVALID_USER_VALUE;
import static kuit3.backend.common.response.status.BaseExceptionResponseStatus.*;
import static kuit3.backend.util.BindingResultUtils.getErrorMessages;

@Slf4j
Expand All @@ -39,7 +45,7 @@ public BaseResponse<PostUserResponse> signUp(@Validated @RequestBody PostUserReq
* 회원 휴면
*/
@PatchMapping("/{userId}/dormant")
public BaseResponse<Object> modifyUserStatus_dormant(@PathVariable long userId) {
public BaseResponse<Object> modifyUserStatus_dormant(@NotNull @JwtAuthrize long userId) {
userService.modifyUserStatus_dormant(userId);
return new BaseResponse<>(null);
}
Expand All @@ -48,7 +54,7 @@ public BaseResponse<Object> modifyUserStatus_dormant(@PathVariable long userId)
* 회원 탈퇴
*/
@PatchMapping("/{userId}/deleted")
public BaseResponse<Object> modifyUserStatus_deleted(@PathVariable long userId) {
public BaseResponse<Object> modifyUserStatus_deleted(@NotNull @JwtAuthrize long userId) {
userService.modifyUserStatus_deleted(userId);
return new BaseResponse<>(null);
}
Expand All @@ -57,7 +63,7 @@ public BaseResponse<Object> modifyUserStatus_deleted(@PathVariable long userId)
* 닉네임 변경
*/
@PatchMapping("/{userId}/nickname")
public BaseResponse<String> modifyNickname(@PathVariable long userId,
public BaseResponse<String> modifyNickname(@NotNull @JwtAuthrize long userId,
@Validated @RequestBody PatchNicknameRequest patchNicknameRequest, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
throw new UserException(INVALID_USER_VALUE, getErrorMessages(bindingResult));
Expand All @@ -74,9 +80,29 @@ public BaseResponse<List<GetUserResponse>> getUsers(
@RequestParam(required = false, defaultValue = "") String nickname,
@RequestParam(required = false, defaultValue = "") String email,
@RequestParam(required = false, defaultValue = "active") String status) {
log.info("[UserController.getUsers]");
if (!status.equals("active") && !status.equals("dormant") && !status.equals("deleted")) {
throw new UserException(INVALID_USER_STATUS);
}
return new BaseResponse<>(userService.getUsers(nickname, email, status));
}

@PostMapping("/address")
public BaseResponse<Object> addUserAddress
(@NotNull @JwtAuthrize long userId , @Validated @RequestBody PostUserAddressRequest postUserAddressRequest, BindingResult bindingResult){
if (bindingResult.hasErrors()) {
throw new UserException(INVALID_ADDRESS_INPUT, getErrorMessages(bindingResult));
}
postUserAddressRequest.setUserId(userId);
userService.addUserAddress(postUserAddressRequest);
return new BaseResponse<>(null);
}

@GetMapping("/address/{userId}")
public BaseResponse<List<GetUserAddressResponse>> getUserAddress(/*@Validated @NotNull*/ @PathVariable("userId") long AskUserId,@RequestAttribute("userId") Long AuthUserId){
if (AskUserId != AuthUserId) {
throw new BadRequestException(INVALID_USER_STATUS);
}
return new BaseResponse<>(userService.getUserAddress(AskUserId));
}
}
Loading