Spring Security - URL 권한을 DB로 관리하기 2.

Posted at 2010.07.18 10:10 // in OpenSource // by MOOVA 무바㏇
필자는 그동안 경험했던 정보나 팁들, 또는 지식을 언제부터인가 온라인보다 오프라인으로 공유하는 것을 즐겨했습니다.
상당수가 프로젝트 보안이 걸려있는 문제이기도 하겠지요, 그 모든 부분을 온라인에 공유할 수 없는 심정, 이루 말할 수 없겠죠?
오픈소스와 관련된 사항도 프로젝트와 연관이 있으면 보안이라는 타이틀이 걸려있습니다. 하지만 공개할 것은 공개하자라는 근본 원칙은 항상 마음속에 간직하고 있습니다.
"버려야 새로운 것을 얻는다." 바로 이 정신. 참 아릅답습니다.~~


그림1. 커스터마이징 할 filterSecurityInterceptor의 인터페이스와 클래스 사용도입니다.


그림2. FilterSecurityInterceptor가 의존하는 두개의 매니저 클래스 ( Authentication Manager, AccessDecision Manager )

FilterSecurityInterceptor는 Spring Security의 중요 필터중 하나입니다. FilterSecurityInterceptor는 AuthenticationManager를 의존하며 User와 관련된 (인증 Authentication) 프로세스를 수행하고, 동시에 수행될 자원에 인가여부를 따지는 AccessDecisionManager를 의존하여 투표를 실시합니다.

<beans:bean id="filterSecurityInterceptor"
class="org.springframework.security.intercept.web.FilterSecurityInterceptor"
autowire="byType">
<custom-filter before="FILTER_SECURITY_INTERCEPTOR" />
<beans:property name="authenticationManager" ref="authenticationManager"></beans:property>
<beans:property name="accessDecisionManager" ref="accessDecisionManager" />
<beans:property name="objectDefinitionSource" >
                    <security:intercept-url pattern="/secure/super/**" access="ROLE_WE_DONT_HAVE"/>
                    <security:intercept-url pattern="/secure/**" access="ROLE_SUPERVISOR,ROLE_TELLER"/>
             </beans:property>
</beans:bean>

FilterSecurityInterceptor의 objectDefinitionSource필드를 커스터마이징 해야합니다. 바로 이 부분이 앞에서 모델링한 테이블과 연동을 해야 할 부분입니다. 위의 objectDefinitionSource를 다음과 같이 바꾸어 줍니다.

<beans:property name="objectDefinitionSource"  ref="coolInvocationDefinitionSource"/>

objectDefinitionsource에 reference될 bean을 등록해야합니다.

<beans:bean id="coolInvocationDefinitionSource"
class="com.moova.secure.filterInvocation.CoolObjectDefinitionSourceFactoryBean">
<beans:property name="dataSource" ref="springSecurityDataSource" />
<beans:property name="resourceQuery"
value="
                                 SELECT URL.URL_SPRING, R.NAME
FROM ROLE ROLE JOIN URL_ROLE UR ON ROLE.ID = UR.ROLE_ID JOIN PROGRAM ON PROGRAM.ID = UR.PROGRAM_ID JOIN URL_REPOSITORY REPO ON PROGRAM.ID = REPO.ID
        " />
</beans:bean>

FilterSecurityInterceptor의 objectDefinitionSource의 API를 살펴보면 다음과 같이 setter injection으로 되어 있는 것을 볼 수 있습니다.

Spring Security 2.0

Spring Security 3.0
 SecurityMetadataSource obtainSecurityMetadataSource() 
           
 void setObjectDefinitionSource(FilterInvocationSecurityMetadataSource newSource) 
          Deprecated. use setSecurityMetadataSource instead
 void setObserveOncePerRequest(boolean observeOncePerRequest) 
           
 void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource newSource) 

Spring Security 2.0 과 3.0 API를 살펴보면 크게 변화된 부분이 바로 이 objectDefinitionSource입니다. 3.0에선 objectDefinitionSource가 deplecated되었습니다. 이것이 securitymetadataSource로 변경이 되어 있네요. 2.0의 objectDefinitionSource의 Type은 FilterInvocationDefinitionSource 이고 3.0의 objectDefinitionSource의 Type은 FilterInvocationSecurityMetadataSourcen 인터페이스입니다.

이를 확인하고 각 버전에 따라 작성해야하는 클래스가 다를 수 있다는 것에 주목해 주십시오. 여기선 그대로 objectDefinitionSource를 사용합니다.

새로 제작할 CoolObjectDefinitionSourceFactoryBean는 FactoryBean을 implements합니다. FactoryBean을 implements 하면 어떤 객체라도 Spring lifecycle안에서 DI할 수 있게 해줍니다. 이는 단순 객체가 아닌 Factory 클래스로써 getObject()와 getObjectType()를 구현해 주기만 하면 새롭게 사용할 클래스를 반환시켜줍니다. 추가로 데이터베이스와 연계가 필요하니  "org.springframework.jdbc.core.support.JdbcDaoSupport"를 상속합니다.

사용할 구조

public class CoolObjectDefinitionSourceFactoryBean extends JdbcDaoSupport implements FactoryBean {
private String resourceQuery;

public boolean isSingleton() {
return true;
}

public Class getObjectType() {
return FilterInvocationDefinitionSource.class;
}

public Object getObject() {
return new DefaultFilterInvocationDefinitionSource(
this.getUrlMatcher(), this.buildRequestMap());
}
}

데이터베이스 연계부분은 MappingSqlQuery을 사용합니다. MappingSqlQuery 사용법은 여기를 참조해 주세요.

private class UrlRepository {
private String url;
private String role;

public UrlRepository(String url, String role) {
this.url = url;
this.role = role;
}

public String getUrl() {
return url;
}

public String getRole() {
return role;
}
}

private class UrlRepositoryMapping extends MappingSqlQuery {
protected UrlRepositoryMapping(DataSource dataSource, String resourceQuery) {
super(dataSource, resourceQuery);
compile();
}

protected Object mapRow(ResultSet rs, int rownum) throws SQLException {
String url = rs.getString(1);
String role = rs.getString(2);
UrlRepository resource = new UrlRepository(url, role);

return resource;
}
}

"com.moova.secure.filterInvocation.CoolFilterInvocationDefinitionSourceFactoryBean"의 원본 파일

더보기




executeResourceMap()메소드에서 URL과 ROLE 관련된 매핑부분을 key와 Value로 조합해야 합니다. 
DefaultFilterInvocationDefinitionSource의 두 번째 인자는 LinkedHashMap입니다. 
당연히 HampMap값을 넘겨주어야 하기 때문에 buildRequestMap에서는 LinkedHashMap를 new해서 반환해 주고 있습니다. 
DefaultFilterInvocationDefinitionSource의 첫 번째 인자는 AntUrlPathMatcher를 new해서 주입하고 있습니다.

스키마를 새로 개정하거나 생성해서 사용해도 무방합니다.
하지만 URL과 Role에 대한 모델링 연관은 그다지 바뀔게 없다고 봅니다.


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
블로그코리아에 블UP하기

댓글을 남겨주세요.

Spring Security - URL 권한을 DB로 관리하기 1.

Posted at 2010.07.18 10:08 // in OpenSource // by MOOVA 무바㏇
필자는 오픈소스나 자/타 제품을 접할 때 먼저 해당 제품의 생명주기(LifeCycle)을 먼저 파악합니다. 서로 연결된 인터페이스나 외부에 공개된 컴포넌트의 연관관계를 파악하고, 자주 사용하는 기능이나 중요하다고 생각되는 기능이 생명주기에 의존되어 있는지를 파악합니다. 전체 생명주기나 구조적 관점을 늘 중요시 하다보면, 치명적인 이슈를 제외한 세부적인 기술이슈까지도  그때 그때 처리할 수 있으리라 봅니다.
<http auto-config='true'>
    <intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" /> 
    <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />
    <intercept-url pattern="/**" access="ROLE_USER" />
    <form-login/>
    <logout logout-success-url="/pages/logout-redirect.jsp"
invalidate-session="true" />
    <remember-me key="moovaSecureRMKey" user-service-ref="userDetailsService" />
</http>


Spring Security에서 url path에 대한 권한 부여는 보통 위와 같이 설정을 해야합니다. (굵게 표기한 부분)
( 권한을 가지고 있는 사용자가 요청한 url 패턴을 기준으로, 해당 자원을 접속할 수 있는지를 따지게 하여 주는 설정입니다. ) 하지만 안타깝게도 보통 실무에서는, url path나 문서 조회권한에 관련된 설정 파일은 따로 관리하지 않습니다. 

User,Member,Group등 중요 인증 및 인가에 관련된 테이블을 데이터베이스로 관리한다면 URL 권한도 데이터베이스로 관리되어야 차 후 유지보수나 확장에도 이점을 얻을 수 있습니다. Spring Security에선 위의 intercept-url설정이 기본기능으로 구성되어 있습니다만, 이것을 데이터베이스로의 관리방법으로 변경해 보도록 할까 합니다.

하고자 하는것은 매우 단순합니다. Spring Security의 중요 Filter중 하나인 FilterSecurityInterceptor의 objectDefinitionSource를 커스터마이징 할 objectDefinitionSource로 바꿔치기하는 것입니다.



1. 테이블 구축


그림1. 실제 실무에서 사용된 인증/인가와 관련된 테이블 ( 축소공개형 ) 



실제 Magpie 오픈소스로 구축중인 테이블 스키마의 축소 일부입니다. 
User와 Role N:N관계, Role과 Program도 N:N관계, Program과 Url_Repository는 1:1관계로 되어 있습니다.
Role과 Url_Repository를 N:N관계로 모델링 해도 무방하지만 보통 URL은 동적인 속성이 강한 부분이니 카테고리로 분류할 수 있는 Program이란 Entity를 따로 생성하여 url port같은 기능을 하도록 설정해 두었습니다.

사용해둘 데이터를 미리 뽑아 보고, 기존 설정에서 사용했던 포멧으로 데이터가 축출 가능한지 확인합니다.


그림 2. 기존 Spring Definition 설정 위치와 비교한 데이터의 위치.

2. 데이터 확인 및 데이터 추출
SELECT URL.URL_SPRING, R.NAME FROM ROLE ROLE JOIN URL_ROLE UR ON ROLE.ID = UR.ROLE_ID JOIN PROGRAM ON PROGRAM.ID = UR.PROGRAM_ID JOIN URL_REPOSITORY REPO ON PROGRAM.ID = REPO.ID RESULT : ROLE_USER | /** ROLE_ANONYMOUSLY | /** ROLE_ADMIN | /admin.jsp ROLE_USER | /login.jsp ROLE_ANONYMOUSLY | /login.jsp ROLE_MANAGER | /manager/index.jsp
"Spring Security URL 권한을 DB로 관리하기"에서 사전 준비해야 할 재료는 모두 준비된 상태입니다. 이제 Spring Security의 일부분을 커스터마이징해야 합니다. ( "org.springframework.security.intercept.web.FilterSecurityInterceptor" )

참고 문서 :  
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/springsecurity.pdf

저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

'OpenSource' 카테고리의 다른 글

근무환경  (2) 2010.08.10
Spring Security - URL 권한을 DB로 관리하기 2.  (0) 2010.07.18
Spring Security - URL 권한을 DB로 관리하기 1.  (0) 2010.07.18
심심풀이 납품용  (2) 2010.06.05
[Guide] Vaadin Korean Guide (ver01)  (3) 2010.05.11
[Share Document Note] Evernote  (0) 2010.01.24
블로그코리아에 블UP하기

댓글을 남겨주세요.

티스토리 툴바