ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Boot AOP를 이용해 로그인 검증하기
    JAVA 2024. 7. 29. 23:18

    스프링 부트로 개인 프로젝트를 하며 사용자가 로그인을 했는지 권한을 확인할 필요가 있어졌다. 

    이에 개발하려는 기능 정의를 다음과 같이 했다.

    1. AOP를 통해 검증한다
    2. LoginCheck 라는 Annotation을 만들어서 이 어노테이션을 기반으로 AOP를 적용한다
    3. LoginCheck Annotation은 메소드에도 붙을 수 있고 클래스에도 붙을 수 있다.
    4. LoginCheck Annotation은 값을 가질 수 있다. 기본값으로 True를 갖는다. False인 경우 검증하지 않는다. 

    먼저 어노테이션으로 정의한 이유는 개발을 하며 명시적으로 권한이 필요하다는것을 표시해주고 싶었기 때문이다. 

    Controller에 LoginCheck 이라는 어노테이션이 있다면 다른 개발자가 보더라도 권한이 필요한 영역이라 쉽게 유추할것이다.

     

    LoginCheck는 메소드 단위로도 붙지만, 클래스에도 붙일 수 있다. 만약 특정 Controller의 요청들이 전부 권한이 필요한 요청들일 경우 이 요청의 메소드들에 모두 LoginCheck를 붙이기보다는 Controller Class 자체에 LoginCheck를 붙이기만 하면 된다.

     

    어노테이션에 값을 갖게 한 이유는 만약 Controller Class에 LoginCheck 어노테이션을 붙여서 사용하는데, 이 Controller의 특정 요청에서는 권한 검증을 필요하지 않을 경우, LoginCheck(false)로 '여기서는 권한 체크를 하지 않겠다'를 명시적으로 줄 수 있을것으로 기대하기 때문이다.

     

    Annotation 정의

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(value = RetentionPolicy.RUNTIME)
    public @interface LoginCheck {
        boolean value() default true;
    }

     

    LoginCheck라는 어노테이션을 정의했다. Target은 METHOD와 TYPE으로 선언하여 클래스, 메소드 모두 적용 가능하도록 했다.

    Retention은 RUNTIME으로 하여 런타임시에 적용되도록 했다. default로 true 값을 가지는 boolean value도 정의해주었다. 

    Aspect 정의

    @Aspect
    @Component
    @Slf4j
    public class LoginCheckAspect {
    
        @Around("within(@com.cal.calB.common.aop.loginCheck.LoginCheck *) || @annotation(com.cal.calB.common.aop.loginCheck.LoginCheck)")
        public Object loginCheck(ProceedingJoinPoint joinPoint) throws Throwable {
            log.info("loginCheck");
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            LoginCheck classAnnotation = joinPoint.getTarget().getClass().getAnnotation(LoginCheck.class);
            LoginCheck methodAnnotation = method.getAnnotation(LoginCheck.class);
    
            if ((classAnnotation != null && !classAnnotation.value()) || (methodAnnotation != null && !methodAnnotation.value())) {
                log.info("loginCheck false 라서 그냥 통과");
                return joinPoint.proceed();
            }
    
            log.info("실제 세션 처리 부분");
    
            return joinPoint.proceed();
        }
    }

     

    LoginCheck를 이용해서 확인하는 AOP를 적용하는 부분이다. LoginCheck가 붙은 class 또는 method를 검증한다.

    classAnnotation, methodAnnotation의 value를 가져와서 false인 경우 세션 처리를 하지 않는다. 

    사용하는 Controller들

    @RestController
    @RequestMapping("api/v1/test2")
    public class testController2 {
    
        @LoginCheck
        @GetMapping("/testMethodLoginCheck")
        public String testMethodLoginCheck() {
            return "testMethodLoginCheck";
        }
    
        @LoginCheck(false)
        @GetMapping("/testMethodLoginCheckFalse")
        public String testMethodLoginCheckFalse() {
            return "testMethodLoginCheckFalse";
        }
    
        @GetMapping("/testNoLoginCheck")
        public String testNoLoginCheck() {
            return "testNoLoginCheck";
        }
    }

    먼저 메소드에서 사용하는 컨트롤러를 만들어보았다. 

    1. /testNoLoginCheck를 요청할 경우 aspect에 걸리지 않기에 어떠한 로그도 찍히지 않는다.
    2. /testMethodLoginCheck 를 요청할 경우. "loginCheck", "실제 세션 처리 부분" 이라는 로그가 찍힌다.
    3. /testMethodLoginCheckFalse를 요청할 경우. "loginCheck", "loginCheck false 라서 그냥 통과" 를 출력한다.
    @LoginCheck
    @RestController
    @RequestMapping("api/v1/test")
    public class testController {
    
        @GetMapping("/testClassLoginCheck")
        public String testClassLoginCheck() {
            return "testClassLoginCheck";
        }
    
        @LoginCheck(false)
        @GetMapping("/testClassLoginCheckFalse")
        public String testClassLoginCheckFalse() {
            return "testClassLoginCheckFalse";
        }
    }

    다음은 class에 붙은 경우이다. 이 Controller로의 요청은 기본적으로 모두 LoginCheck를 수행한다.

    1. /testClassLoginCheck 를 요청할 경우. "loginCheck", "실제 세션 처리 부분" 이라는 로그가 찍힌다.
    2. /testClassLoginCheckFalse를 요청할 경우. "loginCheck", "loginCheck false 라서 그냥 통과" 를 출력한다.

    결론

    이렇게 어노테이션을 기반으로 로그인 처리 기능을 구현해보았다. Pointcut을 이용하여 이런 LoginCheck 기능을 구현하는 방법은 많겠지만 어노테이션을 직접 명시함으로써 개발자가 권한이 필요하다는것을 명시적으로 알 수 있는점이 장점으로 보인다.

    'JAVA' 카테고리의 다른 글

    Spring Boot MessageConverter  (0) 2024.06.24
    Java mutable 과 immutable  (1) 2023.10.08
    Java와 일급 함수  (0) 2023.09.18
    Java Generic Compile 동작  (0) 2023.09.11
    JVM Memory Area  (0) 2023.09.08
Designed by Tistory.