Whiteship's Note


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



지금까지 등록한 필터들을 나열 하면 다음과 같습니다.

1. httpSessionContextIntegrationFilter => 세션 문맥 통합 필터
2. logoutFilter => 로그 아웃 필터
3. authenticationProcessingFilter => 인증 처리 필터
4. exceptionTranslationFilter => 예외 처리 필터
5. filterInvocationInterceptor => 권한 처리 필터

FilterChainProxy의 filterInvocationDefinitionSource속성에 등록한 이 들의 순서는 매우 중요합니다.

일례로, exceptionTranslationFilter가 filterInvocationInterceptor보다 뒤에 있으면 어떤 일이 발생할까요? 예외 처리 필터의 존재 가치가 없어집니다. 예외 처리 필터가 예외를 확인하기 전에 권한 처리 필터에게 넘겨지기 때문에, 예외 처리 필터는 예외가 발생한지도 모른 상태이고, 웹 브라우저는 그냥 에러를 출력하게 됩니다.

또 다른 예로, 권한 처리가 인증 처리 필터보다 앞에 있으면 어떤 일이 발생할까요? 위의 상태에서, 1, 2, 4, 5, 3형태로 배열 했더니 웹 요청 처리가 무한루프에 빠지는 것을 볼 수 있었습니다.ㅎㅎ

그런 반면, 로그 아웃 필터의 위치는 어디에 두던지 별 영향을 미치지 않는 것을 확인할 수 있었습니다.

필터들의 순서를 가지고 여러 가지 실험을 해봤을 때, 가장 안전한 선택은 다음과 같습니다.

1. 세션 문맥 통합 필터는 가장 앞에 둔다.
2. 권한 처리 필터는 가장 마지막에 둔다.
3. 예외 처리 필터는 권한 처리 필터 바로 앞에 둔다.
4. 인증 처리 필터는 예외 처리 필터 앞에 둔다.
5. 기타 인증과 관련된 필터(리멤버미 필터, 익명 사용자 처리 필터)는 인증 처리 필터 바로 뒤에 연달아 둔다.

예를 들어 다음과 같은 순서대로 필터를 나열하면 안전 합니다.

httpSessionContextIntegrationFilter,
authenticationProcessingFilter,
anonymousProcessingFilter,
rememberMeProcessingFilter,
logoutFilter,
exceptionTranslationFilter,
filterInvocationInterceptor

top

Write a comment.


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



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

사용자 삽입 이미지

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

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

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

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


top

Write a comment.


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에서 삭제하는 방법입니다. 계속해서 한 사용자로 로그인 되어 있으면 안 되겠죠. 로그아웃이 필요합니다.
그리고 사용자 정보가 없을 때 예외를 브라우저에 출력하지 말고 로그인 폼으로 이동하도록 하는 예외 처리가 필요합니다.

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

  1. Favicon of http://www.truthiness.pe.kr BlogIcon ongs 2007.10.26 14:19 PERM. MOD/DEL REPLY

    으아 끝장나네요. ~OTL. 마치 Acegi Load map같군요. 많은 도움 됩니다..

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2007.10.26 16:22 신고 PERM MOD/DEL

    에구 시험 볼 때도 안 하던 벼락치기 중입니다.

Write a comment.


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



4. 권한 확인하기.

앞에서 인증은 제대로 동작하지만, 인증 만으로는 보안을 할 수 없습니다. 사용자는 식별할 수 있지만, 해당 사용자가 요청에 대한 권한이 있는지는 별도로 확인을 해야 합니다. 이 일을 해줄 필터과 bean을 등록하겠습니다.

권한을 위해 Acegi에서 제공하는 필터는 FilterSecurityInterceptor입니다.
사용자 삽입 이미지
FilterSecurityInterceptor에서는 앞에서 설저한 AuthenticationManager를 사용하며, 그 외에도 AccessDecisionManager와 objectDefinitionSource라는 속성을 설정합니다.

AuthentiactionManager를 사용하여 사용자 정보를 가져오고, ObjectDefinitionSource에 있는 설정과, AccessDecisionManager를 사용하여, 해당 사용자가 요청에 대한 권한이 있는지 확인합니다.

어떤 요청에 대한 필요한 권한을 설정해 놓은 것이 바로 ObejctDefinitionSource입니다. 위 그림의 빨간색 박스에 해당하며, /protected/로 시작하는 모든 요청은 ROLE_HEAD_OF_ENGINEERING 의 role을 가진 사용자에게만 허가합니다. 그것을 제외한 모든 요청은 IS_AUTHENTICATED_ANONYMOUSLY 라는 role이 사용 하도록 했는데, 이 것은 anonymous Filter를 등록했을 때 익명 사용자에게 부여한 권한이라고 생각하시면 됩니다. 나중에 더 자세히 살펴보겠습니다.

이렇게 설정한 상태에서 FilterChainProxy에 여기서 등록한 필터의 이름을 추가해 줍니다.

사용자 삽입 이미지
자 이 상태에서 바라는 것은 다음과 같습니다.
1. /**로 접속 했을 때는 익명 사용자로써, 화면이 보여집니다. 따라서 첫 페이지에 접속할 수 있으며, 로그인 페이지에도 접속할 수가 있습니다.
2. 로그인을 하지 않은 상태에서 /protected/**로 접속했을 때 예외가 발생할 것입니다. 해당 요청에 대한 권한이 없기 때문이죠.
3. ROLE_HEAD_OF_ENGINEERING role을 가진 사용자로 로그인을 한 상태에서라면, /protected/**로 접근 했을 때 화면을 볼 수 있습니다.

발생하는 예외는 다음과 같습니다.
사용자 삽입 이미지
org.acegisecurity.AuthenticationCredentialsNotFoundException
필요한 사용자에 대한 정보가 Security Context 객체에 없기 때문입니다.

서버를 종료한 다음 다시 실행하면, 로그인 정보를 기억하지 못합니다. 사용자 정보를 계속해서 유지하려면 필터 하나만 등록하면 됩니다.
top

Write a comment.


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에서 사용할 수 있는 상수들을 참조할 수 있습니다.

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

top

Write a comment.


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 이렇게 네 개입니다.


top

Write a comment.


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



1. web.xml에 필터 등록하기
사용자 삽입 이미지
FilterToBeanProxy를 Acegi Filter Chain Proxy로 등록합니다. FilterToBeanProxy는 스프링 애플리케이션 컨텍스트에 위치한 bean을 참조할 수 있도록 만들어둔 클래스입니다. 이녀석이 없었다면 일일히 모든 Filter에서 다음과 같은 코드를 삽입하여, Spring에 종속적인 코드를 만들어 가면서까지, 스프링 애플리케이션 컨텍스트에 위치한 bean을 필터에서 사용했을 것입니다.

ApplicationContext ctx = WebApplicationContextUtils.
getWebApplicationContext(servletContext);
Bar bar = (Bar) ctx.getBean("bar");

FilterToBeanProxy는 실제로 자신이 할 일을 스프링 애플리케이션 컨텍스트에 위치한 bean에게 모두 위임을 하는데, 그 bean을 찾기 위한 파라미터가 바로 targetClass 입니다. 이 필터를 생성할 때 스프링 애플리케이션 컨텍스트에서 targetClass 파라미터의 값으로 설정된 타입의 bean을 찾습니다. 즉 위의 설정을 보면 org.acegisecurity.util.FilterChainProxy를 찾게 되는 것입니다. 그리고 찾은 FilterChainProxy에게 할 일을 위임하는 것입니다. 그래야 스프링의 장점 중 하나인 DI(Dependency Injection)를 백분 활용할 수 있을 것이며, web.xml이 복잡해지지 않을 것입니다.

targetClass 속성 말고 targetBean 속성을 사용해서 스프링 애플리케이션 컨텍스트에 위치한 bean의 이름을 사용하여 역할을 위임할 bean을 찾을 수도 있는데, 만약에 bean의 이름을 변경하면, web.xml에서도 targetBean 속성의 값을 변경해야하기 때문에, targetClass 속성의 사용을 권장하고 있습니다.

이렇게 filter 설정을 마치면.. 이 filter를 적용할 url을 설정해 줍니다. 그 부분이 filter-mapping이며, 설정 내용을 보시면 모든 url이 모두 이 필터를 거치도록 설정한 것을 보실 수 있습니다.

이제 해당 웹 애플리케이션으로 들어오는 모든 요청은 FilterToBeanProxy를 거치게 되며, 이 녀석은 FilterChainProxy에게 그 역할을 위임합니다. 결국 모든 요청은 스프링 애플리케이션 컨텍스트에 등록되어 있는 FilterChainProxy를 거치게 되는 것입니다.

그럼 다음 글에서 FilterChainProxy를 살펴보겠습니다.

참조 : SIA 2판 7장
top

Write a comment.


Access Decision Manager



AccessDecisionManager 인터페이스를 사용하여 보안이 필요한 자원에 접근하는 사용자가 접근 권한이 있는지 확인합니다. AccessDecisionManager는 여러개의 AccessDecisionVoter 에게 실제 권한 확인 역할을 위임하며, 그 결과를 가지고 자원의 공개 여부를 결정합니다.

Acegi는 AccessDecisionManager의 구현체로 여러 Voter들의 투표 결과를 가지고 판단하는 방법에 따라 세 개의 클래스를 제공합니다. AffirmativeBased(Voter들 중에 하나라도 허락 하면 공개), ConsensusBased(Voter들의 결과가 일치하면 공개), UnanimousBased(모든 Voter들이 허락하면 공개) 클래스입니다.

AccessDecisionVoter 인터페이스의 핵심 메소드는 vote() 메소드로, 실제 요청한 자원에 대한 접근의 허가 여부를 결정하는 메소드입니다. 이 메소드는 결과값으로 세 개의 값 중 하나를 반환합니다.

ACCESS_GRANTED : 보안이 필요한 자원에 접근을 허용.
ACCESS_DENIED : 보안이 필요한 자원에 접근을 거부.
ACCESS_ABSTAIN : 나는 모르겠다.

Acegi가 제공하는 AccessDecisionVoter 인터페이스 구현체 중에 하나인 RoleVoter는 vote() 메소드에 전달되는 인자 중에 하나인 ConfigAttribute를 바탕으로 해당 자원에 대한 접근 권한이 있는지 Role 정보를 통해서 판단합니다.

ConfigAttribute는 필터나 다른 곳에서 설정할 것이고, RoleVoter에서 최소로 설정할 것은 Role 정보를 가져오는 방법입니다. 기본으로 Role_ 접두어가 붙어있는 녀석들을 읽어오는데, 이 접두어를 변경할 수 있으며, 아예 입력하지 않을 수도 있습니다.(이 방법은 비추하고 있습니다.)[각주:1]

정리하자면, access decision manager를 등록할 때는 어떠한 방식으로 투표 결과를 처리할 것인지에 따라 AccessDecisionManager 구현체를 선택하고, RoleVoter에 Role을 나타낼 때 사용한 접두어를 설정해 줍니다.

사용자 삽입 이미지

bean 설정은 다음과 같이 간단한 편입니다.
사용자 삽입 이미지


  1. An empty role prefix means that the voter will vote for every ConfigAttribute. When there are different categories of ConfigAttributes used, this will not be optimal since the voter will be voting for attributes which do not represent roles. [본문으로]
top

Write a comment.


Spring Security 설정 분류 및 커스터마이징



현재 1.0.5까지 나온 Acegi(Spring Security)의 설정은 매~~~우 깁니다. 하지만 대부분이 비슷한 설정을 사용하실 것이기 때문에 필요한 필터에 대한 설정을 복사해서 붙여넣고 일부분만 변경하여 사용하시면 됩니다.

문제는 설정이 매~~~우 길기 때문에 어지럽다는 것입니다. 'XML 지옥'이 이런 것이구나 하는 것을 Spring 공부하고 나서 처음으로 느끼게 되었습니다. 사실 이전에는 이렇게 긴 Spring 설정을 해본적이 별로 없었거든요. 그냥 컨트롤러가 늘어나면 XML 설정이 좀 많아 지는 구나.. 하는 정도 였으니까요. 그런데 Acegi는 정말 파격적으로 XML 코드가 들어납니다. 따라서 분류가 필요하다고 생각되더군요.

사용자 삽입 이미지


제가 생각한 분류 방법은 이렇습니다.
1. 필터들만 모아 둔 XML => acegiFilter.xml
2. Authentication Manager 관련 XML -> authenticationManager.xml
3. Authorization Manager 관련 XML -> authorizationManager.xml

이렇게 나누면 필터들만 따로 정리해서 볼 수 있기 때문에 그나마 덜 어지럽습니다.

acegiFilter.xml에서 변경해야 할 부분은 먼저 자신이 사용할 필터들만 남기고 나머지는 제거하는 일입니다. 그 다음으로는 자신의 애플리케이션에 맞게 세부 설정을 수정하는 일입니다. httpSessionContextIntegrationFilter 이 녀석은 뭐 거의 변경할 일이 없어보이고, authenticationEntryPoint에는 loginFormUrl에 로그인 폼을 보여줄 URL을 적어 주시면 됩니다. authenticationProcessingFilter 이 녀석의 속성 중에서 authenticationFailureUrl 정도를 변경하시면 되겠습니다. 그리고 로그인 폼과 로그인 실패 화면 두 개를 만들어 줍니다. 이 때 로그인 폼의 형식은

<form method="POST" action="j_acegi_security_check">
    <b>Username: </b><input type="text" name="j_username"><br>
    <b>Password: </b><input type="password" name="j_password"><br>
    <input type="submit" value="Login">
</form>

이것을 그대로 사용하시면 됩니다.

그런 다음 authenticationManager.xml로 이동하여 authenticationManager에서 자신이 사용할 인증 방법들을 설정해 줍니다. 보통 daoAuthenticationProvider는 꼭 사용하실 것이라 생각이 됩니다. 따라서

    <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
        <property name="providers">
            <list>
                <ref bean="daoAuthenticationProvider" />
            </list>
        </property>
    </bean>

이 상태의 bean 설정을 그대로 사용하시면 됩니다. 그 다음 daoAuthenticationProvider 설정은 역시 그대~로 사용하시면 되며 실제로 변경해야 할 부분은 daoAuthenticationProvider 이녀석이 사용하는 userDetailsService 설정입니다.

userDetailsService로 보통은 JdbcDaoImpl을 사용할 것으로 생각됩니다. DB에 있는 사용자 정보를 가져올 때 사용할 수 있는 구현체로, 사용자 정보, 패스워드, enabled, Role 등을 가져오는 sql을 작성해 주셔야 합니다. DB 스키마를 Acegi가 기본으로 예상하고 있는 형태를 사용하고 있다면 그럴 필요가 없겠지만 말이죠.

그러면 authenticationManager.xml 설정은 끝이 압니다. 복잡해 보이지만 실제로 변경해야 할 부분은 userDetailsService밖에 없었습니다.

그럼 다시 acegiFilter.xml로 돌아가서 '인증'과 관련된 필터과 manager 설정은 끝났고, 이제 예외 처리를 하는 필터와 '권한'과 관련된 필터를 설정하면 최소한의 필터를 갖추게 됩니다.

exceptionTranslationFilter는 인증에러가 발생하면 authenticationEntryPoint 속성에 설정된(이미 위에서 설정했습니다.) 곳으로 다시 돌아갑니다. 다시 인증을 요구하는 거죠. 그리고 accessDeniedHandler속성에 설정된 bean을 사용하여 권한 에러가 발생하면 해당 페이지로 이동합니다. 따라서 accessDeniedHandler bean의 errorPage 속성에 권한 에러 발생시 이동할 URL을 적어주시면 됩니다.

마지막으로 권한 처리 필터는 filterSecurityInterceptor 이 녀석으로써, 여기서는 objectDefinitionSource 속성에 URL = 권한, 권한, 권한
URL = 권한
이런 형태로 권한이 필요한 URL과 해당 URL에 접근할 수 있는 권한을 설정해 주시면 됩니다. 물론 Ant 스타일의 정규식을 사용해서 여러 URL을 나타낼 수 있습니다.

그리고 진짜 마지막으로 filterSecurityInterceptor가 사용하는 authorizationManager를 authorizationContext.xml에서 설정해 줍니다. 이 부분은 권한을 나타내는 값이 ROLE_ 이라는 접두사로 시작한다면 변경할 것이 하나도 없이 다음의 설정을 그대로 사용하시면 됩니다.

    <!-- access decision관련 : 하나의 voter만이라도 있으면 인증 통과 -->
    <bean id="accessDecisionManager"
        class="org.acegisecurity.vote.AffirmativeBased">
        <property name="decisionVoters">
            <list>
                <ref bean="roleVoter" />
            </list>
        </property>
    </bean>

    <bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter">
        <property name="rolePrefix">
            <value>ROLE_</value>
        </property>
    </bean>

위의 코드는 전부 한수형이 작성한 secutiryContext.xml에 제가 약간의 수정을 가한 코드를 참고하였습니다.


top

  1. Favicon of https://springframework.tistory.com BlogIcon 영회 2007.10.19 11:16 신고 PERM. MOD/DEL REPLY

    이런 얘기는 결론을 앞쪽에서 그림으로 표현하면 좋을 것 같다.

    Favicon of http://whiteship.tistory.com BlogIcon 기선 2007.10.19 11:19 PERM MOD/DEL

    아.. 네~ ㅎㅎ
    역시 글보다는 그림이 좋쵸.

    Favicon of https://springframework.tistory.com BlogIcon 영회 2007.10.21 23:30 신고 PERM MOD/DEL

    그림으로 그리니까 훨씬 좋구나

  2. Favicon of http://www.truthiness.pe.kr BlogIcon ongs 2007.10.19 13:05 PERM. MOD/DEL REPLY

    Acegi 준비하고 계시는군요. 열공~~!!^^

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2007.10.20 21:20 신고 PERM MOD/DEL

    넵~ 열공이요.

Write a comment.


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

Write a comment.