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주차 미션 / 서버 3조 정소은 #2

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
Expand Up @@ -7,5 +7,5 @@

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreAuthorize {
public @interface JWTAuthorize {
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package kuit3.backend.common.argument_resolver;

import jakarta.servlet.http.HttpServletRequest;
import kuit3.backend.common.interceptor.JwtAuthInterceptor;
import kuit3.backend.service.AuthService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.security.access.prepost.PreAuthorize;
Expand All @@ -13,9 +15,18 @@
@Slf4j
@Component
public class JwtAuthHandlerArgumentResolver implements HandlerMethodArgumentResolver {

private final JwtAuthInterceptor jwtAuthInterceptor;
private final AuthService authService;

public JwtAuthHandlerArgumentResolver(JwtAuthInterceptor jwtAuthInterceptor, AuthService authService) {
this.jwtAuthInterceptor = jwtAuthInterceptor;
this.authService = authService;
}

@Override
public boolean supportsParameter(MethodParameter parameter) {
boolean hasAnnotation = parameter.hasParameterAnnotation(PreAuthorize.class);
boolean hasAnnotation = parameter.hasParameterAnnotation(JWTAuthorize.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 +36,15 @@ 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 = jwtAuthInterceptor.resolveAccessToken(request);
jwtAuthInterceptor.validateAccessToken(accessToken);
Copy link
Contributor

Choose a reason for hiding this comment

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

토큰에 대한 검증은 이미 Interceptor에서 끝났기 때문에 ArgumentResolver에는 요청 헤더 -> 토큰 -> payload 추출 로직만 있어도 됩니다!

String email = jwtAuthInterceptor.getEmail(accessToken);
jwtAuthInterceptor.validatePayload(email);
long userId = authService.getUserIdByEmail(email);
Copy link
Contributor

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,19 @@
package kuit3.backend.common.exception;

import kuit3.backend.common.response.status.ResponseStatus;
import lombok.Getter;
@Getter
public class RestaurantException extends RuntimeException{

private final ResponseStatus exceptionStatus;

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

public RestaurantException(ResponseStatus exceptionStatus, String message) {
super(message);
this.exceptionStatus = exceptionStatus;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package kuit3.backend.common.exception_handler;

import jakarta.validation.ConstraintViolationException;
import kuit3.backend.common.exception.BadRequestException;
import kuit3.backend.common.exception.InternalServerErrorException;
import kuit3.backend.common.response.BaseErrorResponse;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpStatus;
Expand Down Expand Up @@ -63,4 +63,4 @@ public BaseErrorResponse handle_RuntimeException(Exception e) {
return new BaseErrorResponse(SERVER_ERROR);
}

}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package kuit3.backend.common.exception_handler;

import jakarta.annotation.Priority;
import kuit3.backend.common.response.BaseErrorResponse;
import jakarta.annotation.Priority;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
Expand Down Expand Up @@ -31,4 +31,5 @@ public BaseErrorResponse handle_DataAccessException(DataAccessException e) {
log.error("[handle_DataAccessException]", e);
return new BaseErrorResponse(DATABASE_ERROR);
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package kuit3.backend.common.exception_handler;

import jakarta.annotation.Priority;
import kuit3.backend.common.exception.jwt.bad_request.JwtBadRequestException;
import kuit3.backend.common.exception.jwt.unauthorized.JwtUnauthorizedTokenException;
import kuit3.backend.common.response.BaseErrorResponse;
import jakarta.annotation.Priority;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
Expand All @@ -28,4 +28,5 @@ public BaseErrorResponse handle_JwtUnauthorizedException(JwtUnauthorizedTokenExc
log.error("[handle_JwtUnauthorizedException]", e);
return new BaseErrorResponse(e.getExceptionStatus());
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package kuit3.backend.common.exception_handler;

import jakarta.annotation.Priority;
import kuit3.backend.common.exception.RestaurantException;
import kuit3.backend.common.exception.UserException;
import kuit3.backend.common.response.BaseErrorResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import static kuit3.backend.common.response.status.BaseExceptionResponseStatus.INVALID_RESTAURANT_VALUE;

@Slf4j
@Priority(0)
@RestControllerAdvice
public class RestaurantExceptionContorollerAdvice {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(RestaurantException.class)
public BaseErrorResponse handle_RestaurantException(RestaurantException e) {
log.error("[handle_RestaurantException]", e);
return new BaseErrorResponse(e.getExceptionStatus(), e.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package kuit3.backend.common.exception_handler;

import jakarta.annotation.Priority;
import kuit3.backend.common.exception.UserException;
import kuit3.backend.common.response.BaseErrorResponse;
import jakarta.annotation.Priority;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
Expand All @@ -20,6 +20,7 @@ public class UserExceptionControllerAdvice {
@ExceptionHandler(UserException.class)
public BaseErrorResponse handle_UserException(UserException e) {
log.error("[handle_UserException]", e);
return new BaseErrorResponse(INVALID_USER_VALUE, e.getMessage());
return new BaseErrorResponse(e.getExceptionStatus(), e.getMessage());
}
}

}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package kuit3.backend.common.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import kuit3.backend.common.exception.jwt.unauthorized.JwtExpiredTokenException;
import kuit3.backend.common.exception.jwt.unauthorized.JwtInvalidTokenException;
import kuit3.backend.common.exception.jwt.bad_request.JwtNoTokenException;
import kuit3.backend.common.exception.jwt.bad_request.JwtUnsupportedTokenException;
import kuit3.backend.common.exception.jwt.unauthorized.JwtExpiredTokenException;
import kuit3.backend.common.exception.jwt.unauthorized.JwtInvalidTokenException;
import kuit3.backend.jwt.JwtProvider;
import kuit3.backend.service.AuthService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
Expand All @@ -24,29 +23,28 @@ public class JwtAuthInterceptor implements HandlerInterceptor {
private static final String JWT_TOKEN_PREFIX = "Bearer ";

private final JwtProvider jwtProvider;
private final AuthService authService;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
log.info("[JwtAuthInterceptor.preHandle]");

String accessToken = resolveAccessToken(request);
validateAccessToken(accessToken);

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

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

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

private void validateToken(String token) {
public void validateToken(String token) {
if (token == null) {
throw new JwtNoTokenException(TOKEN_NOT_FOUND);
}
Expand All @@ -55,16 +53,19 @@ private void validateToken(String token) {
}
}

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

private void validatePayload(String email) {
public void validatePayload(String email) {
if (email == null) {
throw new JwtInvalidTokenException(INVALID_TOKEN);
}
}

}
public String getEmail(String accessToken) {
return jwtProvider.getPrincipal(accessToken);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package kuit3.backend.common.response;

import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import kuit3.backend.common.response.status.ResponseStatus;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import lombok.Getter;

import java.time.LocalDateTime;

// 실패 응답 객체
@Getter
@JsonPropertyOrder({"code", "status", "message", "timestamp"})
public class BaseErrorResponse implements ResponseStatus {
Expand Down Expand Up @@ -44,4 +44,4 @@ public String getMessage() {
return message;
}

}
}
6 changes: 3 additions & 3 deletions src/main/java/kuit3/backend/common/response/BaseResponse.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package kuit3.backend.common.response;

import kuit3.backend.common.response.status.ResponseStatus;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import kuit3.backend.common.response.status.ResponseStatus;
import lombok.Getter;

import static kuit3.backend.common.response.status.BaseExceptionResponseStatus.SUCCESS;

// 성공 응답 객체
@Getter
@JsonPropertyOrder({"code", "status", "message", "result"})
public class BaseResponse<T> implements ResponseStatus {
Expand Down Expand Up @@ -40,4 +40,4 @@ public String getMessage() {
return message;
}

}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package kuit3.backend.common.response.status;

import lombok.RequiredArgsConstructor;
import org.hibernate.validator.internal.constraintvalidators.hv.ru.INNValidator;
import org.springframework.http.HttpStatus;

@RequiredArgsConstructor
Expand Down Expand Up @@ -42,10 +43,18 @@ public enum BaseExceptionResponseStatus implements ResponseStatus {
INVALID_USER_VALUE(5000, HttpStatus.BAD_REQUEST.value(), "회원가입 요청에서 잘못된 값이 존재합니다."),
DUPLICATE_EMAIL(5001, HttpStatus.BAD_REQUEST.value(), "이미 존재하는 이메일입니다."),
DUPLICATE_NICKNAME(5002, HttpStatus.BAD_REQUEST.value(), "이미 존재하는 닉네임입니다."),
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(), "존재하지 않는 이메일입니다.");
USER_NOT_FOUND(5003, HttpStatus.BAD_REQUEST.value(), "존재하지 않는 회원입니다."),
PASSWORD_NO_MATCH(5004, HttpStatus.BAD_REQUEST.value(), "비밀번호가 일치하지 않습니다."),
INVALID_USER_STATUS(5005, HttpStatus.BAD_REQUEST.value(), "잘못된 회원 status 값입니다."),
EMAIL_NOT_FOUND(5006, HttpStatus.BAD_REQUEST.value(), "존재하지 않는 이메일입니다."),

/**
* 6000: Restaurant 오류
*/
INVALID_RESTAURANT_VALUE(6000,HttpStatus.BAD_REQUEST.value(),"식당등록 요청에서 잘못된 값이 존재합니다."),
DUPLICATE_PHONE(6001,HttpStatus.BAD_REQUEST.value(),"이미 존재하는 전화번호입니다."),
INVALID_RESTAURANT_STATUS(6002,HttpStatus.BAD_REQUEST.value(),"잘못된 식당 status입니다."),
RESTAURANT_NOT_FOUND(6003,HttpStatus.BAD_REQUEST.value(),"존재하지 않는 식당입니다.");

private final int code;
private final int status;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ public interface ResponseStatus {

String getMessage();

}
}
17 changes: 5 additions & 12 deletions src/main/java/kuit3/backend/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package kuit3.backend.config;


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.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
Expand All @@ -23,15 +20,11 @@ public PasswordEncoder passwordEncoder() {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

http
.csrf(AbstractHttpConfigurer::disable)

.formLogin(FormLoginConfigurer::disable)

.sessionManagement((sessionManagement) ->
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
);
.csrf().disable()
Copy link
Contributor

Choose a reason for hiding this comment

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

'csrf()' is deprecated since version 6.1 and marked for removal
해당 방식이 6.1 버전부터 deprecated 되어 이전 코드 대로 작성해주시는게 좋을 것 같아요 :)

.formLogin().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
}

}
3 changes: 2 additions & 1 deletion src/main/java/kuit3/backend/config/WebConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ public void addInterceptors(InterceptorRegistry registry) {
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(jwtAuthHandlerArgumentResolver);
}
}

}
Loading