본문 바로가기

프로그래밍/Spring Security

Spring Security의 자원 접근 판단에 대한 설명

지난 글에서는 권한에 대한 개념을 조금 짚어봤다. 지난 글에 동의하는 사람도 있고 동의하지 않는 사람도 있을것이다. 지난 글에서도 언급했지만 그게 절대적인 진리는 아니다. 어찌보면 내가 가지고 있는 권한 설계의 사상을 얘기한 것이다. 다만 그 설계 사상이 모든 플젝에 맞는 것은 아니기땜에 단순하게 할수도 있고 장기적인 안목을 보고 좀더 신경써서 디테일하게 할수도 있는 것이다. 암튼 플젝에 따른 권한 설계 사상을 가지고 신중히 설계하길 바란다.

 

이번 글에서는 이렇게 설계한 권한을 이용해서 Spring Security가 어떤식으로 자원과 매핑을 지어 관리하는지를 보도록 하자. 이 기능을 하는데 있어 총괄적인 역할을 하는 클래스는 org.springframework.security.web.access.intercept.FilterSecurityInterceptor 클래스이다. 이 클래스를 어디서 보았는지 기억하는가? Spring Security 관련 연재글에서 처음 글을 쓸때 Spring Security의 Filter에 대해 언급한 적이 있는데 이 클래스가 Filter중 가장 마지막으로 올라오는 Filter 클래스이다. 즉 이 Filter 다음으로 오는게 Spring MVC의 Controller 클래스이다. 이 클래스 객체에 설정되어야 하는 것은 다음의 3가지 이다

 

● 인증 정보(Authentication Manager)

● 대상 정보(Security MetaDataSource)

● 판단 주체(Access Manager)

 

인증 정보는 로그인 한 주체의 정보가 된다. 즉 로그인 한 사람의 계정 및 권한 정보이다. 우리는 이것을 이전의 글에서 DB에서 조회해서 세션에 보관하는 식으로 넣었다. 대상 정보는 우리가 가지고 있는 자원과 이를 접근할 수 있는 권한이 매핑된 정보이다. 우리는 이전글에서 <intercept-url> 태그에서 URL 패턴과 그 패턴을 이용할 수 있는 권한 설정을 했던 적이 있다. 이것이 대상 정보이다. 그래서 인증 정보와 대상 정보는 이전 글에서 어떤 식으로든 구현하거나 설정을 이용해서 표현한 적이 있다. 문제는 판단 주체이다. 판단 주체? 이것에 대한 설정은 한번도 한적이 없는데? 판단 주체란 무엇이지?

 

판단 주체는 인증 정보와 대상 정보를 이용해서 이를 허용할 것인지 말 것인지를 결정하는 주체이다. Spring Security는 이와 관련하여 3가지의 Class를 제공한다. 이 3개의 클래스는 org.springframework.security.access.vote.AbstractAccessDecisionManager 클래스를 모두 상속 받는데 3개의 클래스는 다음과 같다.

 

● org.springframework.security.access.vote.AffirmativeBased

● org.springframework.security.access.vote.ConsensusBased

● org.springframework.security.access.vote.UnanimousBased

 

위에서 우리는 판단 주체라는 말을 사용했는데 이것은 일종의 조직이라고 이해하는 것이 더 쉽다. 이 판단 주체는 일종의 위원회 개념이라고 보는 것이 더 편하다. 즉 판단을 심사하는 것(판단을 심사하는 위원)들이 여러개가 모여있는 조직이다. 판단을 심사하는 것을 Voter라고 하는데 Spring Security는 이런 Voter를 org.springframework.security.access.AccessDecisionVoter 인터페이스를 구현한 클래스들로 제공해주고 있다. 어떤 클래스가 제공되는지는 Spring Security API 문서에서 AccessDecisionVoter 인터페이스에 대한 내용을 보면 알 수 있다(Spring Security 3.2.4 기준으로 보면 이 인터페이스를 구현한 8가지의 클래스를 제공해주고 있다). 이 인터페이스에는 3가지 상수가 설정되어 있는데 이 상수는 다음과 같다

 

● ACCESS_GRANTED

● ACCESS_DENIED

● ACCESS_ABSTAIN

 

ACCESS_GRANTED는 자원에 대한 접근을 허가한다는 의미이고, ACCESS_DENIED는 자원에 대한 접근을 거부한다는 의미이고, ACCESS_ABSTAIN은 자원에 대한 접근 여부 판단을 보류하겠다는 의미이다. 우리가 무언가를 허가/거절/보류 할때를 생각해보자. 그런 판단을 하기 위한 자료를 다 받은뒤에 판단을 할 것이다. 그때 허가할수도 있고, 거절할수도 있고, 또는 판단하기에는 받아온 자료가 미진해서 판단을 보류할 수도 있는 것이다. 그리고 이 인터페이스에서 제공되는 함수인 vote 함수를 통해 판단하여 그 결과를 위에서 언급한 3개의 함수 중 하나를 return 하게 되는 것이다. AccessDecisionVoter 인터페이스에서 vote 함수는 다음과 같이 정의되어 있다.

 

public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes)

 

vote 함수에서 첫번째 인자는 로그인 한 사용자의 인증정보로 여기에는 사용자가 가지고 있는 권한도 포함되어 있다. 두번째 인자는 현재 접근하고자 하는 자원이다. 세번째 인자는 현재 접근하는 자원에 접근할 수 있는 권한 목록이 넘어가게 된다.

 

지금까지의 설명으로 Voter가 잘 와닿지가 않는가? 그러면 이해하기 쉽도록 Spring Security에서 제공하는 AccessDecisionVoter 인터페이스를 구현한 클래스들 중 하나인 org.springframework.security.access.vote.RoleVoter 클래스에 대해 설명하도록 하겠다. RoleVoter 클래스는 권한을 가지고 판단하는 voter 클래스이다. 이 클래스의 vote 함수 소스를 보면 다음과 같다

 

public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
	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;
				}
			}
		}
	}

	return result;
}

 

vote 함수에서 첫번째 파라미터로 넘어오는 것은 인증 정보로써 Authentication 인터페이스를 구현한 클래스가 넘어오게 된다. form 기반 로그인 작업시에는 로그인을 하지 않았을때는 Anonymous 권한을 가진 org.springframework.security.authentication.AnonymousAuthenticationToken 객체를 넘겨받게 되고 로그인을 했을 경우 org.springframework.security.authentication.UsernamePasswordAuthenticationToken 객체를 넘겨받게 된다(form 기반 로그인이라고 명시한 이유는 로그인이 반드시 form 방식만 있는건 아니기 때문이다. form 기반이 아닌 방식에서는 다른 클래스 객체가 넘어올 수도 있다. 그러나 그렇다 해도 Authentication 인터페이스를 구현한 클래스 객체가 넘어오는 것은 분명하다). 두번째 파라미터로 넘어오는 것은 현재 접근하고자 하는 자원으로 우리가 웹페이지를 방문할 경우 해당 웹페이지에 대한 org.springframework.security.web.FilterInvocation 객체가 넘어온다. 세번째 파라미터로 넘어오는 것은 현재 접근하는 자원(여기서는 두번째 파라미터인 object)에 접근 가능한 권한 목록이 넘어오게 된다. 이 권한 목록은 이 글 맨 위에서 FilterSecurityInterceptor 클래스 설명 당시 대상 정보를 얘기한 적이 있는데 이 대상 정보를 통해 가져올 수가 있다. 즉 해당 자원(여기서는 URL)을 접근할 수 있는 권한의 목록을 가져오게 되는 것이다. 여기서는 LinkedList 객체로 넘겨지게 되는데 이 객체는 내부에 org.springframework.security.access.SecurityConfig 클래스 객체로 설정되어 있는 권한 객체들이 들어있다.

 

이렇게 넘어오는 값을 가지고 한번 위의 소스를 분석해보자. 먼저 첫번째 파라미터를 이용해서 사용자가 가지고 있는 권한 목록을 뽑아낸다(Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication);) 그리고 중첩 for문을 이용해서 사용자가 가지고 있는 권한 목록과 접근하는 자원에 대한 권한 목록을 비교하여 하나라도 만족하는 권한이 있으면 ACCESS_GRANTED를 return 한다. 만약 찾질 못하면 첫번째 for문에서 초기화한 값인 ACCESS_DENIED가 return이 된다. 그리고 사용자가 가지고 있는 권한 목록에 아무런 권한값이 들어있질 않은 경우 중첩 for문을 들어가기 전에 초기화한 값인 ACCESS_ABSTAIN이 return 된다.

 

이 소스 분석 내용과 아까 AccessVoter 인터페이스 설명한 내용을 맞춰보자. 사용자가 가지고 있는 권한들과 자원을 접근할 수 있는 권한들 중 하나라도 맞는게 있으면 접근할 수 있다는 의미로 ACCESS_GRANTED를 return 한다. 그러나 사용자가 가지고 있는 권한들과 자원을 접근할 수 있는 권한을 다 비교해봐도 맞는게 없으면 접근할 수 없다는 의미로 ACCESS_DENIED를 return 한다. 마지막으로 사용자가 가지고 있는 권한들을 가져왔으나 아무런 권한도 들어있는게 없으면 판단을 유보하는 의미로 ACCESS_ABSTAIN을 return 한다. 이제 이 부분이 이해가 되는가?

 

이렇게 Voter는 권한과 자원에 대해 접근 가능/불가능/보류를 판단하는 일종의 거수기 역할을 하게 된다. 그러나 이러한 권한과 자원에 대한 접근 판단을 하나의 Voter 만으로 하지 않는 경우도 있다. Spring Security가 제공하는 AccessDecisionVoter 인터페이스를 구현한 클래스 중 org.springframework.security.access.vote.AuthenticatedVoter 클래스가 있다. 이 클래스가 하는 역할은 로그인 여부를 가지고 판단하는 것이다. Spring Security는 로그인에 대해 3가지 상태를 가지고 판단하게 되는데 이 3가지는 다음과 같다.

 

● IS_AUTHENTICATED_FULLY

● IS_AUTHENTICATED_REMEMBERED

● IS_AUTHENTICATED_ANONYMOUSLY

 

로그인 상태의 경우는 크게는 로그인을 한 경우와 로그인을 하지 않은 경우(IS_AUTHENTICATED_ANONYMOUSLY)로 두 가지로 나눌수 있다. 또 로그인을 한 경우도 두 가지로 나눌수가 있는데 하나는 아이디와 패스워드를 입력받아 로그인 한 경우(IS_AUTHENTICATED_FULLY)와 예전에 로그인 한 아이디와 비밀번호를 기억하고 있다가 그것으로 로그인 하는 Remember me 기능을 사용한 로그인 하는 경우(IS_AUTHENTICATED_REMEMBERED)로 나눌수 있다. IS_AUTHENTICATED_REMEMBERED의 경우는 로그인 아이디와 비밀번호를 직접 입력받아 로그인 하는 경우와 Remember me 기능 중 하나라도 로그인 된 경우를 얘기한다. 기본적으로 사용되는 AuthenticatedVoter 클래스는 위의 3가지 경우 모두 접근을 허용하고 있다. 즉 아이디와 패스워드를 입력해서 로그인 했든, Remember Me 기능을 이용해서 로그인 했든, 아니면 아예 로그인을 하지 않았든 모두 접근을 허용(ACCESS_GRANTED)하고 있다. 그러나 우리는 AuthenticatedVoter 클래스를 상속받아 vote 메소드를 override 함으로써 이러한 결과를 수정할 수가 있다. 즉 Remember Me 기능을 이용한 로그인은 허용하지 않겠다고 코드를 수정할 수가 있는 것이다.

 

지금까지 두 개의 Voter 클래스를 언급했는데 이 2개를 서로 섞어서 판단할 수 있게 된다. 이 2개를 모두 만족해야 한다거나, 이 2개 중 하나만 만족해도 허용한다거나, 아니면 이것에 대해 또 다른 적절한 기준을 두어서 판단할 수가 있다. 이렇게 여러개의 Voter를 등록해서 이 Voter들이 내놓는 판단결과에 따라 최종 판단을 내리는 것이 위에서 얘기했던 판단 주체가 되는 것이다. 이 판단 주체를 얘기하기 위해 Voter를 먼저 설명하게 되었는데 이제는 본격적으로 이 거수기 역할을 하는 1개 이상의 Voter를 어떻게 세팅해서 판단주체로 하는지 알아보겠다.

 

다음의 설정을 보자

 

<beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
	<beans:constructor-arg>
		<beans:list>
			<beans:bean class="org.springframework.security.access.vote.RoleVoter">
				<beans:property name="rolePrefix" value="" />
			</beans:bean>
			<beans:bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
		</beans:list>
	</beans:constructor-arg>
	<beans:property name="allowIfAllAbstainDecisions" value="false" />
</beans:bean>

 

위의 설정은 이 글의 맨 처음에 언급했던 클래스 중 AffirmativeBased 클래스에 RoleVoter와 AuthenticatedVoter를 2개를 설정한 것이다. 자 이제 이런 의문을 가질 수 있다. 사용자의 인증 정보와 접근하고자 하는 자원(이해하기 쉽게 URL이라고 생각하자)의 권한 정보를 voter가 이용해서 접근 가능 여부를 판단할텐데 RoleVoter는 RoleVoter의 판단 기준에 맞춰 판단해보니 접근 허가(ACCESS_GRANTED)한다고 판단을 했는데 AuthenticatedVoter는 자신의 판단 기준에 맞춰 판단해보니 접근 거부(ACCESS_DENIED)라고 판단했다. 그러면 이럴 경우 접근을 허용해야 하는가? 아니면 접근을 거부해야 하는가?

 

바로 이런 2개 이상의 Voter 클래스를 등록하는 상황에서 이 글의 맨 위에서 설명했던 판단 주체의 역할이 나오게 된다. 위에서 판단 주체를 설명할때 위원회라는 개념으로 설명했는데 위원회엔 위원들이 구성될 것이다. 이 위원들이 Voter 클래스들로 거수기 역할을 하는 것이다. 접근 허용한다, 접근 거절한다..이런 식으로 등록된 Voter 클래스들이 거수기 역할을 했으면 이제 위원회가 이 결과들을 종합적으로 판단하게 되는 것이다. 위의 설정에서 보면 위원회 역할을 하는 클래스로 AffirmativeBased 클래스를 사용했다. 이 글의 맨 위에서 AffrimativeBased 클래스를 보여줬을때 그것과 같이 보여준 UnanimousBased 클래스와 ConsensusBased 클래스를 같이 나열했었다. 이제 이 3가지 클래스의 차이를 설명하겠다.

 

AffrirmativeBased 클래스는 등록된 Voter 클래스 객체 중 단 하나라도 접근 허가(ACCESS_GRANTED)로 결론을 내면 최종적으로 접근 허가 한다고 판단한다. 즉 RoleVoter 클래스가 vote 메소드에서 ACCESS_GRANTED를 return 하고, AuthenticatedVoter가 ACCESS_DENIED를 return 할 경우 ACCESS_GRANTED를 return한 voter 객체가 1개가 있기 때문에 최종적으로 접근 허가한다고 판단해준다. 예를 들어 Voter 클래스 객체를 3개 만들어서 등록했다고 가정해보자. 이런 상황에서 등록된 voter들 중 1개는 ACCESS_GRANTED, 2개는 ACCESS_DENIED를 return 해서 접근 거부가 더 많아도 접근 허가를 시켜준다. 왜? ACCESS_GRANTED가 return 된 것이 하나는 있으니까..

 

그러나 이와는 달리 UnanimousBased 클래스의 경우 등록된 모든 Voter 클래스 객체가 접근 허가(ACCESS_GRANTED)  결론을 내야 최종적으로 접근 허가한다고 판단한다. 만장일치제도를 생각하면 된다. 한명의 반대표라도 있으면 안되는 것이다. Voter 클래스 객체를 3개 만들어서 등록했는데 2개는 ACCESS_GRANTED, 1개는 ACCESS_DENIED를 return 하면 ACCESS_DENIED가 1개라도 나왔기 때문에 최종적으로는 접근을 거부하는 것이다.

 

마지막으로 ConsensusBased 클래스는 다수결로 결정하는 방법을 택한다. 즉 ACCESS_GRANTED가 ACCESS_DENIED 보다 많을 경우는 접근 허가, 반대의 경우는 접근 거부로 결정한다. 이러한 결과가 동수일수도 있다. 즉 ACCESS_GRANTED 와 ACCESS_DENIED가 동수일 경우 기본 셋팅은 접근 허가로 결정한다(이 부분은 ConsensusBased 클래스의 allowIfEqualGrantedDeniedDecisions 프로퍼티 값을 false(기본은 true)로 설정하면 접근 거부로 결정한다)

 

이 글의 맨 처음에 얘기했던 AffirmativeBased, UnanimousBased, ConsensusBased 클래스의 기능이 맘에 안들수도 있다. 즉 Voter에 우선순위를 매겨서 이를 이용해 작업하고 싶을수도 있을 것이다. Voter 객체를 등록할 때 List에 담아두기 땜에 List의 index가 가장 낮은 것이 우선순위를 높게 해서 voter의 우선순위에 따라 최종 결정을 내리고 싶은 맘도 있을 것이다. 그런 기능을 어떻게 구현할까? 간단하다. 위에서 3개의 클래스를 설명할 때 이 3개의 클래스는 AbstractAccessDecisionManager 클래스를 상속 받았다고 언급한적이 있다. 이 AbstractAccessDecisionManager 클래스를 상속 받아 decide 메소드를 override 하면 된다. 3개의 클래스 소스를 참고삼아서 decide 메소드를 override 해서 구현하면 충분히 가능하다.

 

RoleVoter 선언한 부분을 보면 rolePrefix에 value를 빈 문자열을 설정한 부분이 있다. RoleVoter는 권한 앞에 특정 문자열을 붙인다. 기본적으로 붙이는 문자열이 ROLE_ 인데 이것을 빈 문자열로 설정하도록 하여 권한 앞에 특정 문자열을 붙이지 않도록 했다. 그래서 내가 설정한 권한 명칭 그대로 비교하도록 한다.

 

지금까지 Spring Security의 자원 접근 판단에 대한 설명을 했다. 이것을 설명해야 이 원리를 이해하고 DB를 이용한 접근 권한 판단 작업을 할 수 있기 때문이다. 지금까지의 설명을 한 줄로 정리하자면 여러개의 Voter 객체들이 내린 결론들을 AbstractAccessDecisionManager 클래스를 상속받은 클래스 객체가 모아놓은 후 이 객체가 자신만의 기준으로 종합적으로 판단해서 결정을 한다라고 할 수 있다(이렇게 한줄로 설명하기 위해 나열한 내용이..ㅠㅠ..) 다음 글에서는 이런 판단을 하는 과정에서 <intercept-url> 태그를 이용해서 자원에 대한 접근 권한 설정으로 하는 것이 아닌 DB를 이용해서 하는 방법으로 설명하도록 하겠다.

 

추가 설명의 글..

중간에 끼워넣기가 좀 어정쩡해서 마지막에 한꺼번에 다루기로 했다.

 

사실 위에서 이제껏 설명했던 내용은 다음에 다룰 DB를 이용해서 하는 방법을 위한 이해땜에 먼저 이렇게 설명을 했다. 그러나 우리가 이런 설정(voter 나 accessDesicionManager 등)을 이제껏 설정하지 않았는데도 잘 이뤄지고 있는 걸 보면 Spring Security가 기본적으로 이런 내용을 등록해주는 것이 있다는 것을 의미한다. 그래서 이 부분에 대해 짤막하게 다뤄보도록 하겠다

 

FilterSecurityInterceptor 클래스는 자동으로 올라오는 Filter 클래스이기 때문에 이것에 대해서는 따로 할말이 없다. 문제는 여기에 설정되는 인증 정보(Authentication Manager), 대상 정보(Security MetaDataSource), 판단 주체(Access Manager) 이다. 무엇이 설정되는가?

 

FilterSecurityInterceptor 클래스의 인증 정보로 org.springsecurity.security.authentication.ProviderManager 클래스 객체가 셋팅이 된다. ProviderManager 클래스 객체는 그러면 무슨 근거로 셋팅되는가? 우리가 설정 파일에서 보면 <authentication-manager> 태그를 설정한 것이 있다. 이 태그가 설정이 되면 ProviderManager 클래스 객체가 만들어지게 된다. 이 클래스는 생성자로 org.springframework.security.authentication.AuthenticationProvider 인터페이스를 구현한 객체들이 들어있는 List 인터페이스 구현 객체(List<AuthenticationProvider>)를 받도록 되어 있다. <authentication-manager> 태그의 하위 태그로 <authentication-provider> 태그를 설정했었다. 이 <authentication-provider> 태그 설정시 AuthenticationProvider 인터페이스를 구현한 클래스 객체가 만들어지는데 이때 태그에 ref 속성을 이용해서 AuthenticationProvider 인터페이스를 구현한 bean을 별도로 지정하지 않으면 내부적으로 org.springframework.security.authentication.dao.DaoAuthenticationProvider 클래스 객체가 만들어진다. 이 DaoAuthenticationProvider 클래스에 우리가 DB에서 사용자 정보를 조회하기 위해 만들었던 서비스 클래스 객체가 셋팅이 된다. 또 <authentication-provider> 태그는 여러개가 들어갈 수 있다. 이런 설정으로 List<AuthenticationProvider> 객체를 만들수가 있게 되는 것이다.(Spring Security가 제공하는 AuthenticationProvider 인터페이스를 구현한 클래스는 17개가 제공된다. 대부분의 상황에서는 이렇게 제공되는 클래스로 해결할 수 있을 것이나 제공되는 걸로 해결이 안될 경우 기존 클래스 소스를 참고하여 만들어도 된다)

 

대상 정보로 셋팅이 되는 것은 우리가 <http> 태그의 하위 태그로 <intercept-url> 태그에 설정한 값들로 구성된 객체가 설정이 된다. Spring Security에서는 내부적으로 org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource 클래스 객체가 셋팅이 되는데 이 객체를 살펴볼 필요가 있다. 이 ExpressionBasedFilterInvocationSecurityMetadataSource는 org.springframework.security.web.access.expression.DefaultFilterInvocationSecurityMetadataSource 클래스를 상속받고 있는데 이 DefaultFilterInvocationSecurityMetaDataSource에 있는 멤버변수 중 Map<RequestMatcher, Collection<ConfigAttribute>> 타입의 requestMap이란게 있다. ExpressionBasedFilterInvocationSecurityMetadataSource 클래스는 <intercept-url> 태그에 설정되어 있는 내용을 읽어다가 이를 LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> 타입의 객체로 만들어서 requestMap 멤버변수에 넣게 된다. 예전에 썼던 글에서 <intercept-url> 태그 설정시 주의 사항을 하나 준 것을 기억할 것이다. 디테일한 설정을 먼저 기록하고 러프한 설정을 맨 나중에 기록하라고 했던 적이 있다. LinkedHashMap은 Map 타입의 자료구조이지만 한편으로는 Linked 자료구조이기 때문에 order 개념도 가질수가 있다. 즉 Map에 저장된 elemement 들의 이전,이후 element와도 연결관계를 가지고 있기 때문에 order 개념도 가질수가 있는것이다. 실제 LinkedHashMap에서 keyset를 구해와서 살펴보면 LinkedHashMap에 넣은 순서대로 key들을 가져오는 것을 볼 수 있다. RequestMatcher 타입으로 element의 key를 셋팅하고 있는데 우리는 <intercept-url> 태그 구성시 Ant Pattern을 사용하고 있기 때문에 org.springframework.security.web.util.matcher.AntPathRequestMatcher 타입의 객체가 key로 셋팅된다. 이 AntPathRequestMatcher 객체는 내부에 pattern을 저장할수가 있는데 이 패턴이 Ant 패턴 문자열로 입력한 URI(ex: /admin/**)가 들어가게 된다. key가 들어가고 있으니 이 key를 이용해서 가져올 수 있는 value 가 당연 저장되고 있을 것이다. Collection<ConfigAttribute> 타입으로 들어가게 되는 value로 들어가지는 것은 org.springframework.security.web.access.expression.WebExpressionConfigAttribute 클래스 객체가 들어있는 ArrayList 객체가 value로 들어가지게 된다(WebExpressionConfigAttibute는 Spring Security API 문서에서는 발견할수가 없는데 이 클래스가 public class가 아니어서 그런듯 하다) 이 WebExpressionConfigAttibute 클래스 객체에 들어가지는 것은 우리가 <intercept-url> 태그 사용시 spel을 이용해서 권한을 표시한 내용이 들어가지게 된다(ex:hasRole("ADMIN")) 방금 설명한 key와 value를 매핑시켜보자. 그러면 해당 AntPathRequestMatcher 타입 객체의 key는 WebExpressionConfigAttribute 객체들로 표현되는 권한을 만족해야 접근할 수 있다고 설정되어 질 수 있지 않겠는가? 물론 이것에 대한 판단은 곧 이어 설명할 판단 주체(Access Manager)가 판단하게 될 것이다.

 

판단 주체로 셋팅되는 것은 이 글에서 설명했던 클래스 중 하나인 AffirmativeBased 클래스 객체가 셋팅된다. 이 클래스는 당연 거수기 역할을 하는 하나 이상의 Voter를 가지고 있을 것은 당연하다. 그러면 여기에 셋팅된 Voter가 무엇인지 알아봐야 할 것이다. 여기에 셋팅된 Voter 객체는 org.springframework.security.web.access.expression.WebExpressionVoter 클래스 객체가 셋팅되어서 이 클래스의 vote 메소드가 접근여부 판단을 결정한다. Voter가 1개만 등록되는데다가 AffrimativeBased 클래스의 성격을 생각해볼때 이 Voter가 접근을 허가하게 되면 최종적으로 접근이 허가 되는 것을 알 수가 있다.

 

기본 셋팅 정보를 설명한 이유는 이렇게 셋팅되는 것을 알아야 커스터마이징 포인트를 이해하는데도 쉽기 때문이었다. 그리고 대상정보 설명시 잠깐 언급했던 requestMap의 경우 다음 글에서도 사용할 부분이어서 이 부분에 대한 설명을 하지 않으면 다음의 글을 이해할수가 없을것 같아서 설명을 달았다. 별도로 빼기에는 글의 내용이 작아서 추가의 글로 마지막에 달았다.