9.3. 認可¶
9.3.1. Overview¶
本節では、Spring Securityが提供している認可機能について説明する。
Webリソース
Javaメソッド
ドメインオブジェクト [1]
画面項目
本節では、「Webリソース」「Javaメソッド」「画面項目」のアクセスに対して認可処理を適用するための実装例(定義例)を紹介しながら、Spring Securityの認可機能について説明する。
9.3.1.1. 認可処理のアーキテクチャ¶
Spring Securityは、以下のような流れで認可処理を行う。
項番 |
説明 |
---|---|
(1)
|
クライアントは、任意のリソースにアクセスする。
|
(2)
|
AuthorizationFilter クラスは、AuthorizationManager インタフェースのメソッドを呼び出し、リソースへのアクセス権の有無をチェックする。 |
(3)
|
AuthorizationManager の実装クラスであるRequestMatcherDelegatingAuthorizationManager が、受け取ったリクエストを適切なAuthorizationManager に振り分けてアクセス権の有無をチェックする。 |
(4)
|
AuthorizationFilter は、AuthorizationManager によってアクセス権が付与された場合に限り、リソースへアクセスする。 |
9.3.1.1.1. ExceptionTranslationFilter¶
ExceptionTranslationFilter
は、認可処理(AuthorizationManager
)で発生した例外をハンドリングし、クライアントへ適切なレスポンスを行うためのSecurity Filterである。9.3.1.1.2. AuthorizationFilter¶
AuthorizationFilter
は、HTTPリクエストに対して認可処理を適用するためのSecurity Filterで、実際の認可処理はAuthorizationManager
に委譲する。AuthorizationManager
インタフェースのメソッドを呼び出す際には、クライアントがアクセスしようとしたリソースに指定されているアクセスポリシーを連携する。AuthorizationFilter
はFilterChain
を続行する。9.3.1.1.3. AuthorizationManager¶
AuthorizationManager
は、アクセスしようとしたリソースに対してアクセス権があるかチェックを行うためのインタフェースである。AccessDeniedException
を発生させアクセスを拒否する。クラス名 |
説明 |
---|---|
RequestMAtcherDelegatingAuthorizationManager |
リクエストに一致する
RequestMatcher を基に、認可処理を特定のAuthorizationManager に移譲する。 |
AuthorityAuthorizationManager |
Spring Securityが提供する一般的な
AuthorizationManager 。認証情報(
Authentication )に指定された権限が含まれているかどうかを評価し、現在のユーザーが認可されているかどうかを判別する。 |
AuthenticatedAuthorizationManager |
匿名ユーザー、完全認証ユーザー、リメンバー認証ユーザーを区別するために使用される。
|
JSR250AuthorizationManager |
認証情報(
Authentication )がJSR-250セキュリティアノテーションから指定された権限を含んでいるかどうかを評価する。 |
SecuredAuthorizationManager |
認証情報(
Authentication )がSpring SecurityのSecured アノテーションから指定された権限を含んでいるかどうかを評価する。 |
PreAuthorizeAuthorizationManager |
認証情報(
Authentication )がPreAuthorize アノテーションから指定された権限を含んでいるかどうかを評価する。 |
PreAuthorizaAuthorizationMAnager |
認証情報(
Authentication )がPostAuthorize アノテーションから指定された権限を含んでいるかどうかを評価する。 |
AuthorizationManager
以外に、独自に構築したRequestMatcherDelegatingAuthorizationManager
を使用することも可能である。9.3.2. How to use¶
9.3.2.1. アクセスポリシーの記述方法¶
アクセスポリシーの記述方法を説明する。
HttpSecurity
に対するメソッドチェーンをサポートしている。<intercept-url>
JSP Taglibなど、式が必要な場合に備えSpring Expression Language(SpEL)をサポートしている。Tip
SpELの使い方については本節でも紹介するが、より詳しい使い方を知りたい場合はSpring Framework Documentation -Spring Expression Language (SpEL)-を参照されたい。
9.3.2.1.1. 代表的な認可メソッド¶
HttpSecurity#authorizeHttpRequests(Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequestsCustomizer)
のCustomizerを実装し、URLパターン経由でHttpServletRequestに基づいてアクセスを制限することができる。メソッド名 |
説明 |
---|---|
hasRole(String role) |
ログインユーザーが、引数に指定したロールを保持している場合に
true を返却する。ロールの
ROLE_ プレフィックスは省略可能である。 |
hasAnyRole(String... roles) |
ログインユーザーが、引数に指定したロールのいずれかを保持している場合に
true を返却する。ロールの
ROLE_ プレフィックスは省略可能である。 |
anonymous() |
ログインしていない匿名ユーザーの場合に
true を返却する。 |
rememberMe() |
Remember Me認証によってログインしたユーザーの場合に
true を返却する。 |
authenticated() |
ログイン中の場合に
true を返却する。 |
fullyAuthenticated() |
Remember Me認証ではなく通常の認証プロセスによってログインしたユーザーの場合に
true を返却する。 |
permitAll() |
常に
true を返却する。 |
denyAll() |
常に
false を返却する。(デフォルト値) |
access(AuthorizationManager<RequestAuthorizationContext> manager) |
カスタム
AuthorizationManager を使用して認可処理を実施する。 |
Note
Spring Securityの認可処理のデフォルト値はdenyAll
であるため、業務要件に応じ適切に認可する範囲を指定する必要がある。
9.3.2.1.2. Built-InのCommon Expressions¶
Spring Securityが用意している共通的なExpressionは以下の通り。
Expression |
説明 |
---|---|
hasRole(String role) |
ログインユーザーが、引数に指定したロールを保持している場合に
true を返却する。ロールの
ROLE_ プレフィックスは省略可能である。 |
hasAnyRole(String... roles) |
ログインユーザーが、引数に指定したロールのいずれかを保持している場合に
true を返却する。ロールの
ROLE_ プレフィックスは省略可能である。 |
isAnonymous() |
ログインしていない匿名ユーザーの場合に
true を返却する。 |
isRememberMe() |
Remember Me認証によってログインしたユーザーの場合に
true を返却する。 |
isAuthenticated() |
ログイン中の場合に
true を返却する。 |
isFullyAuthenticated() |
Remember Me認証ではなく通常の認証プロセスによってログインしたユーザーの場合に
true を返却する。 |
permitAll |
常に
true を返却する。 |
denyAll |
常に
false を返却する。(デフォルト値) |
principal |
認証されたユーザーのユーザー情報(
UserDetails インタフェースを実装したクラスのオブジェクト)を返却する。 |
authentication |
認証されたユーザーの認証情報(
Authentication インタフェースを実装したクラスのオブジェクト)を返却する。 |
Note
Expressionを使用した認証情報へのアクセス
Expressionとしてprincipal
やauthentication
を使用すると、ログインユーザーのユーザー情報や認証情報を参照することができるため、ロール以外の属性を使ってアクセスポリシーを設定することが可能になる。
Note
Spring Secuirtyが提供するその他のExpression
上記に記載した以外にも、Spring Securityではログインユーザーが保持する権限を確認するExpressionとして、hasAuthority(String authority)
、hasAnyAuthority(String... authorities)
、hasPermission(Object target, Object permission)
、hasPermission(Object targetId, String targetType, Object permission)
を提供している。
ユーザの属性により権限をグループ化したものがロールであり、一般的には個々の権限による認可ではなくロールによる認可が推奨される。
Spring Securityの認可においてはいずれもログインユーザが「指定した権限(ロール)を保持しているか」を確認するため利用方法に違いはないが、権限名はロール名と異なりROLE_
のようなプレフィックスがないため、権限の定義と認可で名称を完全一致させる必要がある。
Note
Spring Securityの認可処理のデフォルト値はdenyAll
であるため、業務要件に応じ適切に認可する範囲を指定する必要がある。
9.3.2.1.3. Built-InのWeb Expressions¶
Spring Securityが用意しているWebアプリケーション向けExpressionは以下の通り。
Expression |
説明 |
---|---|
hasIpAddress(String ipAddress) |
リクエスト元のIPアドレスが、引数に指定したIPアドレス体系に一致する場合に
true を返却する。 |
9.3.2.1.4. 演算子の使用¶
SpringSecurityConfig.javaの定義例
@Bean public SecurityFilterChain filterChain(HttpSecurity http) { // omitted http.authorizeHttpRequests(authz -> authz // omitted .access(new WebExpressionAuthorizationManager("hasRole('ADMIN') and hasIpAddress('192.168.10.1')")) // omitted ); return http.build(); }
spring-security.xmlの定義例
<sec:http request-matcher="ant"> <sec:intercept-url pattern="/admin/**" access="hasRole('ADMIN') and hasIpAddress('192.168.10.1')"/> <!-- omitted --> </sec:http>
使用可能な演算子一覧
演算子 |
説明 |
---|---|
[式1] and [式2] |
式1、式2が、どちらも真の場合に、真を返す。
|
[式1] or [式2] |
いずれかの式が、真の場合に、真を返す。
|
![式] |
式が真の場合は偽を、偽の場合は真を返す。
|
9.3.2.2. Webリソースへの認可¶
Spring Securityは、サーブレットフィルタの仕組みを利用してWebリソース(HTTPリクエスト)に対して認可処理を行う。
9.3.2.2.1. 認可処理の適用¶
Webリソースに対して認可処理を適用する場合は、以下のようなbean定義を行う。
SpringSecurityConfig.javaの定義例
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
// omitted
http.authorizeHttpRequests(authz -> authz
.requestMatchers(new AntPathRequestMatcher("/**")).authenticated() // (1)
);
// omitted
return http.build();
}
項番 |
説明 |
---|---|
(1)
|
HttpSecurity のメソッドチェーンにより、HTTPリクエストに対してアクセスポリシーを定義する。ここでは、
authenticated() メソッドを呼び出し「Webアプリケーション配下の全てのリクエストに対して認証済みのユーザーのみアクセスを許可する」というアクセスポリシーを定義している。 |
spring-security.xmlの定義例
<sec:http request-matcher="ant">
<!-- omitted -->
<sec:intercept-url pattern="/**" access="isAuthenticated()" /> <!-- (1) -->
<!-- omitted -->
</sec:http>
項番 |
説明 |
---|---|
(1)
|
<sec:intercept-url> タグに、HTTPリクエストに対してアクセスポリシーを定義する。ここでは、SpELを使用して「Webアプリケーション配下の全てのリクエストに対して認証済みのユーザーのみアクセスを許可する」というアクセスポリシーを定義している。
|
9.3.2.2.2. アクセスポリシーの定義¶
bean定義ファイルを使用して、Webリソースに対してアクセスポリシーを定義する方法について説明する。
9.3.2.2.2.1. アクセスポリシーを適用するWebリソースの指定¶
HttpSecurity#authorizeHttpRequests(Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequestsCustomizer)
のCustomizerを実装し、アクセスポリシーを設定する。
SpringSecurityConfig.javaの定義例
@Bean public SecurityFilterChain filterChain(HttpSecurity http) { // omitted http.authorizeHttpRequests(authz -> authz .requestMatchers(new AntPathRequestMatcher("/admin/accounts/**", HttpMethod.GET.name())).hasRole("ACCOUNT_MANAGER") // (1)(4) .requestMatchers(new AntPathRequestMatcher("/admin/configurations/**")) .access(new WebExpressionAuthorizationManager("hasIpAddress('127.0.0.1') and hasRole('CONFIGURATION_MANAGER')")) // (2) .requestMatchers(new AntPathRequestMatcher("/admin/**")).hasAnyRole("USER", "ADMIN") .requestMatchers(new AntPathRequestMatcher("/**")).denyAll() ); http.requiresChannel(channel -> channel.anyRequest().requiresSecure()); // (3) // omitted return http.build(); }
¶ 項番
説明
(1)パスパターンに一致するリソースを適用対象とするため、AuthorizationManagerRequestMatcherRegistry.requestMatchers
にRequestMatcher
オブジェクトを設定する。上記設定例ではAntPathRequestMatcher
を指定している。設定できる項目は以下となる。¶ 変数名
説明
pattern
Ant形式又は正規表現で指定したパスパターンに一致するリソースを適用対象にする。httpMethod
指定したHTTPメソッド(GET,POSTなど)を使ってアクセスがあった場合に適用対象にする。(2)Expressionsを使用する場合はAuthorizedUrl#access
を使用し、WebExpressionAuthorizationManager
を設定する。WebExpressionAuthorizationManager
のコンストラクタ引数に認可制御用のExpressionsを設定する。(3)HttpSecurity#requiresChannel(Customizer<ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry> requiresChannelCustomizer)
のCustomizerを実装し、指定したプロトコルを強制することができる。上記設定例ではhttp
でリクエストされた際にhttps
へリダイレクトする処理となる。(4)requestMatchers
が返却する
<sec:intercept-url>
タグに、HTTPリクエストに対してアクセスポリシーを定義する。
spring-security.xmlの定義例
<sec:http request-matcher="ant">
<sec:intercept-url pattern="/admin/accounts/**" method="GET" requires-channel="https" access="hasRole('ACCOUNT_MANAGER')"/> <!-- (1) -->
<sec:intercept-url pattern="/admin/configurations/**" access="hasIpAddress('127.0.0.1') and hasRole('CONFIGURATION_MANAGER')" />
<sec:intercept-url pattern="/admin/**" access="hasRole('ADMIN')" />
<!-- omitted -->
</sec:http>
項番 |
説明 |
||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
(1)
|
パスパターンに一致するリソースを適用対象とするため、
<sec:intercept-url> にリソースを設定する。設定できる属性は以下となる。
|
Warning
Spring Security 4.1以降、Spring Securityがデフォルトで使用しているAntPathRequestMatcher のパスマッチングの仕様が大文字・小文字を区別する様になった。
例えば以下に示すように、/Todo/List
というパスが割り当てられたSpring MVCのエンドポイントに対してアクセスポリシーを定義する場合は、パスパターンについて/Todo/List
や /Todo/*
のように大文字・小文字をそろえる必要がある。
誤って/todo/list
や/todo/**
など大文字・小文字がそろっていない値を指定してしまうと、意図した認可制御が行われなくなるので注意されたい。
Spring MVCのエンドポイントの実装例
@RequestMapping(value="/Todo/List") public String viewTodoList(){ // omitted }
アクセスポリシーの定義例
@Bean public SecurityFilterChain filterChain(HttpSecurity http) { // omitted http.authorizeHttpRequests(authz -> authz .requestMatchers(new AntPathRequestMatcher("/Todo/List")).authenticated() // omitted ); // omitted return http.build(); }
<sec:http request-matcher="ant"> <sec:intercept-url pattern="/Todo/List" access="isAuthenticated()" /> <!-- omitted --> </sec:http>
Note
Spring MVCとSpring Securityでは、リクエストとのマッチングの仕組みが厳密には異なっており、この差異を利用してSpring Securityの認可機能を突破し、ハンドラメソッドにアクセスできる脆弱性が存在する。
本事象の詳細は「CVE-2016-5007 Spring Security / MVC Path Matching Inconsistency」を参照されたい。
trimTokens
プロパティにtrue
を設定したorg.springframework.util.AntPathMatcher
のBeanがSpring MVCに適用されている場合に、本事象が発生する。
デフォルト値はfalse
であるため、意図的に変更しない限り本事象は発生しない。
9.3.2.2.2.2. アクセスポリシーの設定例¶
SpringSecurityConfig.java
@Bean public SecurityFilterChain filterChain(HttpSecurity http) { // omitted http.authorizeHttpRequests(authz -> authz .requestMatchers(new AntPathRequestMatcher("/reserve/**")).hasAnyRole("USER", "ADMIN") // (1) .requestMatchers(new AntPathRequestMatcher("/admin/**")).hasRole("ADMIN") // (2) .requestMatchers(new AntPathRequestMatcher("/**")).denyAll() // (3) ); // omitted return http.build(); }
項番
説明
(1)「/reserve/**
」にアクセスするためには、「ROLE_USER」もしくは「ROLE_ADMIN」ロールが必要である。ログインユーザーが指定したロールを保持していれば真を返す。(2)「/admin/**
」にアクセスするためには、「ROLE_ADMIN」ロールが必要である。ログインユーザーが指定したロールを保持していれば真を返す。(3)denyAll
を全てのパターンに設定し、権限設定が記述されていないURLに対してはどのユーザーもアクセス出来ない設定としている。
spring-security.xml
<sec:http request-matcher="ant"> <sec:intercept-url pattern="/reserve/**" access="hasAnyRole('USER','ADMIN')" /> <!-- (1) --> <sec:intercept-url pattern="/admin/**" access="hasRole('ADMIN')" /> <!-- (2) --> <sec:intercept-url pattern="/**" access="denyAll" /> <!-- (3) --> <!-- omitted --> </sec:http>
項番
説明
(1)「/reserve/**
」にアクセスするためには、「ROLE_USER」もしくは「ROLE_ADMIN」ロールが必要である。ログインユーザーが指定したロールを保持していれば真を返す。(2)「/admin/**
」にアクセスするためには、「ROLE_ADMIN」ロールが必要である。ログインユーザーが指定したロールを保持していれば真を返す。(3)denyAll
を全てのパターンに設定し、権限設定が記述されていないURLに対してはどのユーザーもアクセス出来ない設定としている。
Note
URLパターンの記述順序について
クライアントからのリクエストに対して、intercept-urlで記述されているパターンに、上から順にマッチさせ、マッチしたパターンに対してアクセス認可を行う。
そのため、パターンの記述は、必ず、より限定されたパターンから記述すること。
9.3.2.2.2.3. パス変数の参照¶
Spring Security 4.1以降では、アクセスポリシーを適用するリソースを指定する際にパス変数[2]を使用することができ、アクセスポリシーの定義内で#パス変数名
と指定することで参照できる。
ただし、拡張子を付けてアクセス可能なパスに対してパス変数を使用するアクセスポリシーを定義する場合は、パス変数値に拡張子部分が格納されない様に定義する必要がある。
例えば、パターンに/users/{userName}
と定義し、/users/personName.json
というリクエストパスを送信した際、アクセスポリシーの定義内で参照しているパス変数#userName
にはpersonName
ではなくpersonName.json
が格納され、意図しない認可制御が行われてしまう。
この事象を防ぐためには、「拡張子を付けたパスに対するアクセスポリシー」を定義した後に、「拡張子を付けないパスに対するアクセスポリシー」を定義する必要がある。
以下の例は、ログインユーザが自身のユーザ情報のみアクセスできる様にアクセスポリシーを定義している。
ワイルドカードを使用する場合
SpringSecurityConfig.javaの定義例
@Bean public SecurityFilterChain filterChain(HttpSecurity http) { // omitted http.authorizeHttpRequests(authz -> authz .requestMatchers(new AntPathRequestMatcher("/users/{userName}.*")) .access(new WebExpressionAuthorizationManager("isAuthenticated() and #userName == principal.username")) // (1) .requestMatchers(new AntPathRequestMatcher("/users/{userName}/**")) .access(new WebExpressionAuthorizationManager("isAuthenticated() and #userName == principal.username")) // (2) // omitted ); // omitted return http.build(); }
項番
説明
(1)「拡張子を付けたパスに対するアクセスポリシー」を定義する。(2)「拡張子を付けないパスに対するアクセスポリシー」を定義する。ワイルドカードを使用して/users/{userName}
で始まるパスに対するアクセスポリシーを定義する。<sec:http request-matcher="ant"> <!-- (1) --> <sec:intercept-url pattern="/users/{userName}.*" access="isAuthenticated() and #userName == principal.username"/> <!-- (2) --> <sec:intercept-url pattern="/users/{userName}/**" access="isAuthenticated() and #userName == principal.username"/> <!-- omitted --> </sec:http>
項番
説明
(1)「拡張子を付けたパスに対するアクセスポリシー」を定義する。(2)「拡張子を付けないパスに対するアクセスポリシー」を定義する。ワイルドカードを使用して/users/{userName}
で始まるパスに対するアクセスポリシーを定義する。
ワイルドカードを使用しない場合
SpringSecurityConfig.javaの定義例
@Bean public SecurityFilterChain filterChain(HttpSecurity http) { // omitted http.authorizeHttpRequests(authz -> authz .requestMatchers(new AntPathRequestMatcher("/users/{userName}.*")) .access(new WebExpressionAuthorizationManager("isAuthenticated() and #userName == principal.username")) // (1) .requestMatchers(new AntPathRequestMatcher("/users/{userName}/")) .access(new WebExpressionAuthorizationManager("isAuthenticated() and #userName == principal.username")) // (2) .requestMatchers(new AntPathRequestMatcher("/users/{userName}")) .access(new WebExpressionAuthorizationManager("isAuthenticated() and #userName == principal.username")) // (2) // omitted ); // omitted return http.build(); }
項番
説明
(1)「拡張子を付けたパスに対するアクセスポリシー」を定義する。(2)「拡張子を付けないパスに対するアクセスポリシー」を定義する。ワイルドカードを使用しない場合、Spring MVCとSpring Securityのパスマッチングの差を吸収するために末尾が”/
“で終わるパスに対するアクセスポリシーも定義する。spring-security.xmlの定義例
<sec:http request-matcher="ant"> <!-- (1) --> <sec:intercept-url pattern="/users/{userName}.*" access="isAuthenticated() and #userName == principal.username"/> <!-- (2) --> <sec:intercept-url pattern="/users/{userName}/" access="isAuthenticated() and #userName == principal.username"/> <sec:intercept-url pattern="/users/{userName}" access="isAuthenticated() and #userName == principal.username"/> <!-- omitted --> </sec:http>
項番
説明
(1)「拡張子を付けたパスに対するアクセスポリシー」を定義する。(2)「拡張子を付けないパスに対するアクセスポリシー」を定義する。ワイルドカードを使用しない場合、Spring MVCとSpring Securityのパスマッチングの差を吸収するために末尾が”/
“で終わるパスに対するアクセスポリシーも定義する。
パス変数の説明はアプリケーション層の実装のURLのパスから値を取得するを参照されたい。
9.3.2.3. メソッドへの認可¶
Spring Securityは、Spring AOPの仕組みを利用してDIコンテナで管理しているBeanのメソッド呼び出しに対して認可処理を行う。
9.3.2.3.1. AOPの有効化¶
Spring Securityは、以下のアノテーションをサポートしている。
@PreAuthorize
、@PostAuthorize
、@PreFilter
、@PostFilter
JSR-250 (
jakarta.annotation.security
パッケージ)のアノテーション(@RolesAllowed
など)@Secured
本ガイドラインでは、アクセスポリシーをExpressionで使用することができる@PreAuthorize
、@PostAuthorize
を使用する方法を説明する。
SpringSecurityConfig.javaの定義例
@Configuration
@EnableMethodSecurity // (1)
public class SpringSecurityConfig {
項番 |
説明 |
---|---|
(1)
|
@EnableMethodSecurity アノテーションをコンフィグレーションクラスに付与すると、メソッド呼び出しに対する認可処理を行うAOPが有効になる。なお、
prePostEnabled プロパティはデフォルトでtrue となっており、Expressionを指定してアクセスポリシーを定義できるアノテーションが有効となっている。 |
spring-security.xmlの定義例
<sec:method-security /> <!-- (1) -->
項番 |
説明 |
---|---|
(1)
|
<sec:method-security> タグを付与すると、メソッド呼び出しに対する認可処理を行うAOPが有効になる。なお、
pre-post-annotations 属性はデフォルトでtrue となっており、Expressionを指定してアクセスポリシーを定義できるアノテーションが有効となっている。 |
9.3.2.3.2. 認可処理の適用¶
メソッドに対して認可処理を適用する際は、アクセスポリシーを指定するアノテーションを使用して、メソッド毎にアクセスポリシーを定義する。
9.3.2.3.3. アクセスポリシーの定義¶
9.3.2.3.3.1. メソッド実行前に適用するアクセスポリシーの指定¶
メソッドの実行前に適用するアクセスポリシーを指定する場合は、@PreAuthorize
を使用する。
@PreAuthorize
のvalue
属性に指定したExpressionの結果がtrue
になるとメソッドの実行が許可される。@PreAuthorize
の定義例
// (1) (2)
@PreAuthorize("hasRole('ADMIN') or (#username == principal.username)")
public Account findOne(String username) {
return accountRepository.findByUsername(username);
}
項番 |
説明 |
---|---|
(1)
|
認可処理を適用したいメソッドに、
@PreAuthorize を付与する。 |
(2)
|
value 属性に、メソッドに対してアクセスポリシーを定義する。ここでは、「管理者の場合は全てのアカウントへのアクセスを許可する」「管理者以外の場合は自身のアカウントへのアクセスのみ許可する」というアクセスポリシーを定義している。
|
#username
」の部分が引数にアクセスしている部分である。Tip
引数名を指定するアノテーション
Spring Securityは、クラスに出力されているデバッグ情報から引数名を解決する仕組みになっているが、アノテーション(@org.springframework.security.core.parameters.P
)を使用して明示的に引数名を指定することもできる。
以下のケースにあてはまる場合は、アノテーションを使用して明示的に変数名を指定する。
クラスに変数のデバッグ情報を出力しない
Expressionの中から実際の変数名とは別の名前を使ってアクセスしたい (例えば短縮した名前)
@PreAuthorize("hasRole('ADMIN') or (#username == principal.username)") public Account findOne(@P("username") String username) { return accountRepository.findByUsername(username); }
なお、#username
と、メソッドの引数である username
の名称が一致している場合は @P
を省略することが可能である。
ただし、Spring Securityは引数名の解決を、実装クラスの引数名を使用して行っているため@PreAuthorize
アノテーションをインターフェースに定義している場合には、実装クラスの引数名を、 @PreAuthorize 内で指定した #username と一致させる必要があるので、注意されたい。
JDK 8 から追加されたコンパイルオプション(-parameters
)を使用すると、メソッドパラメータにリフレクション用のメタデータが生成されるため、アノテーションを指定しなくても引数名が解決される。
Warning
Spring 5から、SpringのコアAPIにnull-safetyの機能が取り入れられており、SpELが解釈される際のnull
に対する動作も変更(SPR-15540)されている。
例えば@PreAuthorize
の引数(#xxx
)や、@PostAuthorize
の戻り値(resultObject
)がMap
を含む場合、Map
から値を取得するSpELでキー値にnull
となる値を入力すると、Spring 4以前ではそのままMap
にnull
が渡され該当する値がないためnull
が返却されていたが、Spring 5以降ではキーとなるSpELを評価した結果に対するnull
チェックが追加されており、null
の場合はIllegalStateException
が発生する。
そのため、キーとする値に対して事前にnull
チェックを行うなど、null
を考慮した実装が必要となる。
9.3.2.3.3.2. メソッド実行後に適用するアクセスポリシーの指定¶
メソッドの実行後に適用するアクセスポリシーを指定する場合は、@PostAuthorize
を使用する。
@PostAuthorize
のvalue
属性に指定したExpressionの結果がtrue
になるとメソッドの実行結果が呼び出し元に返却される。@PostAuthorize
の定義例
@PreAuthorize("...")
@PostAuthorize("(returnObject == null) " +
"or (returnObject.departmentCode == principal.account.departmentCode)")
public Account findOne(String username) {
return accountRepository.findByUsername(username);
}
returnObject.departmentCode
」の部分が返り値にアクセスしている部分である。returnObject
」を指定すると、メソッドの返り値にアクセスすることができる。9.3.2.4. 画面項目への認可¶
ここでは最もシンプルな定義を例に、画面項目のアクセスに対して認可処理を適用する方法について説明する。
9.3.2.4.1. アクセスポリシーの定義¶
JSPタグライブラリを使用してJSPの画面項目に対してアクセスポリシーを定義する際は、表示を許可する条件(アクセスポリシー)をJSPに定義する。
アクセスポリシー定義例
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<!-- (1) -->
<sec:authorize access="hasRole('ADMIN')"> <!-- (2) -->
<h2>Admin Menu</h2>
<!-- omitted -->
</sec:authorize>
項番 |
説明 |
---|---|
(1)
|
アクセスポリシーを適用したい部分を
<sec:authorize> タグで囲む。 |
(2)
|
access 属性にアクセスポリシーを定義する。ここでは、「管理者の場合は表示を許可する」というアクセスポリシーを定義している。 |
Spring Security Dialectを使用して画面項目に対してアクセスポリシーを定義する際は、表示を許可する条件(アクセスポリシー)をHTMLに定義する。
アクセスポリシー定義例
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<!--/* (1) */-->
<div sec:authorize="hasRole('ADMIN')"> <!--/* (2) */-->
<h2>Admin Menu</h2>
<!--/* omitted */-->
</div>
項番 |
説明 |
---|---|
(1)
|
アクセスポリシーを適用したい部分を
sec:authorize 属性を記述したタグで囲む。 |
(2)
|
属性値にアクセスポリシーを定義する。ここでは、「管理者の場合は表示を許可する」というアクセスポリシーを定義している。
|
9.3.2.4.2. Webリソースに指定したアクセスポリシーとの連動¶
<sec:authorize>
タグのurl
属性を使用する。url
属性に指定したWebリソースにアクセスできる場合に限り<sec:authorize>
タグの中に実装したJSPの処理が実行される。
Webリソースに定義されているアクセスポリシーとの連携例
<ul>
<!-- (1) -->
<sec:authorize url="/admin/accounts"> <!-- (2) -->
<li>
<a href="<c:url value='/admin/accounts' />">Account Management</a>
</li>
</sec:authorize>
</ul>
項番 |
説明 |
---|---|
(1)
|
ボタンやリンクを出力する部分を
<sec:authorize> タグで囲む。 |
(2)
|
<sec:authorize> タグのurl 属性にWebリソースへアクセスするためのURLを指定する。ここでは、「
/admin/accounts というURLが割り振られているWebリソースにアクセス可能な場合は表示を許可する」というアクセスポリシーを定義しており、Webリソースに定義されているアクセスポリシーを直接意識する必要がない。 |
Note
HTTPメソッドによるポリシーの指定
Webリソースのアクセスポリシーの定義をする際に、HTTPメソッドによって異なるアクセスポリシーを指定している場合は、<sec:authorize>
タグのmethod
属性を指定して、連動させる定義を特定すること。
Warning
表示制御に関する留意点
ボタンやリンクなどの表示制御を行う場合は、必ずWebリソースに定義されているアクセスポリシーと連動させること。
ボタンやリンクに対して直接アクセスポリシーの指定を行い、Webリソース自体にアクセスポリシーを定義していないと、URLを直接してアクセスするような不正なアクセスを防ぐことができない。
sec:authorize-url
属性を使用する。sec:authorize-url
属性に指定したWebリソースにアクセスできる場合に限りsec:authorize-url
属性を付与したタグの中に実装したThymeleafの処理が実行される。
Webリソースに定義されているアクセスポリシーとの連携例
<ul>
<!--/* (1) */-->
<li sec:authorize-url="/admin/accounts"> <!--/* (2) */-->
<a th:href="@{/admin/accounts}">Account Management</a>
</li>
</ul>
項番 |
説明 |
---|---|
(1)
|
ボタンやリンクを出力する部分を
sec:authorize-url 属性を記述したタグで囲む。 |
(2)
|
sec:authorize-url 属性にWebリソースへアクセスするためのURLを指定する。ここでは、「
/admin/accounts というURLが割り振られているWebリソースにアクセス可能な場合は表示を許可する」というアクセスポリシーを定義しており、Webリソースに定義されているアクセスポリシーを直接意識する必要がない。 |
Note
HTTPメソッドによるポリシーの指定
Webリソースのアクセスポリシーの定義をする際に、HTTPメソッドによって異なるアクセスポリシーを指定している場合は、sec:authorize-url
属性の前半にmethodを指定して、スペースで区切りURLを記載することによって連動させる定義を特定すること。
Warning
表示制御に関する留意点
ボタンやリンクなどの表示制御を行う場合は、必ずWebリソースに定義されているアクセスポリシーと連動させること。
ボタンやリンクに対して直接アクセスポリシーの指定を行い、Webリソース自体にアクセスポリシーを定義していないと、URLを直接してアクセスするような不正なアクセスを防ぐことができない。
Tip
#authorizationの紹介
ここでは、sec:authorize
属性やsec:authorize-url
属性を用いて、画面項目に対してアクセスポリシーを定義する実装例を説明したが、
#authorization
を用いても、ThymeleafのテンプレートHTMLから認可情報にアクセスする事が可能である。
#authorization
は、変数式 ${}
にて使用できるため、条件判定やリテラル置換等sec:authorize
属性やsec:authorize-url
属性より複雑な使い方が可能である。
上記の例は、以下のように記述できる
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security"><!--/* (1) */--> <!--/* omitted */--> <div th:if="${#authorization.expr('isAuthenticated()')}"> <!--/* (2) */--> <!--/* omitted */--> </div> <div th:if="${#authorization.url('/admin/accounts')}"> <!--/* (3) */--> <!--/* omitted */--> </div>
項番 |
説明 |
---|---|
(1)
|
sec:authorize 属性やsec:authorize-url 属性を使用する際には<html> タグにxmlns:sec 属性を定義していたが、#authorization を使用する際には、xmlns:sec 属性の定義は不要である。 |
(2)
|
#authorization.expr の引数には、sec:authorize 属性と同様にアクセスポリシーを指定する。 |
(3)
|
#authorization.url の引数には、sec:authorize-url 属性と同様にWebリソースへアクセスするためのURLを指定する。 |
9.3.2.4.3. 認可処理の判定結果を変数に格納¶
JSPにおいて、<sec:authorize>
タグを使って呼び出した認可処理の判定結果は、変数に格納して使いまわすことができる。
JSPの実装例
<sec:authorize url="/admin/accounts"
var="hasAccountsAuthority"/> <!-- (1) -->
<c:if test="${hasAccountsAuthority}"> <!-- (2) -->
<!-- omitted -->
</c:if>
項番 |
説明 |
---|---|
(1)
|
var 属性に判定結果を格納するための変数名を指定する。アクセスが許可された場合は、変数に
true が設定される。 |
(2)
|
変数の値を参照して表示処理を実装する。
|
9.3.2.5. 認可エラー時のレスポンス¶
Spring Securityは、リソースへのアクセスを拒否した場合、以下のような流れでエラーをハンドリングしてレスポンスの制御を行う。
項番 |
説明 |
---|---|
(1)
|
Spring Securityは、リソースやメソッドへのアクセスを拒否するために、
AccessDeniedException を発生させる。 |
(2)
|
ExceptionTranslationFilter クラスは、AccessDeniedException をキャッチし、AccessDeniedHandler またはAuthenticationEntryPoint インタフェースのメソッドを呼び出してエラー応答を行う。 |
(3)
|
認証済みのユーザーからのアクセスの場合は、
AccessDeniedHandler インタフェースのメソッドを呼び出してエラー応答を行う。 |
(4)
|
未認証のユーザーからのアクセスの場合は、
AuthenticationEntryPoint インタフェースのメソッドを呼び出してエラー応答を行う。 |
9.3.2.5.1. AccessDeniedHandler¶
AccessDeniedHandler
インタフェースは、認証済みのユーザーからのアクセスを拒否した際のエラー応答を行うためのインタフェースである。AccessDeniedHandler
インタフェースの実装クラスとして以下のクラスを提供している。クラス名 |
説明 |
---|---|
AccessDeniedHandlerImpl |
HTTPレスポンスコードに403(Forbidden)を設定し、指定されたエラーページに遷移する。
エラーページの指定がない場合は、HTTPレスポンスコードに403(Forbidden)を設定してエラー応答(
HttpServletResponse#sendError )を行う。 |
InvalidSessionAccessDeniedHandler |
InvalidSessionStrategy インタフェースの実装クラスに処理を委譲する。このクラスは、CSRF対策とセッション管理機能を使用してセッションタイムアウトを検知する設定を有効にした際に、CSRFトークンがセッションに存在しない(つまりセッションタイムアウトが発生している)場合に使用される。
|
DelegatingAccessDeniedHandler |
AccessDeniedException とAccessDeniedHandler インタフェースの実装クラスのマッピングを行い、発生したAccessDeniedException に対応するAccessDeniedHandler インタフェースの実装クラスに処理を委譲する。InvalidSessionAccessDeniedHandler はこの仕組みを利用して呼び出されている。 |
RequestMatcherDelegatingAccessDeniedHandler |
Note
なお、 |
Spring Securityのデフォルトの設定では、エラーページの指定がないAccessDeniedHandlerImpl
が使用される。
9.3.2.5.2. AuthenticationEntryPoint¶
AuthenticationEntryPoint
インタフェースは、未認証のユーザーからのアクセスを拒否した際のエラー応答を行うためのインタフェースである。AuthenticationEntryPoint
インタフェースの実装クラスとして以下のクラスを提供している。クラス名 |
説明 |
---|---|
LoginUrlAuthenticationEntryPoint |
フォーム認証用のログインフォームを表示する。
|
BasicAuthenticationEntryPoint |
Basic認証用のエラー応答を行う。
具体的には、HTTPレスポンスコードに401(Unauthorized)を、レスポンスヘッダとしてBasic認証用の「
WWW-Authenticate 」ヘッダを設定してエラー応答(HttpServletResponse#sendError )を行う。 |
DigestAuthenticationEntryPoint |
Digest認証用のエラー応答を行う。
具体的には、HTTPレスポンスコードに401(Unauthorized)を、レスポンスヘッダとしてDigest認証用の「
WWW-Authenticate 」ヘッダを設定してエラー応答(HttpServletResponse#sendError )を行う。 |
Http403ForbiddenEntryPoint |
HTTPレスポンスコードに403(Forbidden)を設定してエラー応答(
HttpServletResponse#sendError )を行う。 |
HttpStatusEntryPoint |
任意のHTTPレスポンスコードを設定して正常応答(
HttpServletResponse#setStatus )を行う。 |
DelegatingAuthenticationEntryPoint |
RequestMatcher インタフェースの仕組みを利用して、指定されたリクエストのパターンに対応するAuthenticationEntryPoint インタフェースの実装クラスに処理を委譲する。 |
Spring Securityのデフォルトの設定では、認証方式に対応するAuthenticationEntryPoint
インタフェースの実装クラスが使用される。
9.3.2.5.3. 認可エラー時の遷移先¶
SpringSecurityConfig.javaの定義例
@Bean("accessDeniedHandler")
public AccessDeniedHandler accessDeniedHandler() {
LinkedHashMap<Class<? extends AccessDeniedException>, AccessDeniedHandler> errorHandlers = new LinkedHashMap<>();
// omitted
AccessDeniedHandlerImpl defaultErrorHandler = new AccessDeniedHandlerImpl();
defaultErrorHandler.setErrorPage("/WEB-INF/views/common/error/accessDeniedError.jsp"); // (1)
return new DelegatingAccessDeniedHandler(errorHandlers, defaultErrorHandler);
}
項番 |
説明 |
---|---|
(1)
|
AccessDeniedHandler をBean定義し、defaultHandlerに認可エラー用のエラーページを指定する。 |
SpringSecurityConfig.javaの定義例
@Bean("accessDeniedHandler")
public AccessDeniedHandler accessDeniedHandler() {
LinkedHashMap<Class<? extends AccessDeniedException>, AccessDeniedHandler> errorHandlers = new LinkedHashMap<>();
// omitted
AccessDeniedHandlerImpl defaultErrorHandler = new AccessDeniedHandlerImpl();
defaultErrorHandler.setErrorPage("/common/error/accessDeniedError"); // (1)
return new DelegatingAccessDeniedHandler(errorHandlers, defaultErrorHandler);
}
項番 |
説明 |
---|---|
(1)
|
AccessDeniedHandler をBean定義し、defaultHandlerに認可エラー用のエラーページを指定する。 |
spring-security.xmlの定義例
<sec:http request-matcher="ant">
<!-- omitted -->
<sec:access-denied-handler
error-page="/WEB-INF/views/common/error/accessDeniedError.jsp" /> <!-- (1) -->
<!-- omitted -->
</sec:http>
項番 |
説明 |
---|---|
(1)
|
<sec:access-denied-handler> タグのerror-page 属性に認可エラー用のエラーページを指定する。 |
spring-security.xmlの定義例
<sec:http request-matcher="ant">
<!-- omitted -->
<sec:access-denied-handler
error-page="/common/error/accessDeniedError" /> <!-- (1) -->
<!-- omitted -->
</sec:http>
項番 |
説明 |
---|---|
(1)
|
<sec:access-denied-handler> タグのerror-page 属性に認可エラー用のエラーページを指定する。 |
Tip
サーブレットコンテナのエラーページ機能の利用
認可エラーのエラーページは、サーブレットコンテナのエラーページ機能を使って指定することもできる。
サーブレットコンテナのエラーページ機能を使う場合は、web.xml
の<error-page>
タグを使用してエラーページを指定する。
<error-page> <error-code>403</error-code> <location>/WEB-INF/views/common/error/accessDeniedError.jsp</location> </error-page><error-page> <error-code>403</error-code> <location>/common/error/accessDeniedError</location> </error-page>
9.3.3. How to extend¶
本節では、Spring Securityが用意しているカスタマイズポイントや拡張方法について説明する。
9.3.3.1. 認可エラー時のレスポンス (認証済みユーザー編)¶
ここでは、認証済みユーザーからのアクセスを拒否した際の動作をカスタマイズする方法を説明する。
9.3.3.1.1. AccessDeniedHandlerの適用¶
Spring Securityが提供しているデフォルトの動作をカスタマイズする仕組みだけでは要件をみたせない場合は、AccessDeniedHandler
インタフェースの実装クラスを直接適用することができる。
AccessDeniedHandler
インタフェースの実装クラスを作成してSpring Securityに適用することで実現することができる。AccessDeniedHandlerインタフェースの実装クラスの作成例
public class JsonDelegatingAccessDeniedHandler implements AccessDeniedHandler {
private final RequestMatcher jsonRequestMatcher;
private final AccessDeniedHandler delegateHandler;
public JsonDelegatingAccessDeniedHandler(
public JsonDelegatingAccessDeniedHandler(
RequestMatcher jsonRequestMatcher, AccessDeniedHandler delegateHandler) {
this.jsonRequestMatcher = jsonRequestMatcher;
this.delegateHandler = delegateHandler;
}
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException)
throws IOException, ServletException {
if (jsonRequestMatcher.matches(request)) {
// response error information of JSON format
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
// omitted
} else {
// response error page of HTML format
delegateHandler.handle(
request, response, accessDeniedException);
}
}
}
SpringSecurityConfig.javaの定義例
@Bean("accessDeniedHandler")
public JsonDelegatingAccessDeniedHandler accessDeniedHandler() {
return new JsonDelegatingAccessDeniedHandler(accessAntPathRequestMatcher(), accessDeniedHandler()); // (1)
}
@Bean("accessAntPathRequestMatcher")
public AntPathRequestMatcher accessAntPathRequestMatcher() {
return new AntPathRequestMatcher("/api/**");
}
@Bean("accessDeniedHandler")
public AccessDeniedHandler accessDeniedHandler() {
AccessDeniedHandlerImpl bean = new AccessDeniedHandlerImpl();
bean.setErrorPage("/WEB-INF/views/common/error/accessDeniedError.jsp");
return bean;
}
@Bean
public SecurityFilterChain filterChain1(HttpSecurity http) {
// omitted
http.exceptionHandling(exceptionHandling -> exceptionHandling
.accessDeniedHandler(accessDeniedHandler())); // (2)
// omitted
return http.build();
}
項番 |
説明 |
---|---|
(1) |
|
(2) |
|
SpringSecurityConfig.javaの定義例
@Bean("accessDeniedHandler")
public JsonDelegatingAccessDeniedHandler accessDeniedHandler() {
return new JsonDelegatingAccessDeniedHandler(accessAntPathRequestMatcher(), accessDeniedHandler()); // (1)
}
@Bean("accessAntPathRequestMatcher")
public AntPathRequestMatcher accessAntPathRequestMatcher() {
return new AntPathRequestMatcher("/api/**");
}
@Bean("accessDeniedHandler")
public AccessDeniedHandler accessDeniedHandler() {
AccessDeniedHandlerImpl bean = new AccessDeniedHandlerImpl();
bean.setErrorPage("/common/error/accessDeniedError");
return bean;
}
@Bean
public SecurityFilterChain filterChain1(HttpSecurity http) {
// omitted
http.exceptionHandling(exceptionHandling -> exceptionHandling
.accessDeniedHandler(accessDeniedHandler())); // (2)
// omitted
return http.build();
}
項番 |
説明 |
---|---|
(1) |
|
(2) |
|
spring-security.xmlの定義例
<!-- (1) -->
<bean id="accessDeniedHandler"
class="com.example.web.security.JsonDelegatingAccessDeniedHandler">
<constructor-arg>
<bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
<constructor-arg value="/api/**"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<property name="errorPage"
value="/WEB-INF/views/common/error/accessDeniedError.jsp"/>
</bean>
</constructor-arg>
</bean>
<sec:http request-matcher="ant">
<!-- omitted -->
<sec:access-denied-handler ref="accessDeniedHandler" /> <!-- (2) -->
<!-- omitted -->
</sec:http>
項番 |
説明 |
---|---|
(1) |
|
(2) |
|
spring-security.xmlの定義例
<!-- (1) -->
<bean id="accessDeniedHandler"
class="com.example.web.security.JsonDelegatingAccessDeniedHandler">
<constructor-arg>
<bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
<constructor-arg value="/api/**"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<property name="errorPage"
value="/common/error/accessDeniedError"/>
</bean>
</constructor-arg>
</bean>
<sec:http request-matcher="ant">
<!-- omitted -->
<sec:access-denied-handler ref="accessDeniedHandler" /> <!-- (2) -->
<!-- omitted -->
</sec:http>
項番 |
説明 |
---|---|
(1) |
|
(2) |
|
9.3.3.2. 認可エラー時のレスポンス (未認証ユーザー編)¶
ここでは、未認証ユーザーからのアクセスを拒否した際の動作をカスタマイズする方法を説明する。
9.3.3.2.1. リクエスト毎にAuthenticationEntryPointを適用¶
AuthenticationEntryPoint
インタフェースの実装クラスをSpring Securityに適用することで実現することができる。SpringSecurityConfig.javaの定義例
@Bean("authenticationEntryPoint")
public DelegatingAuthenticationEntryPoint authenticationEntryPoint() {
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> map = new LinkedHashMap<>();
map.put(antPathRequestMatcher(), entryPoint());
DelegatingAuthenticationEntryPoint bean = new DelegatingAuthenticationEntryPoint(map); // (1)
bean.setDefaultEntryPoint(defaultEntryPoint());
return bean;
}
@Bean("antPathRequestMatcher")
public AntPathRequestMatcher antPathRequestMatcher() {
return new AntPathRequestMatcher("/api/**");
}
@Bean("entryPoint")
public JsonAuthenticationEntryPoint entryPoint() {
return new JsonAuthenticationEntryPoint();
}
@Bean("defaultEntryPoint")
public LoginUrlAuthenticationEntryPoint defaultEntryPoint() {
return new LoginUrlAuthenticationEntryPoint("/login");
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
// omitted
http.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(authenticationEntryPoint())); // (2)
// omitted
return http.build();
}
項番 |
説明 |
---|---|
(1)
|
AuthenticationEntryPoint インタフェースの実装クラスをbean定義してDIコンテナに登録する。ここでは、Spring Securityが提供している
DelegatingAuthenticationEntryPoint クラスを利用して、リクエストのパターン毎にAuthenticationEntryPoint インタフェースの実装クラスを適用している。 |
(2)
|
HttpSecurity#exceptionHandling にAuthenticationEntryPoint のbeanを指定する。 |
spring-security.xmlの定義例
<!-- (1) -->
<bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint">
<constructor-arg>
<map>
<entry>
<key>
<bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
<constructor-arg value="/api/**"/>
</bean>
</key>
<bean class="com.example.web.security.JsonAuthenticationEntryPoint"/>
</entry>
</map>
</constructor-arg>
<property name="defaultEntryPoint">
<bean class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg value="/login"/>
</bean>
</property>
</bean>
<sec:http request-matcher="ant" entry-point-ref="authenticationEntryPoint"> <!-- (2) -->
<!-- omitted -->
</sec:http>
項番 |
説明 |
---|---|
(1)
|
AuthenticationEntryPoint インタフェースの実装クラスをbean定義してDIコンテナに登録する。ここでは、Spring Securityが提供している
DelegatingAuthenticationEntryPoint クラスを利用して、リクエストのパターン毎にAuthenticationEntryPoint インタフェースの実装クラスを適用している。 |
(2)
|
<sec:http> タグのentry-point-ref 属性にAuthenticationEntryPoint のbeanを指定する。 |
Note
デフォルトで適用されるAuthenticationEntryPoint
リクエストに対応するAuthenticationEntryPoint
インタフェースの実装クラスの指定がない場合は、Spring Securityがデフォルトで定義するAuthenticationEntryPoint
インタフェースの実装クラスが使用される仕組みになっている。
認証方式としてフォーム認証を使用する場合は、LoginUrlAuthenticationEntryPoint
クラスが使用されログインフォームが表示される。
9.3.3.3. ロールの階層化¶
認可処理では、ロールに階層関係を設けることができる。
/user
配下のパス(「ROLE_USER」権限を持つユーザーがアクセスできるパス)にアクセスすることができる。SpringSecurityConfig.javaの定義例
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
// omitted
http.authorizeHttpRequests(authz -> authz
.requestMatchers(new AntPathRequestMatcher("/user/**")).hasAnyRole("USER")
// omitted
);
// omitted
return http.build();
}
spring-security.xmlの定義例
<sec:http request-matcher="ant">
<sec:intercept-url pattern="/user/**" access="hasAnyRole('USER')" />
<!-- omitted -->
</sec:http>
9.3.3.3.1. 階層関係の設定¶
ロールの階層関係は、org.springframework.security.access.hierarchicalroles.RoleHierarchy
インタフェースの実装クラスで解決する。
SpringSecurityConfig.javaの定義例
@Bean("roleHierarchy")
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl bean = new RoleHierarchyImpl(); // (1)
bean.setHierarchy("""
ROLE_ADMIN > ROLE_STAFF
ROLE_STAFF > ROLE_USER
"""); // (2)
return bean;
}
項番 |
説明 |
---|---|
(1)
|
org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl クラスを指定する。RoleHierarchyImpl は、Spring Securityが提供するデフォルトの実装クラスである。 |
(2)
|
hierarchy プロパティに階層関係を定義する。書式: [上位ロール] > [下位ロール]
上記例では、
STAFFは、USERに認可されたリソースにもアクセス可能である。
ADMINは、USERとSTAFFに認可されたリソースにもアクセス可能である。
|
spring-security.xmlの定義例
<bean id="roleHierarchy"
class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl"> <!-- (1) -->
<property name="hierarchy"> <!-- (2) -->
<value>
ROLE_ADMIN > ROLE_STAFF
ROLE_STAFF > ROLE_USER
</value>
</property>
</bean>
項番 |
説明 |
---|---|
(1)
|
org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl クラスを指定する。RoleHierarchyImpl は、Spring Securityが提供するデフォルトの実装クラスである。 |
(2)
|
hierarchy プロパティに階層関係を定義する。書式: [上位ロール] > [下位ロール]
上記例では、
STAFFは、USERに認可されたリソースにもアクセス可能である。
ADMINは、USERとSTAFFに認可されたリソースにもアクセス可能である。
|
9.3.3.3.2. Webリソースの認可処理への適用¶
ロールの階層化を、Webリソースと画面項目に対する認可処理に適用する方法を説明する。
SpringSecurityConfig.javaの定義例
@Bean
@Order(90)
public SecurityFilterChain filterChain(HttpSecurity http) {
AuthorityAuthorizationManager<RequestAuthorizationContext> authManager = AuthorityAuthorizationManager.hasRole("STAFF");
authManager.setRoleHierarchy(roleHierarchy()); // (1)
// omitted
http.authorizeHttpRequests(authz -> authz
.requestMatchers(new AntPathRequestMatcher("/user/**"))
.access(authManager) //(2)
// omitted
);
return http.build();
}
項番
|
説明
|
---|---|
(1)
|
org.springframework.security.authorization.AuthorityAuthorizationManager のインスタンスを生成し、roleHierarchy プロパティにRoleHierarchy インタフェースの実装クラスのBeanを指定する。 |
(2)
|
(1)で生成した
AuthorizationManager をaccess メソッドで設定する。 |
spring-security.xmlの定義例
<!-- (1) -->
<bean id="webExpressionHandler"
class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">
<property name="roleHierarchy" ref="roleHierarchy"/> <!-- (2) -->
</bean>
<sec:http request-matcher="ant">
<!-- omitted -->
<sec:expression-handler ref="webExpressionHandler" /> <!-- (3) -->
</sec:http>
項番
|
説明
|
---|---|
(1)
|
org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler のBeanを定義する。 |
(2)
|
roleHierarchy プロパティにRoleHierarchy インタフェースの実装クラスのBeanを指定する。 |
(3)
|
<sec:expression-handler> タグのref 属性に、org.springframework.security.access.expression.SecurityExpressionHandler インタフェースの実装クラスのBeanを指定する。 |
9.3.3.3.3. メソッドの認可処理への適用¶
ロールの階層化を、Javaメソッドに対する認可処理に適用する方法を説明する。
SpringSecurityConfig.javaの定義例
@EnableMethodSecurity // (3)
@Configuration
public class SpringSecurityConfig {
// (3)
@Bean("methodExpressionHandler")
public static MethodSecurityExpressionHandler methodExpressionHandler(
RoleHierarchy roleHierarchy) {
DefaultMethodSecurityExpressionHandler bean = new DefaultMethodSecurityExpressionHandler(); // (1)
bean.setRoleHierarchy(roleHierarchy); // (2)
return bean;
}
項番 |
説明 |
---|---|
(1)
|
org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler のBeanをstatic定義する。 |
(2)
|
roleHierarchy プロパティにRoleHierarchy インタフェースの実装クラスのBeanを指定する。 |
(3)
|
@EnableMethodSecurity アノテーションを設定する。 |
spring-security.xmlの定義例
<bean id="methodExpressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler"> <!-- (1) -->
<property name="roleHierarchy" ref="roleHierarchy"/> <!-- (2) -->
</bean>
<sec:method-security>
<sec:expression-handler ref="methodExpressionHandler" /> <!-- (3) -->
</sec:method-security>
項番 |
説明 |
---|---|
(1)
|
org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler のBeanを定義する。 |
(2)
|
roleHierarchy プロパティにRoleHierarchy インタフェースの実装クラスのBeanを指定する。 |
(3)
|
<sec:method-security> タグのref 属性に、org.springframework.security.access.expression.SecurityExpressionHandler インタフェースの実装クラスのBeanを指定する。 |
9.3.4. Appendix¶
9.3.4.1. Spring Securityのパスパターンマッチングにおける拡張子および末尾/
の考慮¶
RequestMatcher
オブジェクトの設定が不足していると認可処理を行わずにアクセス出来てしまう。RequestMatcher
の引数に”*
“や**
などのワイルドカード指定を含めている場合は問題とはならないが、そうでない場合はRequestMatcher
の引数に対して個別に考慮する必要がある。/restrict
に対して「ROLE_ADMIN」ロールを持つユーザからのアクセスのみを許可している。@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// omitted
http.authorizeHttpRequests(authz -> authz
.requestMatchers(new AntPathRequestMatcher("/restrict.*")).hasRole("ADMIN") // (1)
.requestMatchers(new AntPathRequestMatcher("/restrict/")).hasRole("ADMIN") // (2)
.requestMatchers(new AntPathRequestMatcher("/restrict")).hasRole("ADMIN") // (3)
// omitted
return http.build();
}
項番 |
説明 |
---|---|
(1)
|
/restrict に拡張子を付けたパターン(/restrict.json など)のアクセスポリシーを定義する。個別に拡張子を許容している場合または
PathMatchConfigurer#setUseSuffixPatternMatch(true) を設定している場合は必須。 |
(2)
|
/restrict の末尾に”/ “を付けたパターン(/restrict/ )のアクセスポリシーを定義する。個別に末尾の”
/ を許容している場合またはPathMatchConfigurer#setUseSuffixPatternMatch(true) を設定している場合は必須。 |
(3)
|
/restrict に対するアクセスポリシーを定義する。 |
sec:intercept-url
のpattern
属性の設定が不足していると認可処理を行わずにアクセス出来てしまう。sec:intercept-url
のpattern
属性に”*
“や**
などのワイルドカード指定を含めている場合は問題とはならないが、そうでない場合はsec:intercept-url
のpattern
属性に対して個別に考慮する必要がある。/restrict
に対して「ROLE_ADMIN」ロールを持つユーザからのアクセスのみを許可している。<sec:http request-matcher="ant">
<sec:intercept-url pattern="/restrict.*" access="hasRole('ADMIN')" /> <!-- (1) -->
<sec:intercept-url pattern="/restrict/" access="hasRole('ADMIN')" /> <!-- (2) -->
<sec:intercept-url pattern="/restrict" access="hasRole('ADMIN')" /> <!-- (3) -->
<!-- omitted -->
</sec:http>
項番 |
説明 |
---|---|
(1)
|
/restrict に拡張子を付けたパターン(/restrict.json など)のアクセスポリシーを定義する。個別に拡張子を許容している場合または
suffix-pattern="true" を設定している場合は必須。 |
(2)
|
/restrict の末尾に”/ “を付けたパターン(/restrict/ )のアクセスポリシーを定義する。個別に末尾の”
/ を許容している場合またはtrailing-slash="true" を設定している場合は必須。 |
(3)
|
/restrict に対するアクセスポリシーを定義する。 |