본문 바로가기
Spring

Spring Framework : Spring Security 권한설정 DB연동하기

by autumnly 2017. 6. 5.


Spring Security의 FilterSecurityInterceptor 클래스에 설정되는 것은 세 가지가 있다.

1. 인증 정보 - Authentication Manager

2. 대상 정보 - Security MetadataSource

3. 판단 주체 - AccessManager


DB를 이용한 자원접근을 위해선 기본 설정을 바꿔야 한다.

인증 정보는 <authentication-manager>태그로 만들어지고 FilterSecurityInterceptor 클래스에 설정되기 때문에 문제가 없지만, 

대상 정보는 <intercept-url> 태그에서 읽어오는 것이 아닌 DB에서 읽어오는 것으로 바꿔주어야 한다.


판단 주체 또한 기본설정된 Voter가 DB에서 읽어온 값으로 대상 정보를 판단하도록 커스터마이징이 필요하다.



기본 설정된 대상 정보 클래스는

org.springframework.security.web.access.expression.ExpressionBasedFilterInvocationSecurityMetadataSource

이다.

이 클래스는

org.springframework.security.web.access.expression.DefaultFilterInvocationSecurityMetadataSource

클래스를 상속받고 있으며 이 클래스는 requestMap이라는 멤버변수를 가지고 있다.



따라서 우리가 해야할 일은,

DefaultFilterInvocationSecurityMetadataSource 클래스와 같은 구조의 클래스 또는 이 클래스를 상속받는 클래스를 만들고

requestMap 변수를 만들어주고 이를 DB에서 조회한 내용으로 셋팅해주는 것이다.


또한 관리자 페이지에서 자원별 접근 권한을 수정, 조회할 수 있도록

수정된 권한 내용을 반영하는 서비스와 조회하는 서비스를 만들어야 한다.







여기서는 DefaultFilterInvocationSecurityMetadataSource 클래스와 같은 구조의 클래스를 만들어 보겠다.

위 클래스는 org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource 인터페이스를 구현하고 있다. 그리고 생성자로 LinkedHashMap<RequestMatcher, Collection<ConfigAttributes>> 객체를 받아 이를 requestMap 멤버변수에 셋팅해주고 있다.


따라서 우리가 만들 클래스도 해당 인터페이스를 구현하며 생성자를 통해 requestMap 멤버변수를 셋팅해주면 된다.


그렇다면 LinkedHashMap<RequestMatcher, Collection<ConfigAttributes>> 객체에는 어떤 정보를 채워야 할까?

Ant방식의 url pattern과 spel로 설정된 권한이 필요하다.

우리는 DB에서 이 url패턴과 접근권한을 DB에서 조회한 뒤 

org.springframework.security.web.util.matcher.AntPathRequestMatcher 클래스 객체를 key로 하고 

Spring에서 제공하는 권한 클래스 객체가 들어있는 List를 value로 갖는

LinkedHashMap을 만들면 된다.


이를 위해 Dao, Service가 필요하다.

SecuredObjectDao

SecuredObjectService

SecuredObjectServiceImpl



SecuredObjectDao

getRolesAndResources 메소드에서는 쿼리를 실행해 나온 결과를 LinkedHashMap 타입(reaultMap)으로 만들어주는 일을 한다.

이렇게 LinkedHashMap을 만들었다면 이 DAO를 Injection 받아 LinkedHashMap을 가져오는 bean이 있어야 할 것이다.

그것이 SecuredObjectService와 이를 구현한 SecuredObjectServiceImpl 클래스이다.


SecuredObjectServiceImpl

이 클래스에서는 방금 만들었던 SecuredObjectDao bean 객체를 setter를 통해 Injection 받을 수 있다.

그리고 getRolesAndUrl() 메소드에서 SecuredObjectDao bean 객체의 getRolesAndUrl() 메소드를 호출해서 그 결과를 return 해준다.



그리고 이제 requestMap을 전문으로 만드는 bean(UrlResourcesMapFactoryBean 클래스)을 하나 제작할 것이다. 일종의 wrapper를 덮어씌우는 것인데 이렇게 하면 메소드나 포인트컷에 대한 조회도 가능하게끔 확장성을 가질 수 있다.


UrlResourcesMapFactoryBean 클래스는 FactoryBean 이다.

다시말해서 이 클래스를 <bean> 태그를 이용해 설정한 뒤 다른 <bean> 태그에서 이를 참조하게 되면

이 클래스가 참조되는 것이 아니라 이 클래스의 getObject() 메소드를 통해 return되는 객체가 참조되는 것이다.

(이 경우에는 LinkedHashMap 타입의 객체가 참조되는 것이다)


우리는 위에서 만든 SecuredObjectServiceImpl 클래스를 이 FactoryBean에 Injection 시킬 수 있다.

그리고 getObject() 메소드에서 Injection 받은 SecuredObjectServiceImpl bean 의 getRolesAndUrl() 메소드를 호출함으로써

DB에서 얻어온 결과인 LinkedHashMap 클래스 객체를 return하게 되는 것이다.


이제 대상 정보 클래스를 만들기 위해 선작업 해야 할 것들을 다 만들었다.

이제 대상 정보 클래스를 만들어보자.

DefaultFilterInvocationSecurityMetadataSource 와 같은 구조의 클래스인

ReloadableFilterInvocationSecurityMetadataSource 를 만든다.


ReloadableFilterInvocationSecurityMetadataSource 클래스는 requestMap과 SecuredObjectServiceImpl 클래스를 Injection 받아서 작업하게 된다.

requestMap 을 Injection 받을 때는 방금 설명했던 FactoryBean 을 Injection 받아서 받게된다.


ReloadableFilterInvocationSecurityMetadataSource 클래스의 reload() 메소드를 호출하면 

기존에 셋팅된 requestMap을 지운 뒤 Injection 받은 SecuredObjectServiceImpl 클래스에서 다시 requestMap 을 조회해서 셋팅하게 된다.

만약 관리자 페이지에서 권한을 수정할 시, 수정 뒤 이 클래스의 reload() 메소드를 호출하게 되면 DB에서 다시 requestMap을 만들어 셋팅하게 된다.



참고 : http://zgundam.tistory.com/50