봄 보안 권한 관리

11743 단어 springsecurity
최근 프로젝트 는 Spring Security 의 권한 제어 가 필요 하기 때문에 권한 제어 와 관련 된 소스 코드(버 전 4.2)를 간단하게 살 펴 보 았 습 니 다.
AccessDecisionManager
spring security 는 Access Decision Manager 를 통 해 권한 수여 관 리 를 하 는 것 입 니 다.먼저 공식 도 진 루 를 하 겠 습 니 다.

AccessDecisionManager
AccessDecisionManager 인 터 페 이 스 는 다음 과 같은 방법 을 정의 합 니 다.

//  AccessDecisionVoter    (    )
void decide(Authentication authentication, Object object,
    Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,
    InsufficientAuthenticationException;

boolean supports(ConfigAttribute attribute);
boolean supports(Class clazz);

다음은 그 실현 류 의 구체 적 인 실현 을 살 펴 보 자.
AffirmativeBased

public void decide(Authentication authentication, Object object,
    Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
  int deny = 0;

  for (AccessDecisionVoter voter : getDecisionVoters()) {
    //  AccessDecisionVoter  vote(          ),    vote   。
    int result = voter.vote(authentication, object, configAttributes);

    if (logger.isDebugEnabled()) {
      logger.debug("Voter: " + voter + ", returned: " + result);
    }
    
    switch (result) {
    case AccessDecisionVoter.ACCESS_GRANTED://  1
      //   voter   ACCESS_GRANTED,   
      return;

    case AccessDecisionVoter.ACCESS_DENIED://  -1
      deny++;

      break;

    default:
      break;
    }
  }

  if (deny > 0) {
    //        AccessDecisionVoter(         )  ACCESS_DENIED,        
    throw new AccessDeniedException(messages.getMessage(
        "AbstractAccessDecisionManager.accessDenied", "Access is denied"));
  }

  // To get this far, every AccessDecisionVoter abstained
  checkAllowIfAllAbstainDecisions();
}

위의 코드 를 통 해 Affirmative Based 정책 을 직접 볼 수 있 습 니 다.
  • 통과 만 있 으 면(ACCESSGRANTED)표 는 직접 통과 로 판결 된다
  • 통과 표를 던 지지 않 고 반대 한다 면(ACCESSDENIED)표 가 두 개 이상 이면 직접 통과 하지 못 한 것 으로 판결 된다
  • UnanimousBased
    
    public void decide(Authentication authentication, Object object,
        Collection<ConfigAttribute> attributes) throws AccessDeniedException {
    
      int grant = 0;
      int abstain = 0;
    
      List<ConfigAttribute> singleAttributeList = new ArrayList<ConfigAttribute>(1);
      singleAttributeList.add(null);
    
      for (ConfigAttribute attribute : attributes) {
        singleAttributeList.set(0, attribute);
    
        for (AccessDecisionVoter voter : getDecisionVoters()) {
          //          
          int result = voter.vote(authentication, object, singleAttributeList);
    
          if (logger.isDebugEnabled()) {
            logger.debug("Voter: " + voter + ", returned: " + result);
          }
    
          switch (result) {
          case AccessDecisionVoter.ACCESS_GRANTED:
            grant++;
    
            break;
    
          case AccessDecisionVoter.ACCESS_DENIED:
            //                   
            throw new AccessDeniedException(messages.getMessage(
                "AbstractAccessDecisionManager.accessDenied",
                "Access is denied"));
    
          default:
            abstain++;
    
            break;
          }
        }
      }
    
      // To get this far, there were no deny votes
      if (grant > 0) {
        //           ,       
        return;
      }
    
      // To get this far, every AccessDecisionVoter abstained
      checkAllowIfAllAbstainDecisions();
    }
    
    
    이 를 통 해 알 수 있 듯 이 Unanimous Based 정책:
  • 아무리 많은 투표 자가 투 표를 했 더 라 도 통과(ACCESSGRANTED)표,반대 표 만 있 으 면(ACCESSDENIED),그것 은 모두 통과 하지 못 한 것 으로 판결 되 었 다
  • 4.567917.반대 표 가 없고 유권자 가 통과 표를 던 졌 다 면 통과 로 판결 한다ConsensusBased
    
    public void decide(Authentication authentication, Object object,
        Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
      int grant = 0;
      int deny = 0;
      int abstain = 0;
    
      for (AccessDecisionVoter voter : getDecisionVoters()) {
        //          
        int result = voter.vote(authentication, object, configAttributes);
    
        if (logger.isDebugEnabled()) {
          logger.debug("Voter: " + voter + ", returned: " + result);
        }
    
        switch (result) {
        case AccessDecisionVoter.ACCESS_GRANTED:
          grant++;
    
          break;
    
        case AccessDecisionVoter.ACCESS_DENIED:
          deny++;
    
          break;
    
        default:
          abstain++;
    
          break;
        }
      }
    
      if (grant > deny) {
        //                 
        return;
      }
    
      if (deny > grant) {
        //                  
        throw new AccessDeniedException(messages.getMessage(
            "AbstractAccessDecisionManager.accessDenied", "Access is denied"));
      }
    
      if ((grant == deny) && (grant != 0)) {
        //this.allowIfEqualGrantedDeniedDecisions   true
        //             ,      allowIfEqualGrantedDeniedDecisions        
        if (this.allowIfEqualGrantedDeniedDecisions) {
          return;
        }
        else {
          throw new AccessDeniedException(messages.getMessage(
              "AbstractAccessDecisionManager.accessDenied", "Access is denied"));
        }
      }
    
      // To get this far, every AccessDecisionVoter abstained
      checkAllowIfAllAbstainDecisions();
    }
    
    
    이 를 통 해 알 수 있 듯 이 Consensus Based 의 전략:
    4.567917.통 과 된 표 가 반대 표 보다 많 으 면 통과 로 판결 된다4.567917.통 과 된 표 가 반대 표 보다 적 으 면 통과 되 지 않 는 다
  • 통 과 된 표 와 반대 표 가 같 으 면 allowIfEqual Granted Denied Decisions(기본 값 true)설정 에 따라 통과 여 부 를 판단 할 수 있 습 니 다
  • 이 쯤 되면 Affirmative Based,Unanimous Based,Consensus Based 세 가지 차이 점 을 알 겠 죠?spring security 는 기본적으로 Affirmative Based 를 사용 합 니 다.필요 하 다 면 다른 두 가지 로 설정 할 수도 있 고 스스로 실현 할 수도 있 습 니 다.
    투표 자
    이상 의 AccessDecisionManager 의 실현 클래스 는 권한(투표)에 대한 관리(전략의 실현)일 뿐 구체 적 인 투표(vote)의 논 리 는 AccessDecisionVoter 의 하위 클래스(투표 자)를 호출 하 는 vote 방법 을 통 해 이 루어 진다.spring security 는 기본적으로 RoleVoter 와 Authenticated Voter 두 투표 자 를 등록 했다.다음은 그 소스 코드 를 살 펴 보 겠 습 니 다.
    AccessDecisionManager
    
    boolean supports(ConfigAttribute attribute);
    boolean supports(Class<?> clazz);
    //    ,          AccessDecisionManager  ,           。
    int vote(Authentication authentication, S object,
        Collection<ConfigAttribute> attributes);
    RoleVoter
    
    private String rolePrefix = "ROLE_";
    
    //   ROLE_   (     rolePrefix      )
    public boolean supports(ConfigAttribute attribute) {
      if ((attribute.getAttribute() != null)
          && attribute.getAttribute().startsWith(getRolePrefix())) {
        return true;
      }
      else {
        return false;
      }
    }
    
    public int vote(Authentication authentication, Object object,
        Collection<ConfigAttribute> attributes) {
        
      if(authentication == null) {
        //       ,     
        return ACCESS_DENIED;
      }
      int result = ACCESS_ABSTAIN;
      //         
      Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication);
    
      for (ConfigAttribute attribute : attributes) {
        if (this.supports(attribute)) {
          result = ACCESS_DENIED;
    
          // Attempt to find a matching granted authority
          for (GrantedAuthority authority : authorities) {
            if (attribute.getAttribute().equals(authority.getAuthority())) {
              //         
              return ACCESS_GRANTED;
            }
          }
        }
      }
      //     ,      ,     ,      ,      (ACCESS_ABSTAIN)。
      return result;
    }  
    
    
    간단 하 죠?동시에 우 리 는 AccessDecisionManager 를 실현 함으로써 자신의 voter 를 확장 할 수 있 습 니 다.그러나 이 를 실현 하려 면 attributes 라 는 매개 변수 가 어디서 났 는 지 알 아야 합 니 다.이것 은 매우 관건 적 인 매개 변수 입 니 다.공식 그림 을 통 해 이 문 제 를 뚜렷하게 알 수 있다.

    다음은 AccessDecisionManager 의 호출 자 Abstract Security Interceptor 를 살 펴 보 자.
    AbstractSecurityInterceptor
    
    ...
    //       AffirmativeBased,   
    private AccessDecisionManager accessDecisionManager;
    ...
    protected InterceptorStatusToken beforeInvocation(Object object) {
      ...
      //    ,    ,       ConfigAttribute  SecurityMetadataSource(   ,   DefaultFilterInvocationSecurityMetadataSource)  。
      Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
          .getAttributes(object);
      ...
      //            
      Authentication authenticated = authenticateIfRequired();
    
      try {
        //  AccessDecisionManager
        this.accessDecisionManager.decide(authenticated, object, attributes);
      }
      catch (AccessDeniedException accessDeniedException) {
        publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
            accessDeniedException));
    
        throw accessDeniedException;
      }
      ...   
    }
    
    public abstract SecurityMetadataSource obtainSecurityMetadataSource();
    
    이상 의 방법 은 모두 AbstractSecurity Interceptor 의 하위 클래스(기본 값 은 FilterSecurity Interceptor)에서 호출 되 었 습 니 다.다시 보 겠 습 니 다.
    FilterSecurityInterceptor
    
    ...
    //SecurityMetadataSource    ,    ,       。             SecurityMetadataSource               ConfigAttribute
    private FilterInvocationSecurityMetadataSource securityMetadataSource; 
    ...
    //  
    public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {
      FilterInvocation fi = new FilterInvocation(request, response, chain);
      //    
      invoke(fi);
    }
    
    public void invoke(FilterInvocation fi) throws IOException, ServletException {
      if ((fi.getRequest() != null)
          && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
          && observeOncePerRequest) {
        // filter already applied to this request and user wants us to observe
        // once-per-request handling, so don't re-do security checking
        fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
      }
      else {
        // first time this request being called, so perform security checking
        if (fi.getRequest() != null) {
          fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
        }
        //        (AbstractSecurityInterceptor)   ,      accessDecisionManager
        InterceptorStatusToken token = super.beforeInvocation(fi);
    
        try {
          fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        }
        finally {
          super.finallyInvocation(token);
        }
        //     (     ),    ,AOP     
        super.afterInvocation(token, null);
      }
    }
    
    
    자,이제 Spring Security 의 권한 관리 에 대해 잘 알 것 같 습 니 다.이것 을 보고 당신 이 원 하 는 권한 수 요 를 확장 할 수 있 을 지 모 르 겠 습 니 다.아직 잘 모 르 겠 으 면 괜 찮 습 니 다.다음 편 은 실전 을 통 해 자신의 권한 체 계 를 개발 하 겠 습 니 다.
    이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

    좋은 웹페이지 즐겨찾기