본 글은 Spring Security docs 와 여러 블로그 들을 참고하고, 공부하면서 요약하였습니다.
1. AccessDecisionManager, AccessDecisionVoter
1-1) AccessDecisionManager
AccessDecisionManager는 인터페이스 이다.
- 인증, 요청, 권한 정보를 이용해서 사용자의 자원접근을 허용 or 거부 여부를 최종 결정하는 주체이다.
- 여러 개의 Voter들을 가질 수 있고, Voter들로부터 접근허용,거부,보류에 해당하는 각각의 값을 리턴받아 판단&결정한다.
- 최종 접근 거부시 예외 발생
▶ AccessDecisionManager의 3가지 구현체
1. AffirmativeBased
여러개의 Voter 클래스들 중 하나라도 접근 허가 결론을 내면 접근을 허가한다.
2. ConsensusBased
다수결(승인 및 거부)에 의해 최종 결정을 판단한며, 만약 승인과 거부의 표 수가 동일하다면 "승인"이 default로 결정된다.
allowIfEqualGrantedDeniedDecisions을 false로 설정할 경우 접근 거부로 결정된다.
3. UnanimousBased
모든 Voter가 만장일치로 접근을 승인해야 하며, 그렇지 않은 경우 접근을 거부한다.
1-2) AccessDecisionVoter
판단을 심사하는 위원 역할을 한다.
각각의 Voter마다 사용자의 요청마다 해당 자원에 접근할 권한이 있는지 판단 후 AccessDecisionManager에게 반환하게 된다.
이때 Voter가 Manager로부터 인자로 전달받아 권한 판단의 기준으로 작용하는 인자가 3개 있다.
- Authenticaion - 인증정보(user)
- FilterInvocator - 요청 정보(antMatcher("/user"))
- ConfigAttributes - 권한 정보(hasRole("USER"))
public interface AccessDecisionVoter<S> {
int ACCESS_GRANTED = 1;
int ACCESS_ABSTAIN = 0;
int ACCESS_DENIED = -1;
boolean supports(ConfigAttribute attribute);
boolean supports(Class<?> clazz);
int vote(Authentication authentication, S object, Collection<ConfigAttribute> attributes);
}
결정 방식 : AccessDecisionManager 은 반환 받은 결정 방식을 사용해서 후처리를 한다.
- ACCESS_GRANTED: 접근 허용(1)
- ACCESS_DENIED: 접근 거부(-1)
- ACCESS_ABSTAIN: 접근 보류(0) -> Voter가 해당 타입의 요청에 대해 결정을 내릴 수 없는 경우
▶ Flow
1. FilterSecurityInterceptor 가 AccessDecisionManager에 인가처리 위임
2. AccessDecisionManager는 자신이 가지고 있는 Voter들에게 정보decide(authentication, object, configAttributes)를 전달한다.
3. Voter들은 정보들을 가지고 권한 판단을 심사한다.
4. 승인,거부,보류 결정방식을 반환하면 AccessDecisionManager에서는 반환받은 결정 방식을 가지고 후처리를 한다.
a. 승인: FilterSecurityInterceptor에 승인여부 반환
b. 거부: AccessDeniedException 예외를 ExceptionTranslationFilter로 전달
2. 디버깅 해보기
AccessDecisionManager의 인터페이스는 다음과 같다.
public interface AccessDecisionManager {
void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException;
boolean supports(ConfigAttribute attribute);
boolean supports(Class<?> clazz);
}
위에서 말했듯, 이에 대한 구현체로는 AffirmativeBased, ConsensusBased, UnanimousBased 가 있다.
▶ AffirmativeBased
decide 메서드를 보면 voters 목록을 돌면서 어느 하나든 1(승인)의 경우가 나오면 바로 return 하게 된다.
▶ ConsensusBased
이 manager는 모든 voter를 확인하면서 승인, 거부를 확인하여 다수결로 결정하게 된다.
위 코드에도 보이듯, -1, 1 을 case로 판단하여 dent, grant 변수의 수를 카운트 하여 다수결로 결정한다.
▶ UnanimousBased
만약 하나라도 -1을 반환받는다면 바로 예외를 터트리게 된다.
이제 "/"로 접속해보자. AbstractSecurityInterceptor#attemptAuthorization()에 걸리게 된다.
구현체로 넘어가보면 다음과 같다.
구현체 에서는 voter를 찾아오게 되는데, 현재는 WebExpressionVoter하나만 등록되어 있다.
voter.vote()를 통해 판단하게 되고, Manager는 결과로 정수를 받게 된다.
이렇게 인가 처리가 된다.
3. 출처
https://catsbi.oopy.io/f9b0d83c-4775-47da-9c81-2261851fe0d0
댓글