본문 바로가기
Spring/Spring Security

권한위원회 voter

코동이 2021. 10. 17.

일반적으로 권한은 AccessDecisionManager이 담당하며, 역할은 인증이 완료된 사용자가 리소스에 접근하려고 할때 해당 요청을 허용할것인지 판단하는 인터페이스이다. AccessDecisionManager decide()만 구현하면 끝이난다. 

 

Voter기반으로 할 필요는 없지만 Voter를 많이 사용하기 때문에 해당 방식의 원리를 알고 있는 것이 좋다. 인터페이스 구현체는 3가지를 제공한다.

 

AccessDecisionManger 권한 위원회는

- AffirmativeBase :여러 Voter중에 하나라도 허용되면 허용된다. (기본설정)

- ConsensesBased : 다수결

- UnaminousBased : 만장일치

 

3가지의 종류를 가지고 있다.

 

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);

			if (logger.isDebugEnabled()) {
				logger.debug("Voter: " + voter + ", returned: " + result);
			}

			switch (result) {
			case AccessDecisionVoter.ACCESS_GRANTED:
				return;

			case AccessDecisionVoter.ACCESS_DENIED:
				deny++;

				break;

			default:
				break;
			}
		}

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

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

 

 

* AccessDecisionVoter

 

해당 인증이 특정한 요청으로 접근할 때 필요한 ConfigAttributes 를 만족하는지 확인한다.

ConfigAttributes : .permitAll(), .hasRole("") 

 

WebExpressionVoter : 웹 시큐리티에서 사용하는 기본 구현체, ROLE_XXXX 가 매치하는지 확인

RoleHierarchyVoter : 계층형 Role 지원, ADMIN > MANAGER > USER

 

 

Filer에서 검사하는 VoterWebExpressionVoter만 사용한다. Method에서 검사하는 VoterRoleVoter, AuthenticatedVoter, PIAAVoter를 사용한다. 

 

* RoleVoter

 

Role은 권한 계층을 나타내며 리눅스, 아파치 등 IT 초기부터 전통적으로 구현해서 사용하던 직관적인 체계이다. 하지만 너무 Role이 다양해지면서 Role을 확장한 Authority 기반의 권한체계로 확장되었다. 기존의 Role이 가지고 있는 직관적이고 계층적인 사용성을 위해서 RoleVoter가 사용된다.

 

GrantedAuthority에서는 Role_xxx로 사용한다.

RoleHierarchyVoter로 권한 계층 선언이 가능하다.

 

* AuthenticatedVoter

 

통행증을 받았다면, 인증의 종류가 무엇인지 판단한다. 인증을 받은 사용자와 RememberMe 토큰을 통해서 들어온 사용자, 익명자를 구분하기 위해 사용한다.

 

- fully authenticated

- remember me

- anonymous

 

토큰이 탈취당했을 경우, 사용자가 재로그인을 하기 전까지는 탈취된 토큰으로 마음대로 사기행세를 할 수 있다. 따라서, 토큰을 믿을 수 없다!라고 안전장치를 걸고 다시한번 제대로 사용자를 인증하기 위해서 fully authenticated를 사용할 수 있다. 하지만, 이 역할은 대부분 spEL을 사용하는 voter 해결이 가능하다. 따라서 잘 사용하지 않는다.

 

 

*spEL을 이용한 voter

 

 

 

SpEL 문법은 Filter의 경우 WebExpressionVoter로 검사가 되며, Method의 경우 PIAAVoter로 검사가 된다. SpEL은 스프링 3부터 사용되기 시작했다. 스프링 시큐리티의 경우 SpEL을 사용해 권한 검증을 하나의 Voter로 통일하면서 WebExpressionVoterPIAAVoter 2개의 대표적인 Voter를 사용한다. 이 기능들이 RoleVoter와 AuthenticatedVoter 기능을 대체한다.

 

 

자바는 소스코드를 수정하면 다시 서버를 올린다. 따라서 컴파일 언어의 성격을 가지고 있는데 SpEL의 방식으로 시큐리티에서 스크립트 방식을 사용할 수 있다. 동적으로 WEB에서 스프링 빈에 적용하도록 하는 것이다. script를 주면 ExpressionParser를 통해 Expression을 만든다.

 

BeanResolver는 Bean이름으로 Bean을 찾아준다.

 

 

* SecurityExpressionRoot

 

Filter를 담당하는 WebExpressionVoter는 Context Root를 WebSecurityExrespssionRoot로 한다. 글로벌 Method를 담당하는 PreInvocationAuthorizationAdviceVoter는 Context Root를 MethodSecuirityExpressionRoot로 한다.  2개의 Root는 거의 하는 일이 비슷한다. 공통의 SecurityExpressionRoot를 상속받아서 구현을 한다. 

 

SecurityExpressionRoot는 다음과 같은 역할을 한다. permitAll, denyAll

 

- RoleVoter의 기능을 대신한다. hasRole, hasAnyRole, hasAuthority, hasAnyAuthority를 한다.

 

- AuthenticatiedVoter의 기능을 대신한다. isAnonymous, isAuthenticated, isFullyAuthenticated를 한다.

 

- haPermission

  - target: 대상객체를 보고 permission을 검사한다.

  - targetId, type : type를 보고 targetId인 대상 객체를 조회해서 permission을 검사한다.

 

 

*PermissionEvaluator

 

기존의 권한 검사는 Authentication 통행증 검사이다. 그런데, 보통 심사를 할 때 통행증만 검사하는 것이 아니라 어떤 일을 하려고 계획하는지도 확인해야 하는 경우가 대부분이다.

 

예를 들어, 기말고사 시험이라면 시험문제는 선생님만 작성할 수 있고, 교장 선생님은 모든 시험지를 볼 수 있다. 과목이 같은 선생님들끼리는 시험지를 공유할 수 있다. 서로 다른 과목의 선생님은 서로의 시험지를 볼 수 없다. 이런 식의 복잡한 인증 체계가 있다면 PermissionEvaluator를 사용해야 한다. 혹은 객체별로 접근권한을 DB로 관리하는 ACL처럼 권한을 체크하기 위한 별도의 설계가 필요하다.

 

 

* WebExpressionVoter와 PIIAVoter

 

AccessDecisionManagerVoter의 기능들이 거의 사용되지 않는다.

 

spEL을 사용한다. Root객체는 Web의 경우 haslpAddress가 있어서 #을 붙이지 않을 수 있다. Method의 경우도 filterObjectreturnObject때문에 #을 붙이지 않을 수 있다. Web에서는 URL에서 경로에 관한 PathVariable의 경우 Context에서 Variable이라 #으로 사용한다. 빈의 경우는 @를 사용한다.

 

SecurityExpressionHandler는 인터페이스이고 기본 구현객체가 DefaultWebSecurityEpxressionHandler이다.

 

MethodSecurityExpressionHandler는 인터페이스이고 기본 구현객체가 DefaultMethodSecurityExpressionHandler이다.

반응형

'Spring > Spring Security' 카테고리의 다른 글

@Secured 기반 권한체크  (0) 2021.10.20
메서드 후처리  (0) 2021.10.19
스프링 시큐리티의 권한(Authorization)  (0) 2021.10.15
권한체크 오류처리  (0) 2021.10.15
세션관리 ( 동시접속 )  (0) 2021.10.14