6.5. 認可

6.5.1. Overview

本節では、Spring Securityで提供している認可機能を説明する。
Spring Securityのアクセス認可機能を利用して実現するため、Spring Securityの認証機能を用いることを前提とする。
Spring Securityを利用した認証方法については、認証を参照されたい。
アクセス認可の対象のリソースは、以下の3項目である。
  1. Web(リクエストURL)
    • 特定のURLにアクセスするために必要な権限を設定できる
  2. 画面項目(JSP)
    • 画面中の特定の要素を表示するために必要な権限を設定できる
  3. メソッド
    • 特定のメソッドを実行するために必要な権限を設定できる
Spring Securityでは、設定ファイルやアノテーションでアクセス認可情報を記述し、機能を実現している。

6.5.1.1. アクセス認可(リクエストURL)

Authorization(リクエストURL)

Picture - Authorization(リクエストURL)

  1. ユーザのリクエストに対し、Spring Securityのフィルタチェーンが割り込み処理を行う。
  2. 認可制御の対象となるURLとリクエストのマッチングを行い、アクセス認可マネージャにアクセス認可の判断を問い合わせる。
  3. アクセス認可マネージャが、ユーザの権限とアクセス認可情報をチェックし、 必要なロールが割り当てられていない場合は、アクセス拒否例外をスローする。
  4. 必要なロールが割り当てられている場合は、処理を継続する。

6.5.1.2. アクセス認可(JSP)

Authorization(JSP)

Picture - Authorization(JSP)

  1. JSPから生成されたサーブレットが、アクセス認可マネージャに問い合わせる。
  2. アクセス認可マネージャが、ユーザの権限とアクセス認可情報をチェックし、 必要なロールが割り当てられていない場合は、タグの内部を評価しない。
  3. 必要なロールが割り当てられている場合は、タグの内部を評価する。

6.5.1.3. アクセス認可(Method)

Authorization(Method)

Picture - Authorization(Method)

  1. Springコンテナがアクセス認可情報をもとに、対象のオブジェクトに対してインターセプタを生成、割り込みさせる。
  2. インターセプタは設定されたロールをもとにアクセス認可マネージャに問い合わせる。
  3. アクセス認可マネージャが、ユーザが持つ権限とアクセス認可情報をチェックし、 必要なロールが割り当てられていない場合はアクセス拒否例外をスローする。
  4. 必要なロールが割り当てられている場合は、処理を継続する(設定により、処理を実行した後に権限をチェックすることもできる)。

6.5.2. How to use

アクセス認可(リクエストURL)、アクセス認可(JSP)、アクセス認可(Method)の使用方法について説明する。

6.5.2.1. アクセス認可(リクエストURL)

アクセス認可(リクエストURL)機能を使用するために、Spring Securityの設定ファイルに記述する内容を以下に示す。
基本設定については、Spring Security概要を参照されたい。

6.5.2.1.1. <sec:intercept-url>要素の設定

<sec:http>要素の子要素である<sec:intercept-url>要素に制御対象とするURL、認可するロールを記述することで、
URLのパス単位で認可制御を行うことができる。
以下に、設定例を記載する。
  • spring-security.xml

    <sec:http auto-config="true" use-expressions="true">
        <sec:intercept-url pattern="/admin/*" access="hasRole('ROLE_ADMIN')"/>
        <!-- omitted -->
    </sec:http>
    
    属性名
    説明
    pattern
    アクセス認可を行う対象のURLパターンを記述する。ワイルドカード「*」、「**」が使用できる。
    「*」では、同一階層のみが対象であるのに対し、「**」では、指定階層以下の全URLが、認可設定の対象となる。
    access
    Spring EL式でのアクセス制御式や、アクセス可能なロールを指定する。
    method
    HTTPメソッド(GETやPOST等)を指定する。指定したメソッドのみに関して、URLパターンとマッチングを行う。
    指定しない場合は、任意のHTTPメソッドに適用される。主にRESTを利用したWebサービスの利用時に活用できる。
    requires-channel
    「http」、もしくは「https」を指定する。指定したプロトコルでのアクセスを強制する。
    指定しない場合、どちらでもアクセスできる。
    上記以外の属性については、<intercept-url>を参照されたい。
ログインユーザーに「ROLE_USER」「ROLE_ADMIN」というロールがある場合を例に、設定例を示す。
  • spring-security.xml

    <sec:http auto-config="true" use-expressions="true">
        <sec:intercept-url pattern="/reserve/*" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')" /> <!-- (1) -->
        <sec:intercept-url pattern="/admin/*" access="hasRole('ROLE_ADMIN')" /> <!-- (2) -->
        <sec:intercept-url pattern="/**" access="denyAll" /> <!-- (3) -->
        <!-- omitted -->
    </sec:http>
    
    項番
    説明
    (1)
    「/reserve/*」にアクセスするためには、「ROLE_USER」もしくは「ROLE_ADMIN」ロールが必要である。
    hasAnyRoleについては、後述する。
    (2)
    「/admin/*」にアクセスするためには、「ROLE_ADMIN」ロールが必要である。
    hasRoleについては、後述する。
    (3)
    denyAllを全てのパターンに設定し、
    権限設定が記述されていないURLに対してはどのユーザもアクセス出来ない設定としている。
    denyAllについては、後述する。

    Note

    URLパターンの記述順序について

    クライアントからのリクエストに対して、intercept-urlで記述されているパターンに、上から順にマッチさせ、 マッチしたパターンに対してアクセス認可を行う。そのため、パターンの記述は、必ず、より限定されたパターンから記述すること。

<sec:http>属性にuse-expressions="true"の設定をしたことで、Spring EL式が有効になる。
access属性に記述したSpring EL式は真偽値で評価され、式が真の場合に、アクセスが認可される。
以下に、使用例を示す。
  • spring-security.xml

    <sec:http auto-config="true" use-expressions="true">
        <sec:intercept-url pattern="/admin/*" access="hasRole('ROLE_ADMIN')"/>  <!-- (1) -->
        <!-- omitted -->
    </sec:http>
    
    項番 説明
    (1)
    hasRole('ロール名')を指定することで、ログインユーザが指定したロールを保持していれば真を返す。
    使用可能なExpression一覧例
    属性名 説明
    hasRole('ロール名')
    ユーザが指定したロールを保持していれば、真を返す。
    hasAnyRole('ロール1','ロール2')
    ユーザが指定したいずれかのロールを保持していれば、真を返す。
    permitAll
    常に真を返す。認証されていない場合も、アクセスできることに注意する。
    denyAll
    常に偽を返す。
    isAnonymous()
    匿名ユーザであれば、真を返す。
    isAuthenticated()
    認証されたユーザならば、真を返す。
    isFullyAuthenticated()
    匿名ユーザ、もしくはRememberMe機能での認証であれば、偽を返す。
    hasIpAddress('IPアドレス')
    リクエストURL、およびJSPタグへのアクセス認可のみで、有効となる。
    指定のIPアドレスからのリクエストであれば、真を返す。
    その他、使用可能なSpring EL式は、 Common built-in expressionsを参照されたい。
    演算子を使用した判定も行うことができる。
    以下の例では、ロールと、リクエストされたIPアドレス両方に合致した場合、アクセス可能となる。
  • spring-security.xml

    <sec:http auto-config="true" use-expressions="true">
        <sec:intercept-url pattern="/admin/*" access="hasRole('ROLE_ADMIN') and hasIpAddress('192.168.10.1')"/>
        <!-- omitted -->
    </sec:http>
    
    使用可能な演算子一覧
    演算子 説明
    [式1] and [式2]
    式1、式2が、どちらも真の場合に、真を返す。
    [式1] or [式2]
    いずれかの式が、真の場合に、真を返す。
    ![式]
    式が真の場合は偽を、偽の場合は真を返す。

6.5.2.1.2. アクセス認可制御を行わないURLの設定

トップページやログイン画面、cssファイルへのパスなど、認証が必要のないURLに対しては、
http要素のpattern属性、およびsecurity属性を利用する。
  • spring-security.xml
<sec:http pattern="/css/*" security="none"/>  <!-- 属性の指定順番で(1)~(2) -->
<sec:http pattern="/login" security="none"/>
<sec:http auto-config="true" use-expressions="true">
    <!-- omitted -->
</sec:http>
項番
説明
(1)
pattern属性に設定を行う対象のURLパターンを記述する。pattern属性を記述しない場合、すべてのパターンにマッチする。
(2)
security属性にnoneを指定することで、pattern属性に記述されたパスは、Spring Securityフィルタチェインを回避することができる。

6.5.2.1.3. URLパターンでの例外処理

認可されていないURLにアクセスした場合、org.springframework.security.access.AccessDeniedExceptionがスローされる。
デフォルトの設定では、org.springframework.security.web.access.ExceptionTranslationFilterに設定された
org.springframework.security.web.access.AccessDeniedHandlerImplが、エラーコード403を返却する。
http要素に、アクセス拒否時のエラーページを設定することで、アクセス拒否時に指定のエラーページに遷移させることができる。
  • spring-security.xml

    <sec:http auto-config="true" use-expressions="true">
        <!-- omitted -->
        <sec:access-denied-handler error-page="/accessDeneidPage" />  <!-- (1) -->
    </sec:http>
    
    項番
    説明
    (1)
    <sec:access-denied-handler>要素のerror-page属性に、遷移先のパスを指定する。

6.5.2.2. アクセス認可(JSP)

画面表示項目を制御するには、Spring Securityが提供しているカスタムJSPタグ<sec:authorize>を利用する。
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
のタグライブラリの使用宣言設定をされていることが、前提条件である。
  • <sec:authorize>タグの属性一覧

    属性名
    説明
    access
    アクセス制御式を記述する。真であれば、タグ内が評価される。
    url
    設定したURLに対して権限が与えられている場合に、タグ内が評価される。リンクの表示の制御等に利用する。
    method
    HTTPメソッド(GETやPOST等)を指定する。 url属性と合わせて利用し、指定したメソッドのみに関して、
    指定したURLパターンとマッチングを行う。指定しない場合、GETが適用される。
    ifAllGranted
    設定したロールが全て与えられている場合に、タグ内が評価される。ロール階層機能は効かない。
    ifAnyGranted
    設定したロールについて、いずれかが与えられている場合に、タグ内が評価される。ロール階層機能は効かない。
    ifNotGranted
    設定されたロールが与えられていない場合、タグの中身が評価される。ロール階層機能は効かない。
    var
    タグの評価結果を格納するpageスコープの変数を宣言する。同等の権限チェックをページ内で行う場合に利用する。
以下に、<sec:authorize>タグの使用例を示す。
  • spring-security.xml

    <div>
      <sec:authorize access="hasRole('ROLE_USER')">  <!-- (1) -->
          <p>This screen is for ROLE_USER</p>
      </sec:authorize>
      <sec:authorize url="/admin/menu">  <!-- (2) -->
          <p>
            <a href="/admin/menu">Go to admin screen</a>
          </p>
      </sec:authorize>
    </div>
    
    項番
    説明
    (1)
    「ROLE_USER」を持つ場合のみ、タグ内が表示される。
    (2)
    「/admin/menu」に対してアクセスが認可されている場合、タグ内が表示される。

    Warning

    <sec:authorize>タグによる認可処理は、画面表示の制御でしかないため、特定の権限でリンクを表示されなくても、URLが推測されれば、直接リンク先のURLにアクセスできてしまう。 そのため、必ず、前述の「アクセス認可(リクエストURL)」、もしくは、後述の「アクセス認可(Method)」を併用して、本質的な認可制御をに行うこと。

6.5.2.3. アクセス認可(Method)

メソッドに対して、認可制御ができる。
SpringのDIコンテナで管理されているBeanが、認可の対象となる。
前述の2つの認可方法はアプリケーション層での認可制御であったが、
メソッドレベルの認可制御はドメイン層(Serviceクラス)に対して行う。
制御したいメソッドに対してorg.springframework.security.access.prepost.PreAuthorizeアノテーションを設定すればよい。
  • spring-security.xml

    <sec:global-method-security pre-post-annotations="enabled"/>  <!-- (1) -->
    
    項番
    説明
    (1)
    <sec:global-method-security>要素のpre-post-annotations属性をenabledに指定する。
    デフォルトはdisabledである。
  • Javaコード

    @Service
    @Transactional
    public class UserServiceImpl implements UserService {
        // omitted
    
        @PreAuthorize("hasRole('ROLE_ADMIN')") // (1)
        @Override
        public User create(User user) {
           // omitted
        }
    
    
        @PreAuthorize("isAuthenticated()")
        @Override
        public User update(User user) {
           // omitted
        }
    }
    
    項番
    説明
    (1)
    アクセス制御式を記述する。メソッドを実行する前に式が評価され、真であれば、メソッドが実行される。
    偽であれば、org.springframework.security.access.AccessDeniedExceptionがスローされる。
    設定可能な値は、<sec:intercept-url>要素の設定で述べたExpressionや、および
    Spring Expression Language (SpEL)で記述された式である。

    Tip

    上記の設定ではorg.springframework.security.access.prepost.PreAuthorize以外にも、以下のアノテーションを使用できる。

    • org.springframework.security.access.prepost.PostAuthorize
    • org.springframework.security.access.prepost.PreFilter
    • org.springframework.security.access.prepost.PostFilter

    これらの詳細はSpring Security マニュアルを参照されたい。

    Note

    Spring SecurityではJava標準であるJSR-250のjavax.annotation.security.RolesAllowedアノテーションによる認可制御も可能であるが、 @RolesAllowedではSpELによる記述ができない。@PreAuthorizeであればSpELを用いて、spring-security.xmlの設定と同じ記法で認可制御

    Note

    リクエストパスに対する認可制御はControllerのメソッドにアノテーションをつけるのではなく、spring-security.xmlに設定を行うことを推奨する。

    ServiceがWeb経由でしか実行されず、リクエストパスのすべてのパターンが認可制御されているのであればServiceの認可制御は行わなくても良い。 Serviceがどこから実行されるか分からず、認可制御が必要な場合にアノテーションを使用するとよい。

6.5.3. How to extend

6.5.3.1. ロール階層機能

ロールに階層関係を設定することができる。
上位に設定したロールは、下位ロールに認可されたすべてのアクセスが可能となる。
ロールの関係が複雑な場合は、階層機能を検討されたい。
ROLE_ADMINを上位ロール、ROLE_USERを下位ロールとして階層関係を設定する例で説明する。
RoleHierarchy

Picture - RoleHierarchy

このとき、下記のようにアクセス認可を設定すると、
「ROLE_ADMIN」のロールを持つユーザも、「/user/*」のURLにアクセスできる。

Spring Security 設定ファイル

<sec:http auto-config="true" use-expressions="true">
    <sec:intercept-url pattern="/user/*" access="hasAnyRole('ROLE_USER')" />
    <!-- omitted -->
</sec:http>
アクセス認可(リクエストURL)、アクセス認可(JSP)、アクセス認可(Method)のそれぞれで設定方法が異なるため、
使用方法について、以降で説明する。

6.5.3.1.1. 共通設定

共通で必要な設定について述べる。
階層関係を管理するorg.springframework.security.access.hierarchicalroles.RoleHierarchy クラスのBean定義を行う。
  • spring-security.xml

    <bean id="roleHierarchy"
        class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl"> <!-- (1) -->
        <property name="hierarchy">
            <value> <!-- (2) -->
                ROLE_ADMIN > ROLE_STAFF
                ROLE_STAFF > ROLE_USER
            </value>
        </property>
    </bean>
    
    項番
    説明
    (1)
    RoleHierarchyのデフォルト org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl クラスを指定する。
    (2)
    hierarchyプロパティに階層関係を定義する。
    書式:
    [上位ロール] > [下位ロール]
    例では、STAFFはUSERに認可されたすべてのリソースに、アクセスできる。
    ADMINはUSER、STAFFに認可されたすべてのリソースに、アクセスできる。

6.5.3.1.2. アクセス認可(リクエストURL)、アクセス認可(JSP)での使用方法

リクエストURL、JSPに対するロール階層の設定について述べる。
  • spring-security.xml

    <bean id="webExpressionHandler"
        class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">  <!-- (1) -->
        <property name="roleHierarchy" ref="roleHierarchy"/>  <!-- (2) -->
    </bean>
    
    <sec:http auto-config="true" use-expression="true">
        <!-- omitted -->
        <sec:expression-handler ref="webExpressionHandler" />  <!-- (3) -->
    </sec:http>
    
    項番
    説明
    (1)
    クラスにorg.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandlerを指定する。
    (2)
    roleHierarchyプロパティにRoleHierarchyのBean IDをプロパティに設定する。
    (3)
    expression-handler要素に、org.springframework.security.access.expression.SecurityExpressionHandlerを実装したハンドラクラスのBean IDを指定する。

6.5.3.1.3. アクセス認可(Method)での使用方法

Serviceのメソッドにアノテーションをつけて認可制御を行う場合のロール階層設定について説明する。
  • spring-security.xml

    <bean id="methodExpressionHandler"
        class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler"> <!-- (1) -->
        <property name="roleHierarchy" ref="roleHierarchy"/> <!-- (2) -->
    </bean>
    
    <sec:global-method-security pre-post-annotations="enabled">
        <sec:expression-handler ref="methodExpressionHandler" /> <!-- (3) -->
    </sec:global-method-security>
    
    項番
    説明
    (1)
    クラスにorg.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandlerを指定する。
    (2)
    roleHierarchyプロパティにRoleHierarchyのBean IDをプロパティに設定する。
    (3)
    expression-handler要素に、org.springframework.security.access.expression.SecurityExpressionHandlerを実装したハンドラクラスのBean IDを指定する。