๐ ๊ฐ์
์ด๋ฒ์ ์งํํ๋ ํ๋ก์ ํธ์ ๊ฒฝ์ฐ,
๋ง์ API์์ ์์ฒญ์ผ๋ก ๋ค์ด์จ travel id
์ ๋ํ ์ ๋ณด๊ฐ ์กด์ฌํ๋์ง, ํด๋น travel id
์ ๋ํ ์ ๊ทผ ๊ถํ์ด ์๋์ง์ ๋ํ ์์ธ ์ฒ๋ฆฌ๊ฐ ํ์ํ๋ค.
์์ธ ์ฒ๋ฆฌ์ ๋ํ ์ฝ๋ ์ค๋ณต์ ์ค์ด๊ธฐ ์ํด ์ ๋ ธํ ์ด์ ์ ๋ง๋ค์ด ์ฒ๋ฆฌํ๋ ค๊ณ ํ๋ค.
๐ Custom Annotation ์์ฑ
๋จผ์ , ๊ถํ ๊ฒ์ฌ๋ฅผ ์ ์ฉํ ๋ฉ์๋์ ์ฌ์ฉํ ์ปค์คํ ์ ๋ ธํ ์ด์ ์ ๋ง๋ ๋ค.
package withbeetravel.security;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Travel์ ๋ํ ๊ถํ ๊ฒ์ฌ๋ฅผ ์ ์ฉํ ๋ฉ์๋์ ์ฌ์ฉ
*/
@Target(ElementType.METHOD) // ๋ฉ์๋๋จ์์ ์ฌ์ฉ
@Retention(RetentionPolicy.RUNTIME) // ๋ฐํ์ ์์ ์ ์ ์ง
public @interface CheckTravelAccess {
// ํด๋น ์ ๋
ธํ
์ด์
์ ์ฌ์ฉํ ๋ฉ์๋์์ ๊ฐ์ ธ์ฌ ํ๋ผ๋ฏธํฐ ์ด๋ฆ
String travelIdParam() default "travelId";
}
๐ AOP Aspect ํด๋์ค ์์ฑ
AOP๋ฅผ ํตํด ๋ฉ์๋ ์คํ ์ ์ ๊ถํ์ ๊ฒ์ฆํ๋ Aspect
ํด๋์ค๋ฅผ ์์ฑํ๋ค.
package withbeetravel.security;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import withbeetravel.domain.Travel;
import withbeetravel.exception.CustomException;
import withbeetravel.exception.error.TravelErrorCode;
import withbeetravel.repository.TravelMemberRepository;
import withbeetravel.repository.TravelRepository;
/**
* ๋ฉ์๋ ์คํ ์ Travel์ ๋ํ ๊ถํ ๊ฒ์ฆ
*/
@Aspect
@Component
@RequiredArgsConstructor
public class TravelAccessAspect {
private final TravelRepository travelRepository;
private final TravelMemberRepository travelMemberRepository;
@Before("@annotation(checkTravelAccess)") // @CheckTravelAccess๊ฐ ๋ถ์ ๋ฉ์๋ ์คํ ์ ์ ์คํ
public void checkAccess(JoinPoint joinPoint, CheckTravelAccess checkTravelAccess) {
// ๋ก๊ทธ์ธ๋ ํ์ id
Long userId = 3L;
// @CheckTravelAccess๋ฅผ ๋ถ์ธ ๋ฉ์๋์ ํ๋ผ๋ฏธํฐ์์ travelId ์ถ์ถ
Long travelId = getTravelIdFromArgs(joinPoint, checkTravelAccess.travelIdParam());
// travelId์ ํด๋นํ๋ travel์ด ์๋์ง ํ์ธ(์๋ค๋ฉด ์์ธ ๋์ง๊ธฐ)
travelRepository.findById(travelId)
.orElseThrow(() -> new CustomException(TravelErrorCode.TRAVEL_NOT_FOUND));
// ๊ถํ ๊ฒ์ฌ
travelMemberRepository.findByTravel_IdAndUser_Id(travelId, userId)
.orElseThrow(() -> new CustomException(TravelErrorCode.TRAVEL_ACCESS_FORBIDDEN));
}
private Long getTravelIdFromArgs(JoinPoint joinPoint, String travelIdParam) {
// ๋ฉ์๋ ํ๋ผ๋ฏธํฐ์ ์ด๋ฆ์ ๋งคํํ์ฌ travelId๋ฅผ ์ฐพ์ ๋ฐํ
var paramNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
var args = joinPoint.getArgs();
for (int i = 0; i < paramNames.length; i++) {
if(paramNames[i].equals(travelIdParam) && args[i] instanceof Long) {
return (Long) args[i];
}
}
// @CheckTravelAccess๋ฅผ ๋ถ์ธ ๋ฉ์๋์์ "travelId"๋ผ๋ ํ๋ผ๋ฏธํฐ๋ฅผ ์ฐพ์ง ๋ชปํ ๊ฒฝ์ฐ
throw new IllegalArgumentException("travelId ํ๋ผ๋ฏธํฐ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.");
}
}
์์ง, ์ธ์ฆ/์ธ๊ฐ ๊ธฐ๋ฅ์ ๊ตฌํํ์ง ์์์
30๋ฒ ๋ผ์ธ์ userId
๋ฅผ ๋๋ฏธ ๋ฐ์ดํฐ๋ก ๋ฃ์ด๋จ๋ค.
์ธ์ฆ/์ธ๊ฐ ๊ตฌํ์ด ์๋ฃ๋์ด ์๋ ์ํ๋ผ๋ฉด,
userId
์ ๋ก๊ทธ์ธ๋ ์ฌ์ฉ์์ userId
๋ก ๋ฃ์ด์ฃผ๋ฉด ๋๋ค.
๐ Annotation ์ ์ฉ
์ด์ ๊ถํ ๊ฒ์ฌ๊ฐ ํ์ํ ๋ฉ์๋์ @CheckTravelAccess
์ ๋
ธํ
์ด์
์ ์ถ๊ฐํ์ฌ ๊ฒ์ฌ๋ฅผ ์ํํ๋๋ก ์ค์ ํ๋ค.
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/travels/{travelId}/payments")
public class SharedPaymentController implements SharedPaymentControllerDocs {
@Override
@CheckTravelAccess
@PatchMapping(value = "/{sharedPaymentId}/records", consumes = "multipart/form-data")
public ResponseEntity<String> addAndUpdatePaymentRecord(
@PathVariable Long travelId,
@PathVariable Long sharedPaymentId,
@RequestPart(value = "paymentImage") MultipartFile paymentImage,
@RequestParam(value = "paymentComment", required = false) String paymentComment,
@RequestParam(value = "isMainImage", defaultValue = "false") boolean isMainImage
) {
...
}
}
@CheckTravelAccess
๋ฅผ ์ฌ์ฉํ ๋ฉ์๋์์ travel id๋ฅผ ๊ฐ์ง๊ณ ์๋ ํ๋ผ๋ฏธํฐ๋ช
์ด travelId
๊ฐ ์๋๋ผ ๋ค๋ฅธ ์ด๋ฆ์ด๋ผ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์์ฑํด์ฃผ๋ฉด ๋๋ค.
public class SharedPaymentController implements SharedPaymentControllerDocs {
...
@CheckTravelAccess(travelIdParam = "id") // id๋ผ๋ ํ๋ผ๋ฏธํฐ๋ฅผ travelIdParam์ผ๋ก ์ฌ์ฉํ๊ฒ ๋ค.
public ResponseEntity<String> addAndUpdatePaymentRecord(
@PathVariable Long id, // travel id๋ก ์ฌ์ฉํ ํ๋ผ๋ฏธํฐ ๋ช
์ด id์
...
) {
...
}
}
'Back-end > Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring] Swagger ์ค์ ๋ฐ ์ฌ์ฉ ๋ฐฉ๋ฒ (4) | 2024.11.11 |
---|---|
[Spring] Custome Exception ๋ง๋ค๊ธฐ (0) | 2024.11.07 |