2016년 9월 30일 금요일

annotation을 사용한 controller aop 설정하기

annotation을 이용하면 전역 처리가 편리하다.

아래와 같이 어노테이션을 선언했다.


@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasRole('ROLE_USER')")
public @interface PreAccountAuthorize {
 boolean checkBanInfo() default false; //제재 여부 체크
}

해당 타겟이 annotation type이며 실행 시 annotation이 사라지지 않기 위해 RetentionPolicy는 runtime 으로 지정되어 있다.

해당 어노테이션을 사용하는 부분이 class와 method 라는 선언을 하였다.

기존 인증 체크에 추가적인 조건 체크를 더하기 위해 기존 인증 체크를 추가하였다.
@PreAuthorize("hasRole('ROLE_USER')")

사실 @PreAuthorize를 선언하면 해당 어노테이션에 위 선언들이 있기 때문에 아래와 같이 써도 무방하다.
@PreAuthorize("hasRole('ROLE_USER')")
public @interface PreAccountAuthorize {
   boolean checkBanInfo() default false; //제재 여부 체크
}
@Slf4j
@Aspect
@Component
public class PreAccountAuthorizeAspect {
 
   @Setter private BanInfoService banInfoService;

   @Before("@annotation(preAccountAuthorize)")
   public void methodBefore(JoinPoint joinPoint, PreAccountAuthorize preAccountAuthorize) {
      checkBefore(joinPoint, preAccountAuthorize);
    }
 
    @Before("@target(preAccountAuthorize) && bean(*Controller)")
    public void classBefore(JoinPoint joinPoint, PreAccountAuthorize preAccountAuthorize) {
        checkBefore(joinPoint, preAccountAuthorize);
    }
 
    public void checkBefore(JoinPoint joinPoint, PreAccountAuthorize preAccountAuthorize) {
        if (banInfoService == null) {
            throw new BlueskyException("banInfoService is not set");
        }
        checkBanInfo(preAccountAuthorize);
    }
 
    private void checkBanInfo(PreAccountAuthorize preAccountAuthorize) {
        log.debug("checkBanInfo");
        if (!preAccountAuthorize.checkBanInfo()) {
            return;
        }
        List banInfo = banInfoService.checkBanInfo();
        log.debug("checkBanInfo banInfo : {}", banInfo);
        if (!banInfo.isEmpty()) {
            throw new BlueskyException("security.BANNED_ACCOUNT");
        }
    }
}

@target 을 이용한 class annotation은 전체 bean을 전부 훝어서 해당 어노테이션을 체크하는 듯 하다.
이 경우 spring security 를 사용하면 GlobalMethodSecurityConfiguration 관련해서 에러가 발생한다.

aop 관련 동작을 전체 bean 대상으로 체크하다 security쪽 bean의 non-visible class를 대상으로 cglib 동작이 수행되면서 에러가 발생하는데 이를 막기 위해 controller로 제한하였다.
관련한 에러는 다음과 같다.

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration': Unsatisfied dependency expressed through method 'setObjectPostProcessor' parameter 0: Error creating bean with name 'objectPostProcessor' defined in class path resource [org/springframework/security/config/annotation/configuration/ObjectPostProcessorConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'objectPostProcessor' defined in class path resource [org/springframework/security/config/annotation/configuration/ObjectPostProcessorConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor

댓글 없음:

댓글 쓰기