레이블이 annotation인 게시물을 표시합니다. 모든 게시물 표시
레이블이 annotation인 게시물을 표시합니다. 모든 게시물 표시

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