kagamihogeの日記

kagamihogeの日記です。

Spring Security Reference 4.2.2のI. Prefaceをテキトーに訳した(1)

Spring Security Reference http://docs.spring.io/spring-security/site/docs/4.2.2.RELEASE/reference/htmlsingle/ のI. Prefaceをなんとなく訳した。

Spring Securityちょっと勉強したいな、と読み始めたけどI. Prefaceは長めのチュートリアルって感じで、あまり大したことは書いてなかったりした。

Spring Securityは強力で高いカスタマイズ性を備えた認証とアクセス制御のフレームワークです。SpringベースのアプリケーションをセキュアにするためのデファクトスタンダードがSpring Securityです。

Part I. Preface

Spring SecurityはJava EEベースのエンタープライズソフトウェアアプリケーションにおける包括的なセキュリティソリューションです。このリファレンスガイドを読み進めば分かる通り、我々は使いやすくて設定の小回りが利くセキュリティシステムを提供できるよう努めています。

セキュリティとは常に変化し続けるものであり、包括的でシステム全体視点からのアプローチを追及することが重要です。セキュリティ界隈としてはセキュリティレイヤー(“layers of security”)の導入を推奨しており、これにより各レイヤーを独立して出来る限りセキュアにする事が可能となり、レイヤーの層自体が付加的なセキュリティ対策となります。各レイヤのセキュリティを緊密(“tighter”)にすることで、アプリケーションはより堅牢かつ安全になります。最も低い層では中間者攻撃リスクを低減するため、たとえばtransport securityとシステム同定(system identification)などの問題を処理する必要があります。また、一般的には、ファイアウォールを設定し、許可されたシステムだけが接続を試行可能なことを保証するのにVPNとIPセキュリティを使います。企業向けシステムでは公開サーバとバックエンドのデータベースとアプリケーションを分離するのにDMZを構築します。また、OSは非特権ユーザとしてのプロセス実行やファイルシステムセキュリティの最大化などの問題に重要な役割を担います。通常、OSはファイアウォールを内蔵しています。もしかすると、読者の中にはシステムに対するサービス拒否攻撃やブルートフォースの対処経験がある人もいるかもしれません。侵入検知システムは、リアルタイムのblocking offending TCP/IP addressesなどの攻撃からシステムを保護可能となり、攻撃に対する対処とモニタリングにとりわけ有用です。そこから上位レイヤに目を移すと、JVMは幸いにもJavaの型ごとにパーミッションを最小化するような設定が可能なため、, and then your application will add its own problem domain-specific security configuration. Spring Securityは後者の問題領域、アプリケーションセキュリティ、の容易化を扱います。

なお、上に挙げたすべてのセキュリティレイヤを適切に処理する必要があり、すべてのレイヤーを網羅する管理的要因(managerial factors)も対処が必要です。管理的要因は以下ですべてではありませんが、例えば、セキュリティ情報の入手・パッチ当て・personnel vetting・監査・変更管理・engineering management systems・データバックアップ・ディザスタリカバリ・パフォーマンスベンチマーク・負荷監視・ログ集積・インシデントレスポンス、などが挙げられます。

Spring Securityを使うことでエンタープライズアプリケーションのセキュリティレイヤに集中できます。セキュリティレイヤにはビジネス分野と同じくらい様々な要件が存在します。金融アプリケーションとECサイトのセキュリティ要件は異なります。ECサイトと販売管理ツールも異なります。こうした各種の要求はアプリケーションセキュリティを、興味深く、困難で、挑戦し甲斐のあるものとしています。

まずはChapter 1, Getting Startedに目を通して下さい。フレームワーク名前空間ベースの設定(namespace-based configuration)の紹介を数行にまとめています。Spring Securityと使う必要のあるクラスの動作を理解するには、Part II, “Architecture and Implementation”があります。本ガイドのそれ以外については、必要になった段階でその章を参照してください。なお、アプリケーションセキュリティの一般的な課題について出来る限り情報を集めておくことを推奨します。Spring Securityはあらゆるセキュリティ課題を解決する万能薬ではありません。アプリケーション設計において当初からセキュリティを考慮することが重要です。後付けで改良しようとするのは良いアイデアとは言えません。特に、webアプリケーションの場合、多数の脆弱性を考慮する必要があります。XSSCSRFセッションハイジャックなどは最初から対処しておくべきです。OWASP(https://www.owasp.org/)では有益な参照情報とwebアプリケーションの脆弱性のトップテンを掲載しています。

このリファレンスガイドが役に立てれば幸いで、フィードバックと意見を歓迎します。

最後になりますが、Spring Securityのコミュニティにようこそ。

1. Getting Started

本ガイドの後半ではフレームワークアーキテクチャと実装クラスの詳細について解説しており、これらはより細かいカスタマイズをする場合には理解しておく必要があります。このパートでは、Spring Security 4.0の紹介で、プロジェクトの来歴の概要とフレームワークを使い始めるための方法を簡単にまとめてあります。とくに、すべての実装クラスを個々にワイヤリングする伝統的なSpringビーンを使う方法と比べると、かなり簡単にアプリケーションをセキュアにできるnamespace configurationを見ていきます。

また、サンプルアプリケーションも見ていきます。以降のセクションを読み進める前にサンプルを動かしておくだけの価値はあります。フレームワークの理解が深まったらサンプルを見直すのが良いでしょう。また、記事へのリンク・ビデオとチュートリアルなど開発プロジェクトに有益な情報があるproject websiteもチェックしてみてください。

2. Introduction

2.1 What is Spring Security?

JavaEEベースのエンタープライズソフトウェアアプリケーションに包括的なセキュリティサービスを提供します。Spring Frameworkエンタープライズソフトウェア開発分野のJava EEソリューションを先導しており、Spring SecurityはSpring Frameworkを使用するプロジェクトのサポートに力点が置かれています。いまエンタープライズアプリケーション開発にSpringを使っていない場合、我々としてはSpringを調査してみることを推奨します。Springの知識がある、特に依存性注入を知っていれば、Spring Securityの理解はより早くなります。

Spring Securityが使われる動機は様々ですが、Java EE Servlet仕様やEJB仕様のセキュリティ機能が一般的なエンタープライズアプリケーションの要求度合いを満たさないことが分かってからSpring Securityに流れ着いてることが多々あります。これらの標準について、WARやEARレベルでポータブルでは無い点を承知しておくことが重要です。そのため、サーバ環境を移行する場合、新しいターゲット環境用にアプリケーションセキュリティのかなりの再設定作業が基本的には発生します。Spring Securityはこの問題に対処しており、使いやすくカスタマイズ可能なセキュリティ機能を多数提供しています。

アプリケーションセキュリティには"authentication"(認証)と"authorization"(認可)(もしくは"access-control"(アクセス制御))という大きく二つの領域があります。Spring Securityの対象領域はこの二つになります。"認証"とは、彼らが誰と言ってきているかのプリンシパルを確立するプロセスです("プリンシパル"は、アプリケーション内で何らかのアクションを実行可能な、他システム・デバイス・ユーザなどを基本的には意味します)。"認可"とは、アプリケーション内のあるアクションがプリンシパルに許可されるかどうかを決定するプロセスを指します。プリンシパルのIDが認証プロセスによって確立済みになってから、認可が必要とされます。これらの概念は一般的なもので、Spring Securityに固有のものではありません。

認証において、Spring Securityは様々な認証モデルを提供しています。認証モデルの多くはサードパーティ製か、IETF(Internet Engineering Task Force)などの関連標準団体が開発しています。また、Spring Security固有の認証機能も提供しています。具体的には、現在Spring Securityは以下のすべてのテクノロジとの認証インテグレーション機能を提供しています。

  • HTTP BASIC authentication headers (an IETF RFC-based standard)
  • HTTP Digest authentication headers (an IETF RFC-based standard)
  • HTTP X.509 client certificate exchange (an IETF RFC-based standard)
  • LDAP (a very common approach to cross-platform authentication needs, especially in large environments)
  • Form-based authentication (for simple user interface needs)
  • OpenID authentication
  • Authentication based on pre-established request headers (such as Computer Associates Siteminder)
  • Jasig Central Authentication Service (otherwise known as CAS, which is a popular open source single sign-on system)
  • Transparent authentication context propagation for Remote Method Invocation (RMI) and HttpInvoker (a Spring remoting protocol)
  • Automatic “remember-me” authentication (so you can tick a box to avoid re-authentication for a predetermined period of time)
  • Anonymous authentication (allowing every unauthenticated call to automatically assume a particular security identity)
  • Run-as authentication (which is useful if one call should proceed with a different security identity)
  • Java Authentication and Authorization Service (JAAS)
  • Java EE container authentication (so you can still use Container Managed Authentication if desired)
  • Kerberos
  • Java Open Source Single Sign-On (JOSSO) *
  • OpenNMS Network Management Platform *
  • AppFuse *
  • AndroMDA *
  • Mule ESB *
  • Direct Web Request (DWR) *
  • Grails *
  • Tapestry *
  • JTrac *
  • Jasypt *
  • Roller *
  • Elastic Path *
  • Atlassian Crowd *
  • 自前の認証システム(後述)

サードパーティ製は末尾にアスタリスク(*)付与

独立系ソフトウェアベンダー(ISVs)の多くがSpring Securityを、柔軟性のある認証モデルという重要な選択肢のために採用しています。これにより、多量の開発作業やクライアントに環境入れ替えを要求することなく、クライアントが要求するものとソリューションとを速やかにインテグレーション可能になります。もし要求に合致する認証メカニズムが上述のリストに無い場合、Spring Securityはオープンプラットフォームなので自前の認証メカニズムの開発は極めてシンプルです。Spring Securityの企業ユーザーの多くは、ある特定のセキュリティ標準に従ってるわけではない、"レガシー"システムとのインテグレーションを必要としていますが、Spring Securyの幸福はそれらのシステムで"うまくやる"ことにあります。

認証メカニズムとは直接の関係がないですが、Spring Securityは多くの認可機能を提供します。大きく三つの関心領域があり、webリクエストの認可・メソッドが実行可能かどうかの認可・個々のドメインオブジェクトインスタンスへのアクセスの認可、があります。これらの違いを理解するには、例えば、Servle仕様のwebパターンセキュリティ・EJBコンテナマネージドセキュリティ・ファイルシステムセキュリティ、それぞれにおける認可機能を考えてみてください。Spring Securityはこれらの重要な領域すべてにおける豊富な機能を提供しており、詳細は本リファレンスガイドで触れていきます。

2.2 History

Spring Securityは"The Acegi Security System for Spring"として2003年後半に開始されました。Spring Developer'sメーリングリストに投じられたある質問に、Springベースのセキュリティ実装に何らかの考慮があるかどうか、がありました。当時のSpringコミュニティは今ほど大きくなく(特に規模)、また、Spring自体は2003年初頭からSourceForgeプロジェクトとしてのみ存在していました。先の質問に対する回答は、現在は調査するための時間が無いが、セキュリティは重要な領域である、というものでした。

これを受けて、シンプルなセキュリティ実装が開発されたもののリリースはされませんでした。それから数週間後、別のSpringコミュニティメンバがセキュリティに関する情報を求めたため、そのコードが彼らに提供されました。別口で同じリクエストが何回か来たため、2004年1月までに約20人がそのコードを使用しました。2004年3月に正式設立されたSourceForgeプロジェクトが問題無く進んでいると示したグループに、これらの先駆者ユーザは加わりました。

初期の頃は自前の認証モジュールを持っていませんでした。Container Managed Securityは認証プロセスに依存する一方で、Acegi Securityは認可に焦点を当てていました。これは最初は適切でしたが、ユーザが増えれば増えるほどコンテナサポートの追加を要求するようになり、コンテナ固有の認証レルムインタフェースの根本的な限界が明らかになりました。また、コンテナのクラスパスに新しいJARを追加するという関連する問題もありました。これはユーザが混乱して設定ミスをする一般的な原因でした。

その後Acegi Security固有の認証サービスが導入されました。約1年後、Acegi Securityは公式にSpring Frameworkのサブプロジェクトになりました。多数のソフトウェアプロジェクトへの導入と多数の改良とコミュニティコントリビューションで約二年半の後、1.0.0 final releaseはMay 2006に公開されました。

Acegi Securityは2007年末に公式にSpring Portfolioプロジェクトとなりそして、"Spring Security"に改められました。

今日のSpring Securityは強力でアクティブなオープンソースコミュニティを謳歌しています。サポートフォーラムにはSpring Securityに関する多数のメッセージが存在します。コードに取り組むアクティブな開発者があり、開発者は定期的にパッチを共有して互いにサポートするコミュニティになっています。

2.3 Release Numbering

Spring Securityのリリースナンバリングを知っておくことは場合により有用で、例えば将来のリリース版への移行に必要な作業(または欠如)を特定するのに役立ちます。各リリースは三組の整数でMAJOR.MINOR.PATCHとなります。MAJORバージョンは互換性が無くAPIの大規模アップグレードを意味します。MINORバージョンはソースおよびバイナリ互換性が旧マイナーバージョンと大部分保持されるであろう事を意味し、いくつかの設計変更と非互換アップデートがありうると考えられています。PATCHレベルは完全な互換性が前方・後方共にあり、なおバグフィックスでの変更は例外的に可能です。

変更による影響範囲はコードがどの程度強くSpring Securityを利用しているかに依存します。カスタマイズをしていればいるほど影響を受ける可能性があり、単純なnamespace configurationを使わない場合も同様に高くなります。

新規バージョンのロールアウト前にはアプリケーションの統合的なテストを実施してください。

2.4 Getting Spring Security

Spring Securityの入手方法は何通りかあります。

Spring Securityからパッケージディストリビューションをダウンロードするか、Maven Centralリポジトリ(スナップショットとマイルストーンリリースはSpring Mavenリポジトリ)から個々のjarをダウンロードするか、ソースからビルドしてください。

2.4.1 Usage with Maven

Spring SecurityのMaven依存性の最小構成は基本的には以下のようになります。

pom.xml

<dependencies>
<!-- ... その他の依存性 ... -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>4.2.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>4.2.2.RELEASE</version>
</dependency>
</dependencies>

LDAP, OpenIDなど追加機能を使う場合はSection 2.4.3, “Project Modules”の適切なものを追加してください。

Maven Repositories

すべてのGAリリース(.RELEASEで終わるバージョンのこと)はMaven Centralにデプロイされているため、pomに別途Mavenリポジトリを追加する必要はありません。

SNAPSHOTバージョンを使う場合は以下のようなSpring Snapshot定義をしてください。

pom.xml.

<repositories>
<!-- ... その他のリポジトリ要素 ... -->
<repository>
    <id>spring-snapshot</id>
    <name>Spring Snapshot Repository</name>
    <url>http://repo.spring.io/snapshot</url>
</repository>
</repositories>

マイルストーンもしくはリリース候補バージョンを使う場合は以下のようなSpring Snapshot定義をしてください。

pom.xml.

<repositories>
<!-- ... その他のリポジトリ要素 ... -->
<repository>
    <id>spring-milestone</id>
    <name>Spring Milestone Repository</name>
    <url>http://repo.spring.io/milestone</url>
</repository>
</repositories>
Spring Framework Bom

Spring SecurityはSpring Framework 4.3.5.RELEASEに対して開発されていますが、4.0.xでも動作すると思われます。Spring Securityでかなりのユーザがハマるものに、奇妙なクラスパス問題を引き起こす事がある、Spring Framework 4.3.5.RELEASEの推移的依存性の解決があります。

この穴をかわす方法の一つはpomのセクション内にすべてのSpring Frameworkモジュールを追加します。それとは別のアプローチは以下のようにpom.xmlセクションにspring-framework-bomを追加します。

pom.xml.

<dependencyManagement>
    <dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-framework-bom</artifactId>
        <version>4.3.5.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>
    </dependencies>
</dependencyManagement>

上記を使うことでSpring Securityのすべての推移的依存性がSpring 4.3.5.RELEASEモジュールを使うことが保証されます。

上記のアプローチはMavenの"bill of materials" (BOM) を使用しておりMaven 2.0.9+で使用可能です。依存性解決がどのように行われるかについての詳細はMaven’s Introduction to the Dependency Mechanism documentationを参照してください。

2.4.2 Gradle

Spring SecurityのGradleの最小構成の依存性は基本的には以下のようになります。

build.gradle.

dependencies {
    compile 'org.springframework.security:spring-security-web:4.2.2.RELEASE'
    compile 'org.springframework.security:spring-security-config:4.2.2.RELEASE'
}

LDAP, OpenIDなどの追加機能を使う場合はSection 2.4.3, “Project Modules”の適当なものを追加してください。

Gradle Repositories

すべてのGAリリース(バージョンが.RELEASEで終わるもの)はMaven Centralにデプロイされているため、GAリリースの場合にはmavenCentral()リポジトリを使います。

build.gradle.

repositories {
    mavenCentral()
}

SNAPSHOTバージョンを使う場合は以下のようなSpring Snapshotリポジトリの定義をしてください。

build.gradle.

repositories {
    maven { url 'https://repo.spring.io/snapshot' }
}

マイルストーンもしくはリリース候補バージョンを使う場合は以下のようなSpring Milestoneリポジトリの定義をしてください。

build.gradle.

repositories {
    maven { url 'https://repo.spring.io/milestone' }
}
Using Spring 4.0.x and Gradle

デフォルトではGradleは依存性バージョンの解決には最新のバージョンを使用します。つまりSpring Framework 4.3.5.RELEASEでSpring Security 4.2.2.RELEASEを動かす場合にはそれ以上の作業が必要無い場合があるということです。とはいえ、問題が発生することもあるのでその際には以下のようにGradle’s ResolutionStrategyで回避策を取ると良いでしょう。

build.gradle.

configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        if (details.requested.group == 'org.springframework') {
            details.useVersion '4.3.5.RELEASE'
        }
    }
}

上記によりSpring Securityのすべての推移的依存性がSpring 4.3.5.RELEASEモジュールを使うことが保証されます。

上記のサンプルはGradle 1.9を使用していますが、この機能はGradleのincubating feature*1なのでGradleの最新バージョンでは動かすのに修正が必要かもしれません。

2.4.3 Project Modules

Spring Security 3.0のコードベースは複数のjarに分割されており、異なる機能領域とサードパーティ依存性を分離しています。プロジェクトのビルドにMavanを使う場合、pom.xmlに追加するモジュールがそれらのjarになります。Mavanを使わない場合であっても、サードパーティ依存性とバージョンのためにpom.xmlを編集することを推奨します。もしくは、サンプルアプリケーションに含まれるライブラリを調査することもお勧めします。

Core - spring-security-core.jar

コアの認証・アクセス制御クラスとインタフェース・リモーティングサポート・基本的なプロビジョニングAPIが含まれます。Spring Securityを使用するアプリケーションすべてで必要です。スタンドアローンアプリケーション・リモートクライアント・メソッド(サービスレイヤー)セキュリティ・JDBCユーザプロビジョニングをサポートします。トップレベルパッケージは以下の通りです。

  • org.springframework.security.core
  • org.springframework.security.access
  • org.springframework.security.authentication
  • org.springframework.security.provisioning
Remoting - spring-security-remoting.jar

Spring Remotingとの連携機能を提供します。Spring Remotingを使用するリモートクライアントを作成しない限り不要です。メインパッケージはorg.springframework.security.remotingです。

Web - spring-security-web.jar

フィルタと関連webセキュリティ基盤のコードが含まれます。servlet APIへの依存性があります。Spring Securityのweb認証サービスとURLベースアクセス制御を必要とする場合に使用します。メインパッケージはorg.springframework.security.webです。

Config - spring-security-config.jar

security namespaceをパースするコードとJava configurationのコードが含まれます。設定にSpring Security XML namespceあるいはSpring Securityのava Configurationサポートを使う場合に必要となります。メインパッケージはorg.springframework.security.configです。これらのクラスはアプリケーションでの直接使用は想定していません。

LDAP - spring-security-ldap.jar

LDAP認証とプロビジョニングです。LDAP認証もしくはLDAPユーザエントリの管理を使う場合に必要です。トップレベルのパッケージはorg.springframework.security.ldapです。

ACL - spring-security-acl.jar

特殊化されたドメインオブジェクトのACL実装です。アプリケーションの特定のドメインオブジェクトインスタンスにセキュリティを適用ために使います。トップレベルのパッケージはorg.springframework.security.aclsです。

CAS - spring-security-cas.jar

Spring SecurityのCASクライアント連携機能です。CASシングルサインオンサーバでSpring Securityのweb認証を使う場合に使用します。トップレベルのパッケージはorg.springframework.security.casです。

OpenID - spring-security-openid.jar

OpenIDのweb認証サポート機能です。外部のOpenIDサーバに対してユーザを認証するのに使用します。org.springframework.security.openid。OpenID4Javaが必要です。

Test - spring-security-test.jar

Spring Securityでのテストをサポートする機能です。

2.4.4 Checking out the Source

Spring Securityはオープンソースプロジェクトなのでgitでソースコードをチェックアウトしてみることを強くお勧めします。サンプルアプリケーションのすべてのコードを入手可能で、プロジェクトの最新バージョンを手軽にビルドしてみることも可能です。また、プロジェクトのソースを見れることはデバッグの大きな助けになります。例外のスタックトレースブラックボックス内の何らかの問題を指すだけだった時代は過去となり、何かが起きている行のコードをすぐに参照して調査ができます。ソースコードはプロジェクトの究極的なドキュメントであり、実際の動作を知るのに最も適している場合があります。

プロジェクトのソースを取得するには以下のgitコマンドを使います。

git clone https://github.com/spring-projects/spring-security.git

これによりローカルマシン上でプロジェクトヒストリー(リリースおよびブランチを含む)の全体にアクセスできます。

3. What’s New in Spring Security 4.2

Spring Security 4.2はSpring Framework 5に対する早期サポートを提供しています。80以上のissueをクローズしているチェンジログを、4.2.0.M1, 4.2.0.RC1, 4.2.0.RELEASEから参照できます。これら機能の大部分はコミュニティによるものです。以下が今回リリースのハイライトになります。

(省略)

4. Samples and Guides (Start Here)

Spring Securityを使い始めようとする場合、サンプルアプリケーションから始めるのが最良です。

Table 4.1. Sample Applications

Source Description Guide
Hello Spring Security Java-based configurationを使用している既存アプリケーションとのSpring Security連携の紹介 Hello Spring Security Guide
Hello Spring Security Boot 既存のSpring BootアプリケーションとのSpring Security連携の紹介 Hello Spring Security Boot Guide
Hello Spring Security XML XML-based configurationを使用している既存アプリケーションとのSpring Security連携の紹介 Hello Spring Security XML Guide
Hello Spring MVC Security 既存のSpring MVCアプリケーションとのSpring Security連携の紹介 Hello Spring MVC Security Guide
Custom Login Form ログインフォームをカスタマイズする方法の紹介 Custom Login Form Guide

5. Java Configuration

Java ConfigurationがSpring 3.1でSpring Frameworkに追加されています。Spring Security 3.2以降でSpring Security Java Configurationは存在し、XML無しでSpring Securityを簡単に設定できます。

Chapter6, Security Namespace Configurationに慣れている場合、Security Java Configurationとほとんど同じ事に気付くしょう。

Spring SecurityではSpring Security Java Configurationの使い方のサンプルをlots of sample applicationsで多数紹介しています。

5.1 Hello Web Security Java Configuration

最初はSpring Security Java Configurationを作ることから始めます。configurationはspringSecurityFilterChainと呼ばれるServlet Filterを生成し、これはアプリケーション内のすべてのセキュリティ(アプリケーションURLの保護・サブミットされたユーザ名とパスワードの検証・ログインフォームへのリダイレクトなど)に対する責任を持ちます。以下はSpring Security Java Configurationの最も基本的な形の例です。

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.*;
import org.springframework.security.config.annotation.authentication.builders.*;
import org.springframework.security.config.annotation.web.configuration.*;

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public UserDetailsService userDetailsService() throws Exception {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("user").password("password").roles("USER").build());
        return manager;
    }
}

このconfigurationはそれほど分量はありませんが、多くのことを行います。それら機能のサマリは以下の通りです。

5.1.1 AbstractSecurityWebApplicationInitializer

次はwarにspringSecurityFilterChainを登録します。Servlet 3.0+環境ではSpring’s WebApplicationInitializer supportを使用してJava Configurationで設定できます。なお、Spring SecurityはベースクラスAbstractSecurityWebApplicationInitializerを提供しており、このクラスはspringSecurityFilterChainの登録をユーザの代わりに行ってくれます。AbstractSecurityWebApplicationInitializerを使う方法は、Springをすでに使用しているか、Spring Securityがアプリケーションで単なるSpringコンポーネントなのか、に依存します。

5.1.2 AbstractSecurityWebApplicationInitializer without Existing Spring

SpringやSpring MVCを使用しない場合、configurationが参照されるようにスーパークラスWebSecurityConfigを渡します。以下はその例です。

import org.springframework.security.web.context.*;

public class SecurityWebApplicationInitializer
    extends AbstractSecurityWebApplicationInitializer {

    public SecurityWebApplicationInitializer() {
        super(WebSecurityConfig.class);
    }
}

SecurityWebApplicationInitializerは以下を行います。

  • アプリケーションのすべてのURLにspringSecurityFilterChain Filterを自動的に登録。
  • WebSecurityConfigをロードするContextLoaderListenerを追加

5.1.3 AbstractSecurityWebApplicationInitializer with Spring MVC

アプリケーションで何らかの形でSpringをすでに使用している場合、Spring ConfigurationをロードするWebApplicationInitializerがおそらく存在します。すぐ前で見たconfigurationを使うとおそらくエラーになります。このconfigurationを使うのではなく、既存のApplicationContextでSpring Securityを登録して下さい。たとえば、Spring MVCを使っている場合、以下のようなSecurityWebApplicationInitializerになります。

import org.springframework.security.web.context.*;

public class SecurityWebApplicationInitializer
    extends AbstractSecurityWebApplicationInitializer {

}

この設定はアプリケーションのすべてのURLにspringSecurityFilterChain Filterを登録することのみ行います。つぎに、既存のApplicationInitializerでWebSecurityConfigがロードされるようにします。たとえば、Spring MVCを使用している場合、getRootConfigClasses()にこれを追加します。

public class MvcWebApplicationInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { WebSecurityConfig.class };
    }

    // ... other overrides ...
}

5.2 HttpSecurity

ここまでに行ったWebSecurityConfigは単にユーザ認証の方法だけが書かれています。この設定で、Spring Securityはどのようにすべてのユーザに認証を要求する、と解釈しているのでしょうか? また、フォームベースの認証になるのはどうやっているのでしょうか? その理由はWebSecurityConfigurerAdapterがデフォルト設定をしているからで、その中のconfigure(HttpSecurity http)メソッドは以下のようになっています。

protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .anyRequest().authenticated()
            .and()
        .formLogin()
            .and()
        .httpBasic();
}

上記のデフォルト設定は以下を行います。

  • アプリケーションに対するすべてのリクエストは認証済みのユーザを要求する。
  • フォームベースのログインでユーザを認証する。
  • HTTP Basic認証でユーザを認証する。

XML Namespace configurationでの同様な設定は以下となります。

<http>
    <intercept-url pattern="/**" access="authenticated"/>
    <form-login />
    <http-basic />
</http>

XMLの閉じタグと同等な表現はJava Configurationではand()を使用し、これにより親から続けて設定を行えます。コードを読む際にはその書いてある通りに解釈します。リクエストの認証を設定andフォームログインを設定andHTTP Basic認証を設定、となります。

ただし、Java Configuratioは異なるデフォルトURLとパラメータになります。カスタムログインページを作成する場合にはこれを覚えておいてください。URLはRESTfulになります。Additionally, it is not quite so obvious we are using Spring Security which helps to prevent information leaks. For example:

5.3 Java Configuration and Form Login

ログインのプロンプトを表示したときにそのログインフォームがどこから来たのか、HTMLファイルやJSPについて何も触れていないので、不思議に思うかもしれません。

Spring Securityのデフォルト設定はログインページに明示的なURLを設定せず、Spring Securityは有効化されている機能でこれを自動生成します。ログインのサブミットを処理するURLには標準の値を使用します。ユーザログイン後にそのデフォルトターゲットURLが送信されます。

自動生成されるログインページは手っ取り早く動かす分には便利ですが、基本的には自前のログインページを作成するでしょう。これを行うには以下のように設定を書き換えます。

protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .anyRequest().authenticated()
            .and()
        .formLogin()
            .loginPage("/login") //1
            .permitAll();        //2
}
  1. この箇所でログインページの場所を指定している。
  2. ログインページのアクセスにはすべてのユーザ(つまり未認証のユーザ)を許可する。formLogin().permitAll()メソッドはフォームベースのログインで関連付けられたすべてのURLに対してすべてのユーザにアクセスを許可します。

これまでの設定でJSPでログインページを実装する例は以下となります。

以下のログインページはこれまでの設定で書かれています。デフォルトを変更したい場合には好きなように書き換えが可能です。

<c:url value="/login" var="loginUrl"/>
<form action="${loginUrl}" method="post">       1
    <c:if test="${param.error != null}">        2
        <p>
            Invalid username and password.
        </p>
    </c:if>
    <c:if test="${param.logout != null}">       3
        <p>
            You have been logged out.
        </p>
    </c:if>
    <p>
        <label for="username">Username</label>
        <input type="text" id="username" name="username"/>  4
    </p>
    <p>
        <label for="password">Password</label>
        <input type="password" id="password" name="password"/>  5
    </p>
    <input type="hidden"                        6
        name="${_csrf.parameterName}"
        value="${_csrf.token}"/>
    <button type="submit" class="btn">Log in</button>
</form>
  1. /loginURLへのPOSTでユーザ認証
  2. クエリパラメータerrorがある場合、認証を実行したが失敗
  3. クエリパラメータlogoutがある場合、ユーザが正常にログアウト
  4. ユーザ名はHTTPパラメータ名usernameにしなければならない
  5. パスワードはHTTPパラメータ名passwordにしなければならない
  6. Section 18.4.3, “Include the CSRF Token”を含めなければならない。詳細についてはChapter 18, Cross Site Request Forgery (CSRF)を参照してください。

5.4 Authorize Requests

今のところのサンプルは認証されたユーザを必須とし、アプリケーションのすべてのURLで認証を必要とします。http.authorizeRequests()に複数の子を追加することでURLにカスタム要求を定義できます。

protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()                                                                1
            .antMatchers("/resources/**", "/signup", "/about").permitAll()                  2
            .antMatchers("/admin/**").hasRole("ADMIN")                                      3
            .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")            4
            .anyRequest().authenticated()                                                   5
            .and()
        // ...
        .formLogin();
}
  1. http.authorizeRequests()メソッドに複数の子があり、各matcherの宣言順序は意味を持ちます。
  2. すべてのユーザがアクセス可能な複数のURLパターンを指定しています。具体的には、URLが"/resources/“で始まる、”/signup"もしくは"/about"と一致するものに、すべてのユーザはアクセスできます。
  3. “/admin/"で始まるすべてのURLは"ROLE_ADMIN"ロールを持つユーザに制限されます。hasRoleメソッドを使用しているため、"ROLE_"プレフィクスを指定する必要が無い点に注意してください。
  4. “/db/"で始まるすべてのURLは"ROLE_ADMIN"と"ROLE_DBA"両方を持つユーザを必要とします。hasRole句を使用しているため、"ROLE_"プレフィクスを指定する必要が無い点に注意してください。
  5. 上記いずれにもマッチしなかったURLは認証されたユーザを必要とします。

5.5 Handling Logouts

WebSecurityConfigurerAdapterを使用する場合、ログアウト機能は自動的に組み込まれます。デフォルトでは/logoutURLにアクセスするとユーザはログアウトします。このとき、以下が行われます。

  • HTTP Session無効化
  • 設定されているすべてのRememberMe認証をクリーンアップ
  • SecurityContextHolderのクリア
  • /login?logoutへリダイレクト

ログイン機能の設定と同様ですが、ログアウト要求をカスタマイズするための各種オプションがあります。

protected void configure(HttpSecurity http) throws Exception {
    http
        .logout()                                                                1
            .logoutUrl("/my/logout")                                                 2
            .logoutSuccessUrl("/my/index")                                           3
            .logoutSuccessHandler(logoutSuccessHandler)                              4
            .invalidateHttpSession(true)                                             5
            .addLogoutHandler(logoutHandler)                                         6
            .deleteCookies(cookieNamesToClear)                                       7
            .and()
        ...
}
  1. ログアウトサポートの使用。WebSecurityConfigurerAdapterを使用する場合は自動的に適用される。
  2. ログアウトをトリガするためのURL(デフォルトは/logout)。CSRFプロテクションが有効化(デフォルトで有効化)されている場合、リクエストはPOSTでなければならない。詳細についてはJavaDocを参照。
  3. ログアウト後のリダイレクト先URL。デフォルトは/login?logout。詳細についてはJavaDocを参照。
  4. カスタムのLogoutSuccessHandlerを指定します。これを指定する場合はlogoutSuccessUrl()は無視されます。詳細についてはJavaDocを参照。
  5. ログアウト時にHttpSessionを無効化するかどうかを指定します。デフォルトでtrueです。裏でSecurityContextLogoutHandlerを設定します。詳細についてはJavaDocを参照。
  6. LogoutHandlerの追加。デフォルトでLogoutHandlerの末尾にSecurityContextLogoutHandlerが追加されます。
  7. ログアウト成功時に削除するクッキーの名前を指定可能。これは明示的にCookieClearingLogoutHandlerを追加するもののショートカット版です。

XML Namespace記法でログアウトを設定することも当然出来ます。詳細についてはSpring Security XML Namespaceセクションのlogout elementを参照してください。

基本的には、ログアウト機能をカスタマイズする場合、LogoutHandlerLogoutSuccessHandlerの実装を作成します。fluent APIを使用すると、よくある使い方のためのハンドラが裏で適用されます。

5.5.1 LogoutHandler

基本的に、LogoutHandlerの実装はログアウト処理の一部として振る舞うクラスとなります。このクラスは必要なクリーンアップの実行が期待されます。そのため例外はスローしてはいけません。いくつかの実装をSpring Securityは提供しています。

詳細はSection 17.4, “Remember-Me Interfaces and Implementations”を参照してください。

LogoutHandlerの実装を直接登録するやり方とは別に、LogoutHandlerの各実装が裏で登録されるfluent APIのショートカットも存在します。例えば、deleteCookies()はログアウト正常終了時に削除するクッキー名を一つ以上指定します。このショートカットはCookieClearingLogoutHandlerの追加と同等です。

5.5.2 LogoutSuccessHandler

LogoutFilterは適当な宛先にリダイレクトあるいはフォワードする処理を行い、このフィルタがログアウトを正常処理した後にLogoutSuccessHandlerが呼ばれます。このインタフェースはおおむねLogoutHandlerと同じですが、例外をスローする可能性があります。

以下の実装があります。

上述の通り、SimpleUrlLogoutSuccessHandlerの直接指定は必要ありません。代わりに、logoutSuccessUrl()によるfluent APIのショートカットがあります。これは裏でSimpleUrlLogoutSuccessHandlerをセットアップします。与えられるURLがログアウト発生後のリダイレクト先になります。デフォルトは/login?logoutです。

HttpStatusReturningLogoutSuccessHandlerREST APIに適します。ログアウト正常終了後にURLにリダイレクトせず、LogoutSuccessHandlerには返すHTTPステータスコードを指定しまs。指定しない場合はデフォルトでステータスコード200を返します。

5.5.3 Further Logout-Related References

5.6 Authentication

ここまで最も基本的な認証の設定を見てきました。次にもう少し詳しい認証設定のオプションについて見ていきます。

5.6.1 In-Memory Authentication

これまでのサンプルで単一ユーザのインメモリ認証の設定は既に見ています。以下は複数ユーザを設定する例です。

@Bean
public UserDetailsService userDetailsService() throws Exception {
    InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
    manager.createUser(User.withUsername("user").password("password").roles("USER").build());
    manager.createUser(User.withUsername("admin").password("password").roles("USER","ADMIN").build());
    return manager;
}

5.6.2 JDBC Authentication

JDBCベースの認証です。以下の例はアプリケーションにDataSourceが定義済みとします。jdbc-javaconfigのサンプルでJDBCベースの認証を使用しています。

@Autowired
private DataSource dataSource;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .jdbcAuthentication()
            .dataSource(dataSource)
            .withDefaultSchema()
            .withUser("user").password("password").roles("USER").and()
            .withUser("admin").password("password").roles("USER", "ADMIN");
}

5.6.3 LDAP Authentication

LDAPベースの認証です。ldap-javaconfigのサンプルでLDAPベースの認証を使用しています。

@Autowired
private DataSource dataSource;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth
        .ldapAuthentication()
            .userDnPatterns("uid={0},ou=people")
            .groupSearchBase("ou=groups");
}

上の例は以下のLDIFと組み込みApache DS LDAPインスタンスを使用します。

users.ldif

dn: ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groups

dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people

dn: uid=admin,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Rod Johnson
sn: Johnson
uid: admin
userPassword: password

dn: uid=user,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Dianne Emu
sn: Emu
uid: user
userPassword: password

dn: cn=user,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: user
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
uniqueMember: uid=user,ou=people,dc=springframework,dc=org

dn: cn=admin,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: admin
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org

5.6.4 AuthenticationProvider

ビーンとしてAuthenticationProviderを定義することでカスタムの認証を作れます。例えば、以下のようにAuthenticationProviderを実装するSpringAuthenticationProviderを作ることでカスタムの認証を行います。

これはAuthenticationManagerBuilderが処理されない場合にだけ使用します。

@Bean
public SpringAuthenticationProvider springAuthenticationProvider() {
    return new SpringAuthenticationProvider();
}

5.6.5 UserDetailsService

ビーンとしてUserDetailsServiceを定義することでカスタムの認証を作れます。例えば、以下のようにUserDetailsServiceを実装するSpringDataUserDetailsServiceを作ることでカスタムの認証を行います。

これはAuthenticationManagerBuilderが処理されないかつAuthenticationProviderBeanの定義が無い場合にだけ使用します。

@Bean
public SpringDataUserDetailsService springDataUserDetailsService() {
    return new SpringDataUserDetailsService();
}

また、ビーンとしてPasswordEncoderを定義することでパスワードのエンコード方法をカスタマイズ出来ます。例として、bcryptを使う場合は以下のようなビーン定義を追加します。

@Bean
public BCryptPasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

5.6.6 LDAP Authentication

5.7 Multiple HttpSecurity

複数の<http>ブロックを持つ感じで複数のHttpSecurityインスタンスを設定できます。カギはWebSecurityConfigurationAdapterの継承を複数個用意することです。例えば、以下は/api/で始まるURL用の設定とは別にもう一つ設けています。

@EnableWebSecurity
public class MultiHttpSecurityConfig {
    @Bean
    public UserDetailsService userDetailsService() throws Exception {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("user").password("password").roles("USER").build());
        manager.createUser(User.withUsername("admin").password("password").roles("USER","ADMIN").build());
        return manager;
    }

    @Configuration
    @Order(1)                                                        1
    public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        protected void configure(HttpSecurity http) throws Exception {
            http
                .antMatcher("/api/**")                               2
                .authorizeRequests()
                    .anyRequest().hasRole("ADMIN")
                    .and()
                .httpBasic();
        }
    }

    @Configuration                                                   3
    public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                .formLogin();
        }
    }
}

これまで通り認証のconfigurationを作ります。

  1. @Orderを持つWebSecurityConfigurerAdapterインスタンスを作成し、@Orderでこのクラスが先に参照されるようにしています。
  2. http.antMatcherでこのHttpSecurity/api/で始まるURLにのみ適用されるようにしています。
  3. 2.とは別のWebSecurityConfigurerAdapterを作成します。URLが/api/で始まらない場合にこちらの設定が使われます。@Order1を指定しているため、この設定はApiWebSecurityConfigurationAdapterの後になります。(@Order無しのデフォルトは末尾)

*1:試験的な開発中機能の意味。ただ、これ書いてる時点でgradleは3.5とかなのでもうincubatingでも何でもないかもしれない