7.1 개요

보안은 모든 애플리케이션에서 중요한 개념이다. 최종 사용자는 URL을 추측하여 사이트의 임의 영역에 접근해서는 안 된다. 중요한 부분은 반드시 권한을 가지고 있는 요청만 접근할 수 있어야 한다. 스프링 시큐리티는 검증된 보안 플랫폼으로 애플리케이션에 여러 수준으로 통합할 수 있다. 이번 장에서는 플로우 실행 보안을 집중적으로 살펴본다.

7.2. 플로우를 어떻게 보안할까?

플로우 실행 보안은 세 단계로 한다.

  • 스프링 시큐리티를 인증 권한 규칙으로 설정한다.
  • 플로우 정의에 보안 규칙을 정의하는 secured 엘리먼트를 추가한다.
  • SecurityFlowExecutionListener를 추가하여 보안 규칙을 처리한다.

이 모든 단계를 완료해야 하며 그렇지 않으면 보안 규칙은 적용되지 않는다.

7.3. secured 엘리먼트

secured 엘리먼트는 완전히 들어오기 전에 권한 확인을 하도록 고안되었다. 보안하고 있는 플로우 실행 스테이지 마다 두 번 이상 발생하면 안 된다.

플로우 실행에서 세 단계를 보안할 수 있다. flow, state, transition이다. 각각에서 secured 엘리먼트의 의미는 모두 동일하다. secured 엘리먼트는 자신이 보안하는 엘리먼트 안에 위치한다. 예를 들어 state를 보안하려면 해당 state 내부에 secured 엘리먼트를 추가하면 된다.

<view-state id="secured-view">
    <secured attributes="ROLE_USER" />
    ...
</view-state>
       
7.3.1. 보안 속성

attributes 속성은 콤바로 구분한 스프링 시큐리비 권한 속성 목록이다. 보통 특정 보안 롤(role)을 명시한다. 스프링 시큐리티 접근 결정 매니저(access decision manager)에 의해 이 속성에 입력한 값과 사용자가 가지고 있는 값을 비교한다.

<secured attributes="ROLE_USER" />
           
기본적으로, 롤 기반 접근 결정 관리자를 사용하여 사용자가 접근할 수 있는지 확인한다. 만약 애플리케이션이 권한 룰을 사용하지 않는다면 이 부분을 오버라이딩할 필요가 있다.

7.3.2. 매칭 타입

가용한 매칭 타입에는 두 가지가 있다. any와 all이 있다. Any는 필요한 보안 속성 중에 사용자가 하나라도 가지고 있다면 접근을 허용한다. All은 사용자가 명시되어 있는 보안 속성을 모두 가지고 있는 경우에 접근을 허용한다.

<secured attributes="ROLE_USER, ROLE_ANONYMOUS" match="any" />
   
이 속성은 필수가 아니다. 정의하지 않으면 기본 값은 any다.

The match attribute will only be respected if the default access decision manager is used.

7.4. SecurityFlowExecutionListener

보안 규칙을 폴로우 내부에 정의하는 것 만으로는 플로우 실행을 보호하지 못한다. SecurityFlowExecutionListener가 웹 플로우 설정에 정의되어 있어야 플로우 실행기(executor)에 적용된다.

<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
    <webflow:flow-execution-listeners>
        <webflow:listener ref="securityFlowExecutionListener" />
    </webflow:flow-execution-listeners>
</webflow:flow-executor>

<bean id="securityFlowExecutionListener"
      class="org.springframework.webflow.security.SecurityFlowExecutionListener" />

애플리케이션의 특정 부분에서 만약 접근이 거부되면 AccessDeniedException를 던진다. 이 예외는 나중에 스프링 시큐리티가 처리하고 사용자에게 권한을 요청하는데 사용된다. 이 예외는 예외 스택을 무제한으로 올라갈 수 있다는 것이 중요한데 이렇게 하지 않으면 최종 사용자는 권한을 요청할 수 없다.

7.4.1. 커스텀 접근 결정 관리자

만약 애플리케이션이 롤 기반이 아닌 권한을 사용중이라면 커스텀 AccessDecisionManager를 설정할 필요가 있다. 기본 결정 관리자를 오버라이드 하고 시큐리티 리스너의 accessDecisionManager 속성에 설정할 수 있다. 스프링 시큐리티 레퍼런스 문서를 참조하여 결정 관리자에 대해 자세히 살펴보기 바란다.

<bean id="securityFlowExecutionListener"
      class="org.springframework.webflow.security.SecurityFlowExecutionListener">
    <property name="accessDecisionManager" ref="myCustomAccessDecisionManager" />
</bean>

7.5. 스프링 시큐리티 설정하기

스프링 시큐리티는 일관된 설정 옵션을 제공한다. 모든 애플리케이션과 환경이 자신만의 보안 요구사항을 가지고 있기 떄문에 스프링 시큐리티 레퍼런스 문서에서 가용한 옵션을 익히는것이 좋다.

 7.5.1. 스프링 설정

스프링 설정은 (보호하는 URL과 로그인/로그아웃 방법 같은) http 관련 설정과 authentication-provider를 설정한다. 예제 애플리케이션에 로컬 인증 공급자를 설정했다.

<security:http auto-config="true">
    <security:form-login login-page="/spring/login"
                         login-processing-url="/spring/loginProcess"
                         default-target-url="/spring/main"
                         authentication-failure-url="/spring/login?login_error=1" /> 
    <security:logout logout-url="/spring/logout" logout-success-url="/spring/logout-success" />
</security:http>

<security:authentication-provider>
    <security:password-encoder hash="md5" />
    <security:user-service>
        <security:user name="keith" password="417c7382b16c395bc25b5da1398cf076"
                       authorities="ROLE_USER,ROLE_SUPERVISOR" />
        <security:user name="erwin" password="12430911a8af075c6f41c6976af22b09"
                       authorities="ROLE_USER,ROLE_SUPERVISOR" />
        <security:user name="jeremy" password="57c6cbff0d421449be820763f03139eb"
                       authorities="ROLE_USER" />
        <security:user name="scott" password="942f2339bf50796de535a384f0d1af3e"
                       authorities="ROLE_USER" />
    </security:user-service>
</security:authentication-provider>
           
7.5.2. web.xml 설정

web.xml 파일에서 filter는 모든 요청을 가로채도록 정의했다. 이 필터는 로그인/로그아웃 요청을 다루고 적절하게 처리할 것이다. 또한 AccesDeniedExceptions 예외를 잡아서 사용자를 로그인 페이지로 리다이렉트 시킨다.