스프링 시큐리티

Ch02. 스프링 시큐리티 주요 아키텍처 이해 - 인가 결정 심의자(AccessDecisionManager, AccessDecisionVoter)

webmaster 2022. 1. 17. 13:46
728x90

AccessDecisionManager

  • 인증 정보, 요청정보, 권한 정보를 이용해서 사용자의 자원 접근을 허용할 것인지 거부할 것인지를 최종 결정하는 주체
  • 여러 개의 Voter 들을 가질 수있으며 Voter 들로부터 접근 허용,거부, 보류에 해당하는 각각의 값을 리턴받고 판단 및 결정
  • 최종 접근 거부  시 예외 발생
  • 접근 결정의
    • AffirmativeBased 
      • 여러개의 Voter 클래스 중 하나라도 접근 허가로 결론을 내면 접근 허가로 판단한다
      • 하나만 승인이더라도 승인이 된다.
    • ConsensusBased 
      • 다수표(승인 및 거부)에 의해 최종 결정을 판단한다
      • 동수일경우 기본은 접근 허가이나allowIfEqualGrantedDeniedDecisions을설정할 경우 접근거부로 결정된다
      • 다수표에 따른 승인/거부 판결
    • UnanimousBased 
      • 모든 보터가 만장일치로 접근을 승인해야 하며 그렇지 않은 경우 접근을 거부한다
      • 모두 승인해야 최종 승인

AccessDecisionVoter

  • 판단을 심사하는 것(위원)
  • Voter 가 권한 부여 과정에서 판단하는 자료
    • Authentication - 인증 정보(user)
    • FilterInvocation 요청 정보 (antMatcher("/user"))
    • ConfigAttributes - 권한 정보 (hasRole("USER"))
  • 결정 방식
    • ACCESS_GRANTED : 접근 허용(1)
    • ACCESS_DENIED : 접근 거부(-1)
    • ACCESS_ABSTAIN : 접근 보류(0)
      • Voter 가 해당 타입의 요청에 대해 결정을 내릴 수 없는 경우

동작과정

  • 1. FilterSecurityInterceptor에서 AccessDecisionManager에게 인증을 맡긴다.
  • 2. AccessDecisionManager가 인증정보, 요청정보, 권한 정보를 참고하여, AccessDecisionVoter에 심사를 맡는다.
  • 3. 최종 승인이 되면 FilterSecurityInterceptor에 리턴한다.
    • 만약 실해하면 ACCESS_DENIED를 반환하고, AccessDeniedException이 발생한다.
  • AccessDecisionManager
    • void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
            throws AccessDeniedException, InsufficientAuthenticationException;
    • 해당 메서드를 구현한 3가지 구현체가 있다.
  • AffirmativeBased
    • @Override
      @SuppressWarnings({ "rawtypes", "unchecked" })
      public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
            throws AccessDeniedException {
         int deny = 0;
         for (AccessDecisionVoter voter : getDecisionVoters()) {
            int result = voter.vote(authentication, object, configAttributes);
            switch (result) {
            case AccessDecisionVoter.ACCESS_GRANTED:
               return;
            case AccessDecisionVoter.ACCESS_DENIED:
               deny++;
               break;
            default:
               break;
            }
         }
         if (deny > 0) {
            throw new AccessDeniedException(
                  this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
         }
         // To get this far, every AccessDecisionVoter abstained
         checkAllowIfAllAbstainDecisions();
      }
  • ConsensusBased
    • @Override
      @SuppressWarnings({ "rawtypes", "unchecked" })
      public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
            throws AccessDeniedException {
         int grant = 0;
         int deny = 0;
         for (AccessDecisionVoter voter : getDecisionVoters()) {
            int result = voter.vote(authentication, object, configAttributes);
            switch (result) {
            case AccessDecisionVoter.ACCESS_GRANTED:
               grant++;
               break;
            case AccessDecisionVoter.ACCESS_DENIED:
               deny++;
               break;
            default:
               break;
            }
         }
         if (grant > deny) {
            return;
         }
         if (deny > grant) {
            throw new AccessDeniedException(
                  this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
         }
         if ((grant == deny) && (grant != 0)) {
            if (this.allowIfEqualGrantedDeniedDecisions) {
               return;
            }
            throw new AccessDeniedException(
                  this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
         }
         // To get this far, every AccessDecisionVoter abstained
         checkAllowIfAllAbstainDecisions();
      }
  • UnanimousBased
    • @Override
      @SuppressWarnings({ "rawtypes", "unchecked" })
      public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> attributes)
            throws AccessDeniedException {
         int grant = 0;
         List<ConfigAttribute> singleAttributeList = new ArrayList<>(1);
         singleAttributeList.add(null);
         for (ConfigAttribute attribute : attributes) {
            singleAttributeList.set(0, attribute);
            for (AccessDecisionVoter voter : getDecisionVoters()) {
               int result = voter.vote(authentication, object, singleAttributeList);
               switch (result) {
               case AccessDecisionVoter.ACCESS_GRANTED:
                  grant++;
                  break;
               case AccessDecisionVoter.ACCESS_DENIED:
                  throw new AccessDeniedException(
                        this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
               default:
                  break;
               }
            }
         }
         // To get this far, there were no deny votes
         if (grant > 0) {
            return;
         }
         // To get this far, every AccessDecisionVoter abstained
         checkAllowIfAllAbstainDecisions();
      }
728x90