以前も似たような記事を書いていましたが、AzureADのSpringスターターが整理され、実装が非常に簡単になっていたので改めて記事にしました。
前提条件
- Spring Bootで"Hello World!"ができる(出来れば数ページのバリエーションが実装済みだと望ましい)
- AzureADの管理者アカウントを持っている
- Spring Boot+Spring WebのアプリにMicrosoft365アカンウントで認証させたい
上記のような方に向けた記事となります。
AzureAD側での下準備
ドメインにアプリを登録
AzureADスターターの開発者ガイドの「Azure Active Directory インスタンスの作成」を参考にテナントの作成を行います。
また、認証には当然ユーザー情報が必要となりますので、ユーザーの作成が必要です。グループ別のアクセス認可を行う場合には、グループの作成も必要です。
既にテナントやユーザー、グループの設定が終わっている場合は、「Spring Boot アプリのアプリケーション登録を追加する」だけ済ませておけば大丈夫です。
アプリケーション登録の注意点
クライアント シークレットの値は登録時にしか表示されないことと、 API のアクセス許可で取得するアクセスの内容に注意しましょう。
以前の記事ではAPIにAzureAD Graphが使用されていましたが、バージョン3.6以降ではデフォルトでMicrosoft Graphを使用してアクセスするため、APIの許可もMicrosoft Graphから行うことになります。
アクセス許可の内容(必須)
- Directory.AccessAsUser.All:サインインしているユーザーとしてディレクトリにアクセスする
- User.Read:サインインの実行とそのユーザーのプロファイルを参照する
上記があれば最低限のログイン処理が可能です。
リダイレクトURIについて
AzureADスターターを使用してログインを実装する場合、リダイレクトURIはhttp|https//{ドメイン}/login/oauth2/code/
で固定されます。Spring SecurityのOAuth2(OpenID Connect)のテンプレートで生成されるURIとは異なりますので注意してください。
利用するStarterと依存関係の記述
利用するStarter(必須)
必須のスターターは下記となります。Spring Initializrに表示されるスターター名で記述してあります。
- Spring Web
- OAuth2 Client
- Azure Active Directory
Gradleで依存性を記述する場合
Spring Initializrを利用するのが確実ですが、執筆時点でAzureADスターターのバージョンが上がっているため、バージョンの指定を下記のように修正することをお勧めします。
// 前略
ext {
set('azureVersion', "3.6+")
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.azure.spring:azure-spring-boot-starter-active-directory'
}
dependencyManagement {
imports {
mavenBom "com.azure.spring:azure-spring-boot-bom:${azureVersion}"
}
}
// 後略
application.ymlの編集
azure:
activedirectory:
authorization-clients:
graph:
scopes:
- https://graph.microsoft.com/User.Read
- https://graph.microsoft.com/Directory.AccessAsUser.All
tenant-id: AzureADのテナントのID
client-id: AzureADに登録したアプリのID
client-secret: AzureADに登録したクライアントシークレット
user-group:
allowed-group-names:
- グループ名1
- グループ名2
上記のように整理されて非常にシンプルになりました。
AzureADスターターの開発者ガイドの「構成プロパティ」のプロパティにはallowed-groups
という表記がありますが、アクセスを許可するグループをグループ名で指定するときはallowed-group-names
が推奨されます。
また、グループ名は変更される場合があるため、より安全なのはallowed-group-ids
を使用して一意に指定することです。
Configurerクラスを作成する
デフォルトでは、DefaultAADWebSecurityConfigurerAdapterにより、/login以外のパスが自動的に保護されるようになっています。/loginを除外しているのはログイン画面がないとどこにもアクセスできなくなってしまうからですね。
パスの一部に認証をし、また、グループによってアクセスできるパスを変更するなどの構成が必要な場合、AADWebSecurityConfigurerAdapterを継承してクラスを作成します。
AzureADスターターのGitHubによると、下記のような構成クラスではすべてのパスにAzureADの認証が行われます。
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AADOAuth2LoginSecurityConfig extends AADWebSecurityConfigurerAdapter {
/**
* Add configuration logic as needed.
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
.anyRequest().authenticated();
// Do some custom configuration
}
}
既にあるアプリケーションにAzureADによる認証を組み込んでみるだけのテストであれば、上記のクラスをそのまま使用しても良いでしょう。
実用的には明示的にsuper.configure(http);
を呼ぶことでAzureADによる認証に関する諸々の設定がおこなわれ、http.authorizeRequests()
以下で、認証の必要なパスやグループによるアクセス許可を指定することができます。
Spring Security導入時のCSRF対策について
Spring Securityが導入されることでCSRF対策が導入されます。これにより、POST、DELETEなどのメソッドのリクエストに対してトークンのチェックが行われます。
テンプレートエンジンにThymeleafを使用している場合は、依存関係にthymeleaf-extras-springsecurity5を追加することでth:method="post"などのCSRF対策が必要なフォームタグに対し、自動的にトークンの隠しフィールドを追加してくれます。
また、Ajaxに対しても同様にトークンのチェックが行われるため、動作しなくなる場合があります。
Spring SecurityドキュメントのAjax および JSON リクエストに公式な対応方法がありますので、これもテンプレートで実装しておくと良いでしょう。
また、このあたりの情報は@opengl-8080さんの「Spring Security 使い方メモ CSRF」が大変参考になります。この記事のシリーズはSpring Securityを使用するうえで大変参考になるので他の記事も目を通すことをお勧めします。
参考資料
azure-sdk-for-java:MicrosoftのAzure関連のGitHub
Spring Security 使い方メモ CSRF
Spring Security リファレンス