스프링 시큐리티

Ch02. 스프링 시큐리티 주요 아키텍처 이해 - 인가 개념 및 필터 이해 (Authorization, FilterSecurityInterceptor)

webmaster 2022. 1. 17. 11:24
728x90

Authorization

당신에게 무엇이 허가 되었는지 증명하는 것

스프링 시큐리티가 지원하는 권한 계층
  • 웹 계층
    • URL 요청에 따른 메뉴 혹은 화면 단위의 레벨 보안
  • 서비스 계층
    • 화면 단위가 아닌 메소드 같은 기능 단위의 레벨 보안
  • 도메인 계층(Access Control List, 접근제어 목록)
    • 객체 단위의 레벨 보안

FilterSecurityInterceptor

  • 마지막에 위치한 필터로써 인증된 사용자에 대하여 특정 요청의 승인/거부 여부를 최종적으로 결정
  • 인증 객체 없이 보호자원에 접근을 시도할 경우 AuthenticationException을 발생
  • 인증 후 자원에 접근 가능한 권한이 존재하지 않을 경우 AccessDeniedException을 발생
  • 권한 제어 방식 중 HTTP 자원의 보안을 처리하는 필터
  • 권한 처리를 AccessDecisionManager에게 맡김
  • 동작 흐름

    • 인증 객체 있는지 체크 (없으면 AuthenticationException 예외를 발생시킨다)
    • SecurityMetadataSource가 사용자의 인증 객체를 권한 정보를 가지고 온다.
      • 권한 정보가 null : 자원 접근 허용
      • null 아님 : AccessDecisionManager가 권한 정보를 받아 AccessDecisionVoter 심의 요청을 한다.
    • 접근이 승인이 될 경우 자원 접근을 허용하고, 승인이 실패할 경우 AccessDeniedException 예외가 발생한다.
  • protected InterceptorStatusToken beforeInvocation(Object object) {
       Assert.notNull(object, "Object was null");
       if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
          throw new IllegalArgumentException("Security invocation attempted for object " + object.getClass().getName()
                + " but AbstractSecurityInterceptor only configured to support secure objects of type: "
                + getSecureObjectClass());
       }
       //자원에 설정된 권한을 가지고 오는 부분
       Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
       if (CollectionUtils.isEmpty(attributes)) {
          Assert.isTrue(!this.rejectPublicInvocations,
                () -> "Secure object invocation " + object
                      + " was denied as public invocations are not allowed via this interceptor. "
                      + "This indicates a configuration error because the "
                      + "rejectPublicInvocations property is set to 'true'");
          if (this.logger.isDebugEnabled()) {
             this.logger.debug(LogMessage.format("Authorized public object %s", object));
          }
          publishEvent(new PublicInvocationEvent(object));
          return null; // no further work post-invocation
       }
       // 그 사용자가 인증객체가 있는지 없는지
       if (SecurityContextHolder.getContext().getAuthentication() == null) {
          credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",
                "An Authentication object was not found in the SecurityContext"), object, attributes);
       }
       Authentication authenticated = authenticateIfRequired();
       if (this.logger.isTraceEnabled()) {
          this.logger.trace(LogMessage.format("Authorizing %s with attributes %s", object, attributes));
       }
       // Attempt authorization
       attemptAuthorization(object, attributes, authenticated);
       if (this.logger.isDebugEnabled()) {
          this.logger.debug(LogMessage.format("Authorized %s with attributes %s", object, attributes));
       }
       if (this.publishAuthorizationSuccess) {
          publishEvent(new AuthorizedEvent(object, attributes, authenticated));
       }
    
       // Attempt to run as a different user
       Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
       if (runAs != null) {
          SecurityContext origCtx = SecurityContextHolder.getContext();
          SecurityContext newCtx = SecurityContextHolder.createEmptyContext();
          newCtx.setAuthentication(runAs);
          SecurityContextHolder.setContext(newCtx);
    
          if (this.logger.isDebugEnabled()) {
             this.logger.debug(LogMessage.format("Switched to RunAs authentication %s", runAs));
          }
          // need to revert to token.Authenticated post-invocation
          return new InterceptorStatusToken(origCtx, true, attributes, object);
       }
       this.logger.trace("Did not switch RunAs authentication since RunAsManager returned null");
       // no further work post-invocation
       return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
    
    }
728x90