Security Namespace Configuration PART 2

2.3. Advanced Web Features

2.3.1. Adding HTTP/HTTPS Channel Security

애플리케이션에서 요청을 HTTP와 HTTPS로 아무거나 셋 중 하나의 채널로 받아들이도록 설정하려면 <intercept-irl> 의 requires-channel 속성을 사용한다.

  <http>
    <intercept-url pattern=”/secure/**” access=”ROLE_USER” requires-channel=”https”/>
    <intercept-url pattern=”/**” access=”ROLE_USER” requires-channel=”any”/>
    …
  </http>

/secure/** 패턴의 HTTP를 사용해서 접근하면 HTTPS URL로 리다이렉트 된다. 가용한 옵션은 “http”, “https”, “any”(둘 중 아무거나)가 있음.

위 프로토콜을 기본 포트가 아닌 다른 포트에서 사용할 때는 포트 맵핑을 해준다.

  <http>
    …
    <port-mappings>
      <port-mapping http=”9080″ https=”9443″/>
    </port-mappings>
  </http>

채널에 대해서는 여기서 자세히

2.3.2. Concurrent Session Control

단일 사용자 계정을 한 명(세션)만 사용하도록 제한할 수 있다.
먼저 web.xml에 다음 리스너를 등록한다.

<listener>
  <listener-class>org.springframework.security.ui.session.HttpSessionEventPublisher</listener-class>
</listener>

그리고 application context에 다음을 추가한다.

  <http>
    …
    <concurrent-session-control max-sessions=”1″ />
  </http>

이렇게 하면, 같은 계정으로 두 번 로그인 하면, 첫 번째 로그인한 유저가 invalid하게 된다. 두 번째 로그인하는 걸 막고 싶으면 다음과 같이 설정한다.

  <http>
    …
    <concurrent-session-control max-sessions=”1″ exception-if-maximum-exceeded=”true”/>
  </http>

mac-sessions 속성으로 한 계정당 가용한 세션 수를 정할 수 있나봅니다.

2.3.3. OpenID Login

  <http auto-config=’true’>
    <intercept-url pattern=”/**” access=”ROLE_USER” />
    <openid-login />
  </http>

저렇게 설정하면, OpenID 로그인을 사용하게 된다. 그리고 자신의 OpenID 계정을 설정해야 한다. 그리고 사용자 정보를 in-memory <user-service>에 추가한다.(이건 어떻게 하는거지..?)

<user name=”http://jimi.hendrix.myopenid.com/” password=”notused” authorities=”ROLE_USER” />

2.3.4. Adding in Your Own Filters

이전 버전의 Acegi에서는 커스텀 필터를 필터 체인 중에 끼워넣을 수 있었다. 또는 이미 존재하는 필터를 커스터마이징해서 사용하고 싶을 수 있다. 2.0 이후 부터는 필터 체인이 명시적으로 보이지 않는데 어떻게 해야할까?

네임스페이스를 사용할 때 필터의 순서는 항상 고정되어 있다. 각각의 필터들은 스프링의 Ordered 인터페이스를 구현했고 그에 따라서 초기화 과정에서 정렬된다. 각각 필터들은 다음과 같은 이름으로 맵핑되어 있다.

Alias

Filter Class

CHANNEL_FILTER

ChannelProcessingFilter

CONCURRENT_SESSION_FILTER

ConcurrentSessionFilter

SESSION_CONTEXT_INTEGRATION_FILTER

HttpSessionContextIntegrationFilter

LOGOUT_FILTER

LogoutFilter

X509_FILTER

X509PreAuthenticatedProcessigFilter

PRE_AUTH_FILTER

Subclass of AstractPreAuthenticatedProcessingFilter

CAS_PROCESSING_FILTER

CasProcessingFilter

AUTHENTICATION_PROCESSING_FILTER

AuthenticationProcessingFilter

BASIC_PROCESSING_FILTER

BasicProcessingFilter

SERVLET_API_SUPPORT_FILTER

classname

REMEMBER_ME_FILTER

RememberMeProcessingFilter

ANONYMOUS_FILTER

AnonymousProcessingFilter

EXCEPTION_TRANSLATION_FILTER

ExceptionTranslationFilter

NTLM_FILTER

NtlmProcessingFilter

FILTER_SECURITY_INTERCEPTOR

FilterSecurityInterceptor

SWITCH_USER_FILTER

SwitchUserProcessingFilter

커스텀 필터는 <custom-filter> 엘리먼트를 사용해서 체인에 있는 필터를 교체 할 수 있다.

  <beans:bean id=”myFilter” class=”com.mycompany.MySpecialAuthenticationFilter”>
    <custom-filter position=”AUTHENTICATION_PROCESSING_FILTER”/>
  </beans:bean>

필터 전 후에 끼워넣고 싶을 때는 after나 before 속성을 사용할 수 있으며 필터 이름 앞에 FIRST나 LAST를 붙일 수 있다.

2.3.5. Session Fixation 공격 방어

세션을 만들어서 사이트에 접속하려는 Session Fixation 공격이 있다. Spring Security는 사용자가 로그인 할 때 자동으로 새로운 세션을 만들어서 이 공격을 막는다. <http> 에 있는 session0-fixation-protection 속성에 다음과 같은 옵션을 사용하여 설정할 수 있다.

  • migrateSession – 새 세션을 만들고 기존의 세션 속성을 새 세션으로 복사한다. 이게 기본값이다.
  • none – 아무것도 하지 않음. 원래 세션을 유지한다.
  • newSession – 완전히 비어있는 clean 세션을 생성하고 기존 세션으로부터 아무것도 복사하지 않는다.

2.3.6. Setting a Custom AuthenticationEntryPoint

네임스페이스가 지원하는 인증 방식이 아닌 다른 방법을 사용하고 있어서 새로운 인증 필터와 Entry Point를 사용한다면 이 것들을 네임스페이스에 끼워넣고 싶을 것이다. AuthenticationEntryPoint 구현체를 <http>의 entry-point-ref 속성에 설정하면 된다.

2.4. Method Security

스프링 시큐리티 2.0 부터는 서비스 계층의 메소드에 JSR-250 시큐리티 애노테이션과 프레임워크의 @Secured 애노테이션을 사용할 수 있다. 빈 설정에 intercept-methods 엘리먼트를 추가하거나 AspectJ 스타일 포인트 컷으로 여러 개의 빈에 적용할 수 있다.

2.4.1. The <global-method-security> Element

이 엘리먼트를 등록해서 애노테이션 기반 보안이 가능하도록 설정한다.

<global-method-security secured-annotations=”enabled” jsr250-annotations=”true”/>

두 가지 타입의 애노테이션을 모두 지원하도록 설정했다.

2.4.1.1. Adding Security Pointcuts using protect-pointcut

<global-method-security>
    <protect-pointcut expression=”execution(* com.mycompany.*Service.*(..))” access=”ROLE_USER”/>
</global-method-security>

위 설정은 com.mycompany 패키지에 Service 라는 이름이 들어가는 빈들의 모든 메소드를 보안하도록 설정. ROLE_USER인 사용자만 해당 클래스의 메소드를 호출할 수 있다.

2.5. The Default AccessDecisionManager

여기는 기본적인 롤(ROLE) 기반의 접근 권한 관리 말고 좀 더 커스터마이징 하고자하는 분들을 위한 것임.

네임스페이스를 사용하면 기본 AccessDecisionManager 객체를 생성하고 등록해준다. 그리고 이 녀석을 사용해서 메소드 호출과 web URL 접근 가능 여부를 intercept-url와 protect-pointcut 설정을 바탕으로 결정한다.
기존 전략은 RoleVoter와 AuthenticatedVoter를 하나씩 가지고 있는 AffirmativeBased AccessDecisionManager를 사용한다.

2.5.1. Customizing the AccessDecisionManager

좀 더 복잡한 접근 제어 전략을 사용하려면 메소드와 웹 보안 둘에 대한 대체제를 설정하면 ㅕ된다.

메소드 보안을 용도로는 AccessDecisionmanager 빈을 가리키도록 gloval-security 엘리먼트의 acess-decision-manager-ref 속성을 사용한다.

  <global-method-security access-decision-manager-ref=”myAccessDecisionManagerBean”>
    …
  </global-method-security>

웹 보안 접근 제어 대체제는 <http> 에 설정한다.

  <http access-decision-manager-ref=”myAccessDecisionManagerBean”>
    …
  </http>

2.5.2. The Authentication Manager

스프링 시큐리티의 네임스페이스를 사용하면 인증 관리자 객체인 ProviderManager 타입의 객체도 생성해서 등록해준다. 이전 버전을 사용해봤다면 매우 친숙한 객체일 것이다.

추가적인 AuthenticationManager를 추가 등록하고 싶을 때 <custom-authentication0provider> 속성을 사용할 수 있다.

  <bean id=”casAuthenticationProvider”
      class=”org.springframework.security.providers.cas.CasAuthenticationProvider”>
    <security:custom-authentication-provider />
    …
  </bean>

그리고 이렇게 등록한 빈을 컨텍스트 안에 있는 다른 빈들이 AhtehnticationManager라고 참조하도록 별칭을 등록할 수 있다.

  <security:authentication-manager alias=”authenticationManager”/>
  <bean id=”casProcessingFilter” class=”org.springframework.security.ui.cas.CasProcessingFilter”>
     <security:custom-filter position=”CAS_PROCESSING_FILTER”/>
     <property name=”authenticationManager” ref=”authenticationManager”/>
     …
  </bean>

Security Namespace Configuration PART 1

2.1. Introduction

– 스프링 네임스페이스
– 시큐리티 네임스페이스를 기본 네임스페이스로 사용하기

<beans:beans xmlns=”http://www.springframework.org/schema/security”
   xmlns:beans=”http://www.springframework.org/schema/beans”>
    …
</beans:beans>

2.1.1 Design of the Namespace

– 가장 흔하게 사용되는 형태를 잡아서 네임스페이스를 설계했다. (네임스페이스 만드는 방법은 KSUG 1회 세미나에서 토비형님께서 발표해주셨었음. 다시 듣고 싶음..) 다음과 같이 나눠져 있다.

  • Web/Http Security – 가장 복잡한 부분이다. 필터들을 등록하고 그와 연관된 서비스 빈들을 등록한다. 인증, URL 보안, 로그인 페이지 보여주기, 에러 페이지 보여주기 등 많은 일을 한다.
  • Business Object (Method) Security – 서비스 계층을 보안(보완이 아니라 보안!!)하기 위한 옵션
  • AuthenticationManager – 프레임워크 내 다른 부분들에서 필요한 인증 요청을 다룬다. 간단하게 인증 담당자.
  • AccessDecisionManager – 해당 리소스에 접근 권한이 있는지 결정하는 곳. 간단하게 권한 담당자. 자동으로 기본값이 세팅 되는데 물론 커스터마이징 수도 있다.
  • AuthenticationProviders – 인증 담당자가 사용할 인증 방법들. 여러 가지 방법이 있겠지만, 기억하기, OpenID, 로그인 페이지가 가장 흔할듯.
  • UserDetailService – 인증 담당자가 사용자 정보를 가져올 때 사용하는 빈.

2.2. Getting Started with Security Namespace Cofiguration

여러분들이 Spring Security를 애플리케이션에 빨리 적용 인증, 권한 기능을 사용하길 원하다는 가정하게 설명한다.

2.2.1. web.xml 설정하기

가장 먼저 할 일은 web.xml에 필터를 등록하는 것이다.

<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

특정 URL을 후킹해서 스프링 ApplicationContext에 등록되어 있는 머시기 빈이 그 요청 처리 앞에 끼어들도록 하는 것임. 위 설정은 /* 이렇게 모든 요청을 가로채도록 해놨음.

2.2.2. 최소 <http> 설정

웹 보안을 위해 필요한 최소 설정은 아래와 같다.

  <http auto-config=’true’>
    <intercept-url pattern=”/**” access=”ROLE_USER” />
  </http>

모든 요청을 ROLE_USER 만 접근할 수 있다. 로그인 페이지에는 접근할 수가 있을까??? 없을 것 같은데.. 된다. 왜냐면 auto-config 때문에 설정된 <http-basic> 때문에.. 자세한건 아래에 있음.

<http> 엘리먼트 안에 <intercept-url> 엘리먼트 여러개를 설정할 수 있는데, 이 때 맨위에서 아래 순으로 가장 먼저 걸리는 녀석이 적용된다. 순서에 주의하자.

사용자 추가하기

  <authentication-provider>
    <user-service>
      <user name=”jimi” password=”jimispassword” authorities=”ROLE_USER, ROLE_ADMIN” />
      <user name=”bob” password=”bobspassword” authorities=”ROLE_USER” />
    </user-service>
  </authentication-provider>

아이디와 비번 권한 정보를 이렇게 직접 줄 수도 있다. 이 정보들을 프로퍼티 파일로 빼낼 수 도 있다. 자세한건 여기 참조. 그다지 안전해 보이지 않는 방법이다. 하지만 간단하게 적용할 수 있다는 것이 장점이다. 매우 작은 웹 애플리케이션에 적용하기엔 저 정도면 충분할 듯 하다. 여기에 정의한 사용자 정보들은 Authentication Manager가 인증 요청을 다룰 때 사용한다.

위에 설정을 10줄 밖에 안 했지만, 암묵적으로 몇개의 빈들이 설정된다. 로그인 처리라던지 “remember-me” 서비스 같은 것들이 자동으로 설정되었다. 이는 <http>에 auto-config 설정이 기본으로 되어있기 때문이다.

이전 버전에서 필터들의 순서가 이슈가 됐었는데 이제는 그런 걱정할 필요가 없다. 필터 등록하고 필터가 필요한 빈등록하는 일을 전부 <http> 엘리먼트가 책임진다. <http>는 옛날의 FilterChainProxy, <authentication-provider>는 옛날의 DaoAuthenticationProvider, <user-service>는 옛날의 InMemoryDaoImpl 라고 생각할 수 있다. 네임스페이스 처리 시스템이 ProviderManager는 자동으로 만들어주고 DaoAuthenticationProvider 얘를 거기에 자동으로 엮어서 등록한다.

이 내용은 Acegi를 아시는 분들만 이해하실 수 있습니다. 몰라도 별 지장은 없습니다.

2.2.2.1. What does auto-config Include?

auto-config 속성은 다음 코드를 압축시킨거다.

  <http>
    <intercept-url pattern=”/**” access=”ROLE_USER” />
    <form-login />
    <anonymous />
    <http-basic />
    <logout />
    <remember-me />
  </http>

2.2.2.2. Form and Basic Login Options

HTML 파일이나 JSP 파일을 만들지 않아도 Spring Security가 기본 로그인 화면을 만들어 준다. 물론 그걸 그냥 써도 되겠지만, 대부분 그러지는 않을 것이다. 다음과 같이 설정하여 별도의 로그인 폼을 지정할 수 있다.

  <http auto-config=’true’>
    <intercept-url pattern=”/login.jsp*” filters=”none”/>
    <intercept-url pattern=”/**” access=”ROLE_USER” />
    <form-login login-page=’/login.jsp’/>
  </http>

auto-config를 사용하는 상태에서 <form-login>도 설정하고 있는데, 이렇게 하면 기본 세팅값을 오버라이딩한다. /login.jsp 요청은 시큐리티 필터 처리를 하지 않도록 설정했다. 그래야 로그인 페이지에 접근할 수가 있다.

  <http auto-config=’true’>
    <intercept-url pattern=”/**” access=”ROLE_USER” />
    <http-basic />
  </http>

<http-basic /> 이라는 기본 인증은 설정 위치에 상관없이 가장 우선시 된다. 따라서, 제한된 영역에 접근하려고 할 때 로그인창을 띄운다.

2.2.3. Using other Authentication Providers

사용자 인증 방법을 여러가지 사용할 수 있는데, 사용자 정보를 보통은 DB나 LDAP 서버에 저장해 둔다. LDAP 네임스페이스는 LDAP 챕터에 나와있으니 여기서 다루진 않는다. UserDetailSerivce에 대한 구현체가 있다면 다음과 같이 등록할 수 있다.

 <authentication-provider user-service-ref=’myUserDetailsService’/>

DB에서 직접 사용자 정보를 끌어올 거라면 다음과 같이 설정할 수 있다.

  <authentication-provider>
    <jdbc-user-service data-source-ref=”securityDataSource”/>
  </authentication-provider>

위 설정에서 securityDataSource는 그냥 DataSource 타입의 빈 이름이다. 이 때 해당 데이터소스와 연관을 맺는 데이터베이스에는 Spring Secutiry 표준 사용자 테이블 스키마를 따르고 있어야 하는데, 스키마가 그와 다를 경우에는 JdbcDaoImpl 빈을 만들고 그 빈을 user-service-ref에 설정해줄 수있다.

user-service-ref에는 UserDeteailsService 타입의 빈을 등록할 수 있는데, JdbcDaoImpl은 UserDetailsService를 구현한 클래스이기 때문에 등록할 수 있는것이다.

2.2.3.1. Adding a Password Encoder

<authentication-provider>
  <password-encoder hash=”sha”/>
  <user-service>
    <user name=”jimi” password=”d7e6351eaa13189a5a3641bab846c8e8c69ba39f” authorities=”ROLE_USER, ROLE_ADMIN” />
    <user name=”bob” password=”4e7421b1b8765d8f9406d87e7cc6aa784c4ab97f” authorities=”ROLE_USER” />
  </user-service>
</authentication-provider>

위와 같이 <password-encoder has=sha”/>를 사용하여 해싱 알고리즘을 사용해서 암호화 할 수 있다. 사전어 공격을 막기 위해서 salt 값들을 사용할 수 있다.

<password-encoder hash=”sha”>
  <salt-source user-property=”username”/>
</password-encoder>

너무 길어져서 Advanced와 Method Security는 Part 2에서 요약 하겠습니다.