스프링 시큐리티/실전프로젝트 - 인가 프로세스 DB 연동 서비스 계층 구현

ch08. ProxyFactory 를 활용한 실시간 메소드 보안 구현

webmaster 2022. 1. 27. 12:44
728x90

개선점

  • 메서드 보안은 스프링 시큐리티 초기화 시점에 보안 적용 대상 빈의 프록시 생성 및  어드바이스 적용이 이루어짐
  • DB에 자원을 실시간으로 업데이트하더라도AOP 가 바로 적용되지 않음
    • 초기화 과정에 프락시 객체가 생기고 advice를 생성하기 때문에

보안 메서드 실시간 적용 처리 과정

  1. 메소드 보안 최초 설정 시 대상 빈의 프록시 객체 생성하고 메소드에 Advice 등록하여 AOP 적용
  2. MapBasedMethodSecurityMetadataSource에 자원 및 권한 정보 전달
  3. DefaultSingletonBeanRegistry로실제 빈을 삭제하고 프록시 객체를 빈 참조로 등록한다
  4. 보안이 적용된 메서드 호출 시 Advice 작동한다
  5. 메소드 보안 해제 시 메서드에등록된 Advice를 제거한다
  6. 메서드 보안 재 설정 시 메서드에등록된 Advice를Advice를 다시 등록한다

동작 과정

  • Interceptor 빈 추가
  • CustomMethodSecurityInterceptor
    • public class CustomMethodSecurityInterceptor extends AbstractSecurityInterceptor implements
              MethodInterceptor {
          private MethodSecurityMetadataSource securityMetadataSource;
      
          public Class<?> getSecureObjectClass() {
              return MethodInvocation.class;
          }
      
          public Object invoke(MethodInvocation mi) throws Throwable {
              InterceptorStatusToken token = super.beforeInvocation(mi);
      
              Object result;
              try {
                  result = mi.proceed();
              }
              finally {
                  super.finallyInvocation(token);
              }
              return super.afterInvocation(token, result);
          }
      
          public MethodSecurityMetadataSource getSecurityMetadataSource() {
              return this.securityMetadataSource;
          }
      
          public SecurityMetadataSource obtainSecurityMetadataSource() {
              return this.securityMetadataSource;
          }
      
          public void setSecurityMetadataSource(MethodSecurityMetadataSource newSource) {
              this.securityMetadataSource = newSource;
          }
      }
  • MethodService 추가
    • addMethodSecured : 프록시 추가
    • removeMethodSecured : 프록시 제거
    • @Slf4j
      @Component
      public class MethodSecurityService {
          private MapBasedMethodSecurityMetadataSource mapBasedMethodSecurityMetadataSource;
          private AnnotationConfigServletWebServerApplicationContext applicationContext;
          private CustomMethodSecurityInterceptor methodSecurityInterceptor;
      
          private Map<String, Object> proxyMap = new HashMap<>();
          private Map<String, ProxyFactory> advisedMap = new HashMap<>();
          private Map<String, Object> targetMap = new HashMap<>();
      
          public MethodSecurityService(MapBasedMethodSecurityMetadataSource mapBasedMethodSecurityMetadataSource, AnnotationConfigServletWebServerApplicationContext applicationContext, CustomMethodSecurityInterceptor methodSecurityInterceptor) {
              this.mapBasedMethodSecurityMetadataSource = mapBasedMethodSecurityMetadataSource;
              this.applicationContext = applicationContext;
              this.methodSecurityInterceptor = methodSecurityInterceptor;
          }
          //DB에 실시간 정보를 제공할 떄 호출되는 메소드
          public void addMethodSecured(String className, String roleName) throws Exception{
      
              int lastDotIndex = className.lastIndexOf(".");
              String methodName = className.substring(lastDotIndex + 1);
              String typeName = className.substring(0, lastDotIndex);
              Class<?> type = ClassUtils.resolveClassName(typeName, ClassUtils.getDefaultClassLoader());
              String beanName = type.getSimpleName().substring(0, 1).toLowerCase() + type.getSimpleName().substring(1);
      
              ProxyFactory proxyFactory = advisedMap.get(beanName);
              Object target = targetMap.get(beanName);
      
              if(proxyFactory == null){
      
                  proxyFactory = new ProxyFactory();
                  if(target == null) {
                      proxyFactory.setTarget(type.getDeclaredConstructor().newInstance());
      
                  }else{
                      proxyFactory.setTarget(target);
                  }
                  proxyFactory.addAdvice(methodSecurityInterceptor);
      
                  advisedMap.put(beanName, proxyFactory);
      
              }else{
      
                  int adviceIndex = proxyFactory.indexOf(methodSecurityInterceptor);
                  if(adviceIndex == -1){
                      proxyFactory.addAdvice(methodSecurityInterceptor);
                  }
              }
      
              Object proxy = proxyMap.get(beanName);
      
              if(proxy == null){
      
                  proxy = proxyFactory.getProxy();
                  proxyMap.put(beanName, proxy);
      
                  List<ConfigAttribute> attr = Arrays.asList(new SecurityConfig(roleName));
                  mapBasedMethodSecurityMetadataSource.addSecureMethod(type,methodName, attr);
      
                  DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry)applicationContext.getBeanFactory();
                  registry.destroySingleton(beanName);
                  registry.registerSingleton(beanName, proxy);
              }
          }
          //실시간 삭제시 호출되는 메소드
          public void removeMethodSecured(String className) throws Exception{
      
              int lastDotIndex = className.lastIndexOf(".");
              String typeName = className.substring(0, lastDotIndex);
              Class<?> type = ClassUtils.resolveClassName(typeName, ClassUtils.getDefaultClassLoader());
              String beanName = type.getSimpleName().substring(0, 1).toLowerCase() + type.getSimpleName().substring(1);
              Object newInstance = type.getDeclaredConstructor().newInstance();
      
              DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry)applicationContext.getBeanFactory();
      
              ProxyFactory proxyFactory = advisedMap.get(beanName);
      
              if(proxyFactory != null){
                  proxyFactory.removeAdvice(methodSecurityInterceptor);
      
              }else{
                  registry.destroySingleton(beanName);
                  registry.registerSingleton(beanName, newInstance);
                  targetMap.put(beanName,newInstance);
              }
          }
      }
  •  Resource Controller에서 등록, 삭제시 메소드를 호출해 준다.

 

728x90