Skip to content

Commit

Permalink
[#46] [FEATURE] ROLE 별 API 권한 설정
Browse files Browse the repository at this point in the history
  • Loading branch information
JiwonKKang authored Sep 4, 2024
1 parent ac76f29 commit 32be120
Show file tree
Hide file tree
Showing 32 changed files with 196 additions and 104 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package core.startup.mealtoktok.api.dishstore.response;

import core.startup.mealtoktok.common.dto.Money;
import core.startup.mealtoktok.domain.dishstore.Dish;
import core.startup.mealtoktok.domain.dishstore.DishState;
import core.startup.mealtoktok.domain.dishstore.DishWithImage;

public record DishResponse(
Long dishId,
Expand All @@ -12,13 +12,13 @@ public record DishResponse(
int dishQuantity,
DishState dishState) {

public static DishResponse from(DishWithImage dishWithImage) {
public static DishResponse from(Dish dish) {
return new DishResponse(
dishWithImage.dish().getDishId(),
dishWithImage.dish().getDishName(),
dishWithImage.dish().getDishPrice(),
dishWithImage.image().getImageUrl(),
dishWithImage.dish().getDishQuantity(),
dishWithImage.dish().getDishState());
dish.getDishId(),
dish.getDishName(),
dish.getDishPrice(),
dish.getDishImage().getImageUrl(),
dish.getDishQuantity(),
dish.getDishState());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package core.startup.mealtoktok.api.global.exception;

import lombok.RequiredArgsConstructor;

import core.startup.mealtoktok.common.exception.BaseErrorCode;
import core.startup.mealtoktok.common.exception.ErrorReason;

@RequiredArgsConstructor
public enum GlobalErrorCode implements BaseErrorCode {
PERMISSION_DENIED(403, "PERMISSION_DENIED", "해당 API 권한이 없습니다");

private final Integer status;
private final String errorCode;
private final String message;

@Override
public ErrorReason getErrorReason() {
return ErrorReason.of(status, errorCode, message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package core.startup.mealtoktok.api.global.exception;

import core.startup.mealtoktok.common.exception.WebException;

public class NoPermissionException extends WebException {

public static final NoPermissionException EXCEPTION = new NoPermissionException();

private NoPermissionException() {
super(GlobalErrorCode.PERMISSION_DENIED);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package core.startup.mealtoktok.api.global.security;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;

import lombok.extern.slf4j.Slf4j;

import core.startup.mealtoktok.api.global.exception.NoPermissionException;

@Component
@Slf4j
public class CustomAccessDeniedHandler implements AccessDeniedHandler {

private final HandlerExceptionResolver resolver;

public CustomAccessDeniedHandler(
@Qualifier("handlerExceptionResolver") HandlerExceptionResolver resolver) {
this.resolver = resolver;
}

@Override
public void handle(
HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException accessDeniedException) {
resolver.resolveException(request, response, null, NoPermissionException.EXCEPTION);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package core.startup.mealtoktok.api.global.security;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import jakarta.servlet.FilterChain;
Expand All @@ -9,6 +11,8 @@
import jakarta.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
Expand Down Expand Up @@ -76,8 +80,14 @@ private void reIssueToken(UserId userId, HttpServletResponse response) {

private void saveAuthentication(User user) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(user, null, null);
new UsernamePasswordAuthenticationToken(user, null, getAuthorities(user));
SecurityContextHolder.getContext().setAuthentication(authentication);
log.info("{} 유저 인증 성공", user.getUserProfile().getNickname());
}

private List<GrantedAuthority> getAuthorities(User user) {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_" + user.getUserRole().name()));
return authorities;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
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;
Expand All @@ -17,10 +18,12 @@
@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

private final JwtTokenFilter jwtTokenFilter;
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
private final CustomAccessDeniedHandler customAccessDeniedHandler;

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
Expand All @@ -34,20 +37,26 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.authorizeHttpRequests(
authorizationManagerRequestMatcherRegistry ->
authorizationManagerRequestMatcherRegistry
.requestMatchers(PERMIT_SYSTEM_URIS)
.requestMatchers(SYSTEM_URIS)
.permitAll()
.requestMatchers(PERMIT_SERVICE_URIS)
.requestMatchers(SERVICE_URIS)
.permitAll()
.requestMatchers(PERMIT_SWAGGER_URIS)
.requestMatchers(SWAGGER_URIS)
.permitAll()
.requestMatchers(CorsUtils::isPreFlightRequest)
.permitAll()
.requestMatchers("/api/v1/user/my")
.hasRole("ADMIN")
.anyRequest()
.authenticated())
.exceptionHandling(
httpSecurityExceptionHandlingConfigurer ->
httpSecurityExceptionHandlingConfigurer.authenticationEntryPoint(
jwtAuthenticationEntryPoint));
jwtAuthenticationEntryPoint))
.exceptionHandling(
httpSecurityExceptionHandlingConfigurer ->
httpSecurityExceptionHandlingConfigurer.accessDeniedHandler(
customAccessDeniedHandler));

http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package core.startup.mealtoktok.api.global.security;

public class SecurityConstant {
public static final String[] PERMIT_SWAGGER_URIS = {

public static final String[] SWAGGER_URIS = {
/* swagger v2 */
"/v2/api-docs",
"/swagger-resources",
Expand All @@ -15,7 +16,7 @@ public class SecurityConstant {
"/swagger-ui/**"
};

public static final String[] PERMIT_SERVICE_URIS = {
public static final String[] SERVICE_URIS = {
"/",
"/health",
"/api/v1/auth/oauth/can-sign-up",
Expand All @@ -25,7 +26,7 @@ public class SecurityConstant {
"/api/v1/auth/login/oauth2/code/kakao"
};

public static final String[] PERMIT_SYSTEM_URIS = {
public static final String[] SYSTEM_URIS = {
"/error", "/error/**", "/css/**", "/images/**", "/js/**", "/favicon.ico", "/h2-console/**"
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.List;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
Expand Down Expand Up @@ -30,8 +31,14 @@ public class UserApi implements UserApiDocs {

private final UserService userService;

@GetMapping("/{userId}")
public Response<UserResponse> getUser(@PathVariable Long userId) {
return Response.success(UserResponse.from(userService.getUser(userId)));
}

@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/my")
public Response<UserResponse> getUser(@AuthenticationPrincipal User currentUser) {
public Response<UserResponse> getMyInfo(@AuthenticationPrincipal User currentUser) {
return Response.success(UserResponse.from(currentUser));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@
@Tag(name = "회원 API")
public interface UserApiDocs {

@Operation(summary = "유저 정보 조회")
Response<UserResponse> getUser(User currentUser);
@Operation
Response<UserResponse> getUser(Long userId);

@Operation(summary = "내 정보 조회")
Response<UserResponse> getMyInfo(User currentUser);

@Operation(summary = "닉네임 중복 확인")
Response<AvailabilityResponse> checkNicknameDuplicate(String nickname);
Expand Down
1 change: 0 additions & 1 deletion application/app-api/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ springdoc:
server:
shutdown: graceful

tomcat.util.http.parser.HttpParser.requestTargetAllow: '|{}'

jwt:
secret-key: ${JWT_SECRET_KEY}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,35 @@
import lombok.Builder;
import lombok.Getter;

import core.startup.mealtoktok.common.dto.Image;
import core.startup.mealtoktok.common.dto.Money;

@Getter
@Builder
public class Dish {

private Long dishId;
private Long dishStoreId;
private Long dishCategoryId;
private String dishName;
private Money dishPrice;
private DishState dishState;
private int dishQuantity;
private Long dishStoreId;
private Long dishCategoryId;
private DishImage dishImage;
private DishState dishState;
private Image dishImage;

public void reduceQuantity() {
dishQuantity--;
if (dishQuantity == 0) {
dishState = DishState.SOLD_OUT;
}
}

public void increaseQuantity() {
dishQuantity++;
}

public void addDishImage(DishImage dishImage) {
this.dishImage = dishImage;
public void updateDishInfo(String dishName, Money dishPrice) {
this.dishName = dishName;
this.dishPrice = dishPrice;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class DishRemover {

public void remove(Dish dish) {
dishRepository.deleteDish(dish);
Image image = imageReader.read(TargetImage.from(dish.getDishImage().imageId()));
Image image = imageReader.read(TargetImage.from(dish.getDishImage().getId()));
imageRemover.remove(image);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ public class DishService {
private final DishUpdater dishUpdater;
private final DishStoreReader dishStoreReader;
private final DishCategoryReader dishCategoryReader;
private final DishWithImageFinder dishWithImageFinder;

@Transactional
public void createDish(
Expand All @@ -45,11 +44,12 @@ public void updateDish(TargetDish targetDish, File uploadImage, DishContent dish
dishUpdater.update(dishStore, dish, uploadImage, dishContent);
}

public List<DishWithImage> readDishes(TargetDishCategory targetDishCategory) {
return dishWithImageFinder.find(targetDishCategory);
public List<Dish> readDishes(TargetDishCategory targetDishCategory) {
DishCategory dishCategory = dishCategoryReader.read(targetDishCategory);
return dishReader.readAll(dishCategory);
}

public List<DishWithImage> searchDishes(String keyword) {
return dishWithImageFinder.find(keyword);
public List<Dish> searchDishes(String keyword) {
return dishReader.search(keyword);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class DishUpdater {
public void update(DishStore dishStore, Dish dish, File uploadImage, DishContent dishContent) {
dishValidator.validateName(dishStore, dish, dishContent.dishName());
Image image = fileUploader.upload(uploadImage);
Image existingImage = imageReader.read(TargetImage.from(dish.getDishImage().imageId()));
Image existingImage = imageReader.read(TargetImage.from(dish.getDishImage().getId()));
Image updatedImage = imageUpdater.update(existingImage, image);
dishRepository.updateDish(dish, dishContent, updatedImage);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public List<DishWithImage> find(String keyword) {
}

private DishWithImage wrap(Dish dish) {
Image image = imageReader.read(TargetImage.from(dish.getDishImage().imageId()));
Image image = imageReader.read(TargetImage.from(dish.getDishImage().getId()));
return DishWithImage.of(dish, image);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import lombok.Getter;
import lombok.NoArgsConstructor;

import core.startup.mealtoktok.domain.mealdelivery.MealDelivery;
import core.startup.mealtoktok.domain.mealdelivery.MealDeliveryId;
import core.startup.mealtoktok.domain.order.FullDiningInfo;

@Getter
@Builder
Expand All @@ -22,12 +22,9 @@ public class FullDining {
private CollectingState collectState;
private LocalDateTime collectedDateTime;

public static FullDining create(FullDiningInfo fullDiningInfo) {
public static FullDining create(MealDelivery mealDelivery) {
return new FullDining(
null,
MealDeliveryId.from(fullDiningInfo.mealDeliveryId()),
CollectingState.NOT_COLLECTED,
null);
null, mealDelivery.getMealDeliveryId(), CollectingState.NOT_COLLECTED, null);
}

public void collectRequest() {
Expand Down
Loading

0 comments on commit 32be120

Please sign in to comment.