Whiteship's Note


스프링 시큐리티 맞춤확장(customization) - 파트 1. UserDetail 또는 GrantedAuthority 맞추기



참조 및 번역: http://blog.springsource.com/2009/01/02/spring-security-customization-part-1-customizing-userdetails-or-extending-grantedauthority/

이번 글은 스프링 시큐리티 맞춤확장과 관련된 실용적인 예제 중심의 여러 작은 글들의 시리즈 중 첫 번째 글이다. 이번 맞춤확장 요구 사항은 상상에서 온 것이 아니라 전부 현장에서 요구한 것이다.

다음과 같은 요구사항이 있다고 가정해보자. 역할(role) 목록이 있고 각각의 역할은 비즈니스 기능(business function) 목록을 가지고 있다.(아래를 참조하라.)

ROLE_ADMIN
  BF_QUOTE_CREATE
  BF_POLICY_CREATE
  BF_POLICY_DELETE

ROLE_AGENT
  BF_QUOTE_CREATE
  BF_POLICY_CREATE

ROLE_USER
  BF_QUOTE_CREATE

필요한 기술은 권한 결정을 ROLE과 BF 모두를 가지고 결정할 수 있어야 한다는 것이다.

예를 들어:

ROLE_ADMIN이라는 역할을 가진 사용자는 해당 역할로 보호하고 있는 모든 리소스에 접근할 수 있어야 한다.

<sec:authorize ifAllGranted="ROLE_ADMIN">
    <p><a href="http://www.google.com">Google</a>
</sec:authorize>

또는

@Secured("ROLE_ADMIN")
public void foo()
    . . .
}

또한 해당 사용자는 해당 역할이 가지고 있는 비즈니스 기능으로 제한하고 있는 모든 리소스에도 접근할 수 있어야 한다.

<sec:authorize ifAllGranted="BF_POLICY_DELETE">
    <p><a href="http://www.google.com">Google</a>
</sec:authorize>

또는

@Secured("BF_POLICY_DELETE")
public void foo()
    . . .
}

이 요구사항을 다루는 방법은 몇 가지가 있다. 그 중 하나는 RoleHierarchy를 만들고 RoleHierarchyVoter를 사용하여 역할의 계층 구조를 순회하는 것이다. 이 접근 방법의 단점은 현재 스프링 시큐리티 2.0.4 구현체의 태그 라이브러리(security: authority ...)가 AccessDecisionManager를 통해서 의사 결정을 하고 있지 않으며 게다가 어떤 Voter도 제한하고 있는 HTML 엘리먼트에 대한 결정을 할 때 Role을 신경쓰지 않는다. 하지만 스프링 시큐리티의 놀라운 유연함과 맞춤확장 힘으로 인해 이 요구사항을 매우 간단하게 해결할 수 있다.
스프링 시큐리티의 가장 큰 잇점 중 하나는 어떻게 Principal(UserDetail 객체)을 생성하는지 맞춤확장 할 수 있다는 것이다. UserDetail 객체를 만들 때 GrantedAutorities 목록을 생성한다. 이 목록은 나중에 리소스를 제한하고 있는 GrantedAutority와 비교해볼 때 사용된다.
맞춤확장을 하는 또 한 가지 방법은 UserDetail 객체를 생성할 때 GrantedAuthorities 목록을 수정하는 것이다.

아래에서 제공하고 있는 예제에는 두 개의 프로퍼티 파일이 있다.(간단하게 하려고 프로퍼티 파일을 사용했지만, 여러분은 쉽게 그것을 DB나 LDAP으로 바꿀 수 있을 것이다.)

파일 하나는 users.properties로 사용자를 role에 맵핑한다.

oleg=powder,ROLE_ADMIN

다른 파일 하나는 role-to-db.properties로 각각의 role에 비즈니스 기능 목록을 맵핑한다.

ROLE_ADMIN=BF_QUOTE_CREATE,BF_POLICY_CREATE,BF_POLICY_DELETE

이제 필요한 작업은 UserDetailsService 구현체를 정의하여 위 두 개의 프로퍼티 파일을 사용하여 GrantedAUthorities 목록을 만들고 그것을 UserDetails 객체에 주입하는 것이다. 이 작업은 GrantedAuthorityImpl같은 GrantedAuthority 인터페이스 구현체를 재사용할 수 있으며 매우 간단하다. 하지만, 우리는 이때 (디버깅 또는 다른 의도로) 비즈니스 기능을 표현하는 GrantedAuthority의 상위(parent) GrantedAuthority를 추적할 수 있도록 하고 싶다.
이 두 가지 목적을 달성하기위해 GrantedAuthorityImpl을 확장하는 BusinessFunctionGrantedAuthority 클래스를 정의하고 모든 상위 GrantedAuthority 객체에 대한 목록을 담고 있게 했다.

public class BusinessFunctionGrantedAuthority extends GrantedAuthorityImpl {
    private List<GrantedAuthority> parentAuthorities;
        . . .
}

그런 다음 UserDetailsService 구현체를 만들었고 loadUserByNames(..) 메소드를 구현하여 다음 작업을 수행하도록 했다.

1. users.properties 파일 내용을 기반하여 UserAttribute 객체를 만든다. UserAttribute는 role을 나타내는 GrantedAuthority 목록을 담고 있다.
2. role-GrantedAuthorities 목록을 순회하면서 각각의 role-GrantedAuthorities 마다 BusinessFunctionGrantedAuthority를 만들고 그것을 미리 만들어둔 GrantedAuthority 목록에 추가한다.
  2-1. 각각의 BusinessFunctionGrantedAuthority에 parent GrantedAuthority를 추가한다.
3. 전체 GrantedAuthority 목록을 가지고 있는 최종 UserDetail 객체를 만든다.

그런 다음 AuthenticationProvider를 스프링 시큐리티 설정 파일에 정의한다.

사용자 삽입 이미지

노트: AuthenticationProvider에 커스텀 UserDetailService 구현체 ComplexAuthorityUserDetailsService 클래스(더 자세한 내용은 샘플 코드를 살펴보라.)를 주입했다.

리소스를 보안하고 배포한 다음 http://localhost:8080/spring-security-sample-grantedAuthority/index.jsp에 접속하라.

로그인한 뒤 여러분은 GrantedAuthorities 목록을 볼 수 있을 것이다.

사용자 삽입 이미지

여기서 여러분은 비즈니스 기능을 나타내는 GrantedAuthority가 해당 비즈니스 기능을 가지는 상위 GrantedAuthorities 목록도 보여주고 있다는 것을 확인할 수 있을 것이다.
index.jsp를 살펴보고 어떻게 security:authorize 태그를 사용ㅇ하여 role과 비즈니스 기능 목록을 사용하여 HTML 엘리먼트를 제한하는지 살펴보라
이게 끝이다. 여러분은 최소한의 맞춤확장으로 쉽게 Principal 구조를 확장하고 커스터마이징 할 수 있는지 살펴보았다. 그리고 이것을 비즈니스 코드를 더럽히지 않고 커스텀 GrantedAuthorities에 기반하여 선언적으로 보안을 적용하는지 살펴보았다.

샘플 코드는 여기서 다운로드 할 수 있다. spring-security-sample-grantedauthority
top


Whiteship's Spring Security 총 정리

Spring Security/etc : 2008.05.07 18:16


기본 개념

1.1. What is Acegi Security? : Acegi 정의와 개요.
2.2 Shared Components : 주요 구성 요소
2.3 Authentication (2) : 인증 절차를 도식화 했습니다.
Spring Acegi Tutorial : 가장 중요한 필터들에 대한 설명과 해당 필터들이 물고 있는 빈들을 보여줍니다.
3.2 Filters : 전체 필터들 간단 요약
4. Channel Security : https 사용에 대한 개요
Access Decision Manager : 접근 권한 관리를 하는 핵심 컴포넌트

웃긴 이야기

왜 이름이 Acegi 인가? : AbCdEfGhI, 스프링 서브 프로젝트가 되기 위한 노력.
Acegi 필터 등록할 때 발생할 수 있는 몹쓸 버그 : 필터 체인 사용하던 시절에 발생하던 웃긴 버그
Spring Security 설정 분류 및 커스터마이징 (5) : Acegi 관련 XML이 커져서 어떻게 나눌지 고민했던 시절

Spring Security(Acegi) 2.0 이전

Acegi로 웹 애플리케이션 보안하기 1
Acegi로 웹 애플리케이션 보안하기 2
Acegi로 웹 애플리케이션 보안하기 3
Acegi로 웹 애플리케이션 보안하기 4
Acegi로 웹 애플리케이션 보안하기 5 (2)
Acegi로 웹 애플리케이션 보안하기 6
Acegi로 웹 애플리케이션 보안하기 7

Spring Security 2.0 네임스페이스

Security Namespace Configuration PART 1 (1)
Security Namespace Configuration PART 2

많이 미흡하지만 이 정도면 Spring Security 학습에 어느 정도 도움이 되실 거라고 생각합니다.
Live with Passion!!! Live with Spring!!
top


Spring Security 2 조만간 볼 수 있을 듯



참조 : http://blog.interface21.com/main/2007/12/06/whats-new-in-spring-security-2/

다음 주 쯤에 배포가 될 것 같습니다. 벌써부터 기다려지네요. Ben Alext가 올린 글을 보면, 설정이 상당히 간결해 진것 같은데, 너무 간결해져서 다시 공부해야 할 것 같습니다. 수 많은 필터들을 직관적인 엘리먼트와 속성이름을 사용해서 감춰주길 기대하고 있습니다.

대강 보니, 사용법은 일단 web.xml에 Filter To Chain Proxy를 등록하던 저번과 비슷한데, 대신 등록하는  클레스가 Filter To Chain Proxyr가 아니라 org.springframework.web.filter.DelegatingFilterProxy 로 바뀐 것 같습니다. 그리고 수 많은 필터를 빈으로 등록해야 했던 부분이 다음과 같은 한 덩어리로 뭉쳐진 것 같습니다.

    <http autoConfig="true">
        <intercept-url pattern="/**" access="IS_AUTHENTICATED_REMEMBER" />
    </http>

    <repository>
        <user-service hash="md5:hex">
            <user name="rod" password="a564de63c2d0da68cf47586ee05984d7" authorities="ROLE_SUPERVISOR,ROLE_USER" />
        </user-service>
    </repository>

TSE 2007에서 Spring Security와 관련된 세션이 세 개나 된다고 하는데, 내년에도 올해처럼 재밌는 발표들이 가득한 TSE가 되면 좋겠습니다.
top


Java에 OpenID 적용 관련 아티클

Good Tools : 2007.11.20 12:44


재밌겠군. Spring Security랑 엮어보고 싶은데 일단은 OpenID부터 파악해야지;;
top


Authentication Manager



사용자 삽입 이미지
Spring Security를 구성하는 다섯 가지 컴포넌트 중에 하나인 Authentication Manager는 인증을 담당하고 있습니다.

Authentication Manager는 인증을 시도(authenticate 메소드 호출)해서 성공하면, Authentication 객체를 반환하고, 실패하면, AuthenticationException 예외를 던지는 메소드를 가지고 있습니다.

이 인터페이스의 구현체로 Provider Manager를 제공하고 있으며, 이 클래스를 여러 인증 방식을 사용할 수 있는 관리자 역할을 하고 있습니다. Spring Security에서 제공하고 있는 인증 방식은 총 14가지 입니다. 그 중에서 가장 자주 사용할 것 같은 Provider는 DAO를 사용하여 인증 정보를 가져오는 DaoAuthenticationProvider입니다.

사용자 삽입 이미지
하늘색 선은 implements, 파란색 선은 extends, 빨간 선은 composition 입니다.
하늘색 네모는 인터페이스, 파란색 네모는 클래스입니다.

DaoAuthenticationProvider는 UserDetailsService 인터페이스를 통해서 사용자 정보(UserDetail)를 가져옵니다.

UserDetailsService 인터페이스를 직접 구현하여 설정해도 되지만, Spring Sercurity에서 제공하는 InMemoryDaoImpl 또는 JdbcDaoImpl을 사용할 수도 있습니다. DB에서 사용자 정보를 가져오려면, JdbcDaoImpl을 사용하는 것이 좋겠습니다.

JdbcDaoImpl에는 dataSource, usersByUsernameQuery, authoritiesByUsernameQuery 속성을 설정하여 DB로 부터 사용자 정보를 가져옵니다. usersByUsernameQuery에는 username, password, enabled를 SELECT하고, authoritiesByUsernameQuery에는 username과 authority를 SELECT 하는 쿼리를 설정해 줍니다.

사용자 삽입 이미지

좀 복잡해 보이지만, 철저하게 인터페이스 기반으로 작성되어 있으며, 역할을 분담해 놓은 모습이 매우 깔끔합니다.

bean 설정을 다음과 같이 할 수 있습니다.
사용자 삽입 이미지
설정 내용은 상당하지만, 자세히 살펴보시면 클레스 상속 구조와 일치하기 때문에, 쉽게 이해하실 수 있습니다.
사용자 삽입 이미지
특히 DaoAuthenticationProvider에는 passwordEncoder(비번 암호화)와 saltSource, userCache(사용자 정보 캐쉬 사용) 들을 설정할 수도 있습니다.

마지막으로 정리하자면, AuthenticationManager의 구현체로 ProviderManager가 있으며, 이 녀석은 여러 AuthenticationProvider를 사용할 수 있습니다.
그 중에서도 DaoAuthenticationProvider는 UserDetails를 사용하여 사용자 정보를 가져옵니다.
이 때 JdbcDaoImp 이라는 구현체를 사용하여 datasource를 사용하여 사용자 정보와 권한을 가져오는 SQL 쿼리를 설정해주면 끝.

참조 : Spring In Action 2nd
top


Spring Acegi Tutorial



참조 :  Spring Acegi Tutorial

Spring Security(Acegi Security)에 대한 맛보기용 아티클로 매우 적당한 것 같습니다. 먼저 Acegi에서 흔히 사용하는 용어에 대해 설명을 하고, 간단한 애플리케이션을 통해 실제 Acegi를 적용하여 구동하고 있는 녀석을 살펴본 뒤에, 해당 샘플에서 사용한 필터들과 각각의 필터들의 종속성을 살펴보고 있습니다. 개인적으로는 이러한 구성이 어렵고 생소한 프레임워크를 공부하는데 가장 좋은 것 같습니다.

Acegi에서 사용하는 용어
- Authentication(인증) : "너는 누구냐?"
    - principal(주체) : username
    - credentials(신용) : password
- Authorization(권한) : "뭐 할려고?"
    - object definitions : secured resources, 관계자외 출입 금지인 자원

샘플 애플리케이션  
사용자 삽입 이미지
- 두 링크 모두 로그인으로 인증을 하며, 이때 validation을 해서 에러페이지로 이동시킬 수 있습니다.
- 특정 url과 role을 맵핑한 정보를 사용하여 권한 체크를 합니다.

샘플에서 사용한 필터들과 그들의 종속성
사용자 삽입 이미지
사용자 삽입 이미지
사용자 삽입 이미지
예전에 토비님께서 작성하셨던 "나의 Spring Acegi Security Framework 학습기"에 보면 더 많은 필터들이 있지만 일단 여기서는 위의 예제에서 사용한 httpSessionContextIntegrationFilter, authenticationProcessingFilter, exceptionTranslationFilter, filterInvocationInterceptor 네 개만 살펴보겠습니다.

1. AuthenticationProcessingFilter(위 그림에서 formAuthenticationProcessingFilter)
Http Request가 지나가는 첫 번째 필터로 인증과 관련된 요청을 처리하는데 특화된 필터 입니다. 즉, 로그인과 관련된 요청에 특화 되었기 때문에 폼 서브밋 URL 값(filterProcessUrl 속성)이나 로그인 실패시 URL 값(authenticationFailureUrl 속성)들을 설정합니다.

2. HttpSessionContextIntegrationFilter
위에서 살펴보았던 AuthenticationProcessingFilter가 사용하고 있는 AuthenticationManager에 의해 만들어지고 관리되는 Authentication 객체를 Thread Local을 사용하여(servlet이 thread기반이라..) 감싸서(wrap) 해당 쓰레드 내의 여러 request에서 같은 Authentication 객체를 공유할 수 있도록 하는 역할을 합니다.

3. ExceptionTranslationFilter
security 시스템의 중추적인 역할을 하는 두 개의 필터 중에 하나이며(다른 하나는 FilterSecurityInterceptor), 인증과 권한에 관련되어 발생하는 예외(AcegiSecurityException)를 잡아서 다음의 두 가지 중 하나로 처리 합니다.
- Authentication 객체 부재로 인한 예외 일 경우 : AuthenticationEntryPoint 를 사용하여 사용자에게 로그인을 요구함.
- FilterSecurityInterceptor에 의해 던져진 권한이 없어서 발생한 예외 일 경우 : 브라우저에게 SC_FORBIDDEN (HTTP 403) 에러를 던집니다.

4. FilterSecurityInterceptor
secured resources에 대한 정의를 담고 있습니다. 이곳에서 실제 특정 URL과 role을 맵핑하는 정보인 objectDefinitionSource 속성을 설정하고 있습니다. 이 속성을 설정할 때는 두 개의 dialect를 지정해 줍니다. 이름만 봐도 대강 짐작이 가는 dialect기 때문에 설정 내용을 살펴보시면 됩니다.
사용자 삽입 이미지




top