Whiteship’s Spring Security 총 정리

기본 개념

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 학습에 어느 정도 도움이 되실 거라고 생각합니다.
Live with Passion!!! Live with Spring!!

Acegi로 웹 애플리케이션 보안하기 6

6. 예외 다루는 필터 등록하기

사용자 삽입 이미지
org.acegisecurity.ui.ExceptionTranslationFilter 를 등록해 줍니다. 그리고 이 필터에 authenticationEntryPoint 속성과 accessDeniedHandleerImpl 속성을 설정해 줍니다. authenticationEntryPoint 는 인증 예외가 발생했을 때 인증을 요구하는 페이지로 이동하도록 설정했으며, accessDeniedHandleerImpl 는 해당 사용자가 권한이 없을 요청을 했을 때 보여질 페이지를 설정했습니다.

7. 로그아웃 필터 등록하기

사용자 삽입 이미지이 필터는 생성자 인젝션을 사용했네요. 흠~ 독특합니다. 첫 번째 인자로는 로그아웃을 한 뒤 보여줄 URL을 설정하고, 두 번쨰 인자로는 로그아웃 핸들러를 등록해 주었습니다. 이 객체가 실제로 Session 객체에서 Security Context 객체를 제거하는 일을 담당할 것입니다.

위 두 개의 필터를 역시 FilterChainProxy에 등록해주면, 잘 동작하는 모습을 확인할 수 있습니다.

Acegi로 웹 애플리케이션 보안하기 5

5. HttpSessionIntegrationFilter 사용하기

앞에서 인증과 보안에 관련된 필터과 bean들을 모두 등록했지만, 애플리케이션은 동작하지 않습니다. 그 이유는 보안 정보가 여러 요청들 간에 유지되지 않기 때문입니다. 이 문제를 해결하기 위해 보안 정보를 Session에서 관리하는 필터를 등록해 줍니다.

    <bean id=”httpSessionContextIntegrationFilter”
        class=”org.acegisecurity.context.HttpSessionContextIntegrationFilter” />

이 필터는 다른 객체를 사용하지 않고 있어서 설정이 매우 간단합니다. 이렇게 등록한 다음 역시 다른 필터들과 마찬가지로 FilterChainProxy에 등록해 줍니다.
사용자 삽입 이미지이 필터를 제일 앞에 등록합니다. 그 이유는 이 녀석이 하는 일과 관련이 있습니다.

사용자 삽입 이미지그림은 IBM 기사에서 가져왔습니다. 먼저 Filter Chain Proxy에서 SIF를 호출하고 이 필터에게 요청을 넘깁니다.(1) 그럼 SIF는 이 요청이 이전에 다뤘던 요청인지 아닌지 판단합니다.(2) 만약에 이전에 다뤘던 요청이면 일을 끝내고 다음 필터로 요청을 넘깁니다.(4) 처음 다루는 요청이면 플래그를 설정하고(이미 다룬 요청이라는 것을 기억하기 위해) 세션 객체가 있는지 그리고 그 안에 보안 문맥(Security Context)를 담고 있는지 확인합니다. 있으면, 해당 보안 객체를 Security Context Holder에 넣어 둡니다. 만약 세션 객체가 없으면 새로운 보안 문맥을 생성하고 그것을 Security Context Holder에 넣어 둡니다.(3) 그런 다음 요청을 다음 필터로 넘깁니다.(4)

다른 필터들이 이 보안 문맥을 수정할 수 있습니다.(5) 모든 필터의 처리가 완료된 다음 SIF가 제어권을 가지게 됩니다.(6) 필터 체인 특성 상 그렇게 되어있습니다.
사용자 삽입 이미지만약 이 때 Security Context가 변경되어 있다면, 이 정보로 Session 객체있는 Security Context를 수정합니다.(7)

이제는 애플리케이션에 한 번 로그인 상태로 끝내더라도, 다음에 접속할 때 정보를 계속 유지하고 있게 됩니다. 즉, 서버를 꼈다 켜도 사용자 정보를 기억하고 있습니다. 신기하네요.

이제 사용자 정보를 유지하는 방법은 알아냈는데, 문제는 한 번 로그인 한 사용자의 정보를 Session에서 삭제하는 방법입니다. 계속해서 한 사용자로 로그인 되어 있으면 안 되겠죠. 로그아웃이 필요합니다.
그리고 사용자 정보가 없을 때 예외를 브라우저에 출력하지 말고 로그인 폼으로 이동하도록 하는 예외 처리가 필요합니다.

다음 글에서 예외 처리와 로그아웃 필터를 다루겠습니다.

Acegi로 웹 애플리케이션 보안하기 3

3. 인증하자.(보안된 정보에 접근할 때 로그인을 하도록…)

이제부터 이전 글에서 등록한 FilterChainPorxy에 필터를 하나씩 등록하면 됩니다. 그러려면 일단 필터를 bean으로 등록해야겠죠. 그리고 그 필터가 종속성을 가지는 객체들도 역시 bean으로 등록하면 됩니다.

인증을 하기 위해 Acegi에서 제공하고 있는 필터는 AuthenticationProcessingFilter입니다.
사용자 삽입 이미지이 필터는 authenticationManager를 사용하여 인증 처리를 하고 있습니다. 동작하는 원리는 IBM에 올라온 기사를 참조하면 자세히 설명되어 있습니다.
사용자 삽입 이미지간략히 설명을 하면, APF로 요청, 응답, 필터 체인 객체가 넘어옵니다.(1) 그 뒤에 APF에서 인증 토큰(username, password, 등 기타 요청 개체에 딸려온 정보)을 만들고(2), 이 것을 AuthenticationManager에 넘겨줍니다.(3)

그럼 이제, AuthenticationManager를 등록해야겠습니다.
사용자 삽입 이미지authenticationManager에서는 하나 이상의 authenticationProvider를 사용하여, 인증 작업을 처리하고 있습니다. manager는 여러 provider를 사용하여 어떤 provider가 APF로 부터 받은 인증 토큰을 지원하는지 확인합니다.(4) 그러려면 인증 토큰을 provider에게 보내야겠죠.(5)

authentication Provider는 manager로 부터 받은 인증 토큰에서 username을 꺼낸 다음, userChache가 등록되어 있다면 이 정보를 user cache service라는 녀석에게 넘깁니다.(6) 하지만 위의 예제는 아직 userChache를 사용하고 있지 않기 때문에, (7), (8)에 대한 설명은 생략하겠습니다.

userChache가 없거나, userChache에서 username으로 확인(9) 결과가 null 이면, 다시 username을 이번에는 user detail service에게 넘깁니다. (10)userDetailService는 뒷단(위의 예제에서는 프로퍼티 파일을 사용했네요.)에서 사용자 정보를 찾습니다.(11) 해당 username에 대한 사용자 정보를 찾아서 가져오거나, 그런 사용자가 없다는 예외를 발생시킬 것 입니다.(12)

이렇게해서 찾은 정보와 인증 토큰의 정보를 가지고 비번 확인을 합니다. 일치하면, 해당 사용자 정보를 다시 Authentication Manager에게 넘겨주고, 일치하지 않으면 예외를 발생시킵니다.(13) Authentication Manager는 다시 이 정보를 APF에게 잔달합니다.(14) 그러면 APF는 이 정보를 Security Context에 저장하고(15) 다음 필터를 호출합니다.(16)

자 이제 거의 다 끝났습니다.

마지막으로 할 일은 APF를 “Acegi로 웹 애플리케이션 보안하기 2″에서 만들었던 FilterChainProxy에 등록하는 일입니다.
사용자 삽입 이미지filterChainProxy의 filterInvocationDefinitionSource 속성에 위와 같이 설정해 줍니다. 모든 URL은 비교하기 전에 모두 소문자로 바꾸고, ANT 스타일의 표현식을 사용할 것이라는 설정과, 모든 url이 authenticationProcessingFilter를 거치도록 설정했습니다.

http://www.acegisecurity.org/acegi-security/apidocs/constant-values.html#org.acegisecurity.util.FilterChainProxy.TOKEN_NONE

위 링크에서 Acegi에서 사용할 수 있는 상수들을 참조할 수 있습니다.

자 이렇게 하면…
이제 인증(로그인)은 제대로 동작하지만, 여전히 인증을 하지 않고도 보안이 필요한 자원에 누구나(로그인을 하든 안하든 관계없이)접근할 수 있습니다.

Acegi로 웹 애플리케이션 보안하기 2

2. acegi 컨텍스트 파일 작성하고, 스프링 컨텍스트 리스너 등록하기.

일반적인 스프링 컨텍스트 파일을 작성합니다. Acegi 관련 bean 설정을 따로 모아두는 것이 관리에 용이할 것입니다. 따라서 새로운 파일을 작성합니다.

사용자 삽입 이미지
그리고 web.xml에서 참조할 FilterChainProxy bean을 위에서 생성한 파일에 등록합니다.

    <bean id=”filterChainProxy”
        class=”org.acegisecurity.util.FilterChainProxy”>
    </bean>

그리고 다시 web.xml로 돌아갑니다. 위에서 작성한 스프링 애플리케이션 컨텍스트 파일을 스프링이 사용할 수 있도록 설정해 주어야 합니다. 그렇게 하기 위해서 필요한 것이, ContextLoaderListener 이며, 이 녀석을 등록하면 <context-param> 엘리먼트로 등록되어 있는 정보를 바탕으로 스프링 애플리케이션 컨텍스트 파일을 읽어옵니다.

즉 web.xml 설정에 다음의 코드를 추가합니다.

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/acegi-config.xml</param-value>
    </context-param>

이제 web.xml과는 영원히 안녕입니다. 지금까지 web.xml에 추가한 내용은 filter, filter-mapping, listener, context-param 이렇게 네 개입니다.