이번 포스팅에서는 회원 권한에 따른 특정 핸들러에 접근 제한
을 설정해 보자.
본 프로젝트는 회원 가입 시, 임의의 토큰을 포함하는 URL이 전송되며 전송된 URL을 클릭하면 인증이 완료된다.
무분별한 회원가입과 유령회원을 방지하기 위해 회원 권한을 총 3개의 UserLevel
로 분리했다.
UNAUTH
(판매/구매
와 관련된 기능 제한)AUTH
(유저 서비스)ADMIN
(관리자 기능)[ReSeller Project] 반복되는 부가기능을 공통적으로 처리해 보자에서 구현한 LoginCheck 어노테이션
은 아래와 같다.
@Retention(RUNTIME)
@Target(METHOD)
public @interface LoginCheck {
UserLevel authority() default UserLevel.UNAUTH;
}
default 값이 UNAUTH
로 설정되어 있다.
우선 아래와 같이 enum 클래스
를 먼저 구현해 주자.
public enum UserLevel {
UNAUTH, AUTH, ADMIN
}
그 후, [ReSeller Project] 반복되는 부가기능을 공통적으로 처리해 보자에서 작성한 LoginCheckInterceptor
에 아래와 같은 코드를 추가하자.
@Component
@RequiredArgsConstructor
public class LoginCheckInterceptor implements HandlerInterceptor {
...
@Inject
private Environment environment;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
LoginCheck loginCheck = handlerMethod.getMethodAnnotation(LoginCheck.class);
...
UserLevel auth = loginCheck.authority();
// 계층 검사
switch (auth) {
case ADMIN:
adminUserLevel();
break;
case AUTH:
authUserLevel();
break;
}
}
...
}
/**
* 현재 USER의 권한(UserLevel)이 AUTH 인지 체크
* 해당 리소스는 ADMIN과 AUTH는 접근 가능 -> UNAUTH 경우 제한
*/
private void authUserLevel() {
UserLevel auth = sessionLoginService.getUserLevel();
if (auth == UserLevel.UNAUTH) {
throw new NotAuthorizedException("해당 리소스에 대한 접근 권한이 존재하지 않습니다.");
}
}
/**
* 현재 USER의 권한(UserLevel)이 ADMIN 인지 체크
* ADMIN 외 다른 권한은 해당 요청 제한
*/
private void adminUserLevel() {
UserLevel auth = sessionLoginService.getUserLevel();
if (auth != UserLevel.ADMIN) {
throw new NotAuthorizedException("해당 리소스에 대한 접근 권한이 존재하지 않습니다.");
}
}
}
여기서 주의할 점은 권한 검사 외에도 계층 처리
를 하는 코드를 추가로 구현해야 한다.
@LoginCheck
어노테이션의 경우, 권한이 default 값으로 UNAUTH로 설정되기 때문에 @LoginCheck(authority = UserLevel.AUTH)
의 경우 계층 처리가 필요하다.
@LoginCheck(authority = UserLevel.AUTH)
가 적용된 핸들러는 UNAUTH만 접근이 불가능하고 ADMIN과 AUTH는 접근이 가능해야 한다. 따라서 switch 문을 통해 계층 검사를 진행하는 코드를 추가했다.
@LoginCheck(authority = UserLevel.ADMIN)
@ResponseStatus(HttpStatus.CREATED)
@PostMapping
public void createBrand(@Valid @RequestPart SaveRequest requestDto, @RequestPart(required = false) MultipartFile brandImage) {
brandService.saveBrand(requestDto, brandImage);
}