🍃

Spring BootでAzure Active DirectoryのOAuth2認証を利用する

2022/10/26に公開

Spring Boot にて、Azure Active DirectoryのOAuth2認証を利用してみます。
Azure Active Directory用の Spring Boot Starterが用意されているので簡単に実装することができます。

今回確認に利用したプロジェクト全体は、下記に配置しています。

Azure Active Directory側での準備

Azure Active Directory側で下記を用意しておく必要があります。

  • (テナントが未作成なら)テナント作成
  • 「アプリの登録」から、
    • 「新規登録」で、対象のアプリケーションを登録
      • リダイレクトURIは http://localhost:8080/login/oauth2/code/
    • 「証明書とシークレット」で、新しいクライアントシークレットを作成
    • 「アプリロール」からアプリロールの作成
  • (ユーザが未作成なら)ユーザの作成
  • 「エンタープライズ アプリケーション」で登録したアプリケーションを選び、「ユーザとグループ」から「ユーザまたはグループの追加」を選んで、ユーザ+ロールの組み合わせを割り当て

詳しい操作は下記が参考になります。

プロジェクト

Spring Bootの最新(2022年10月23日時点)となる2.7.5で確認します。

  • Java 17
  • Spring Boot 2.7.5
  • Gradle 7.5

build.gradleは下記のようになります。

build.gradle
plugins {
    id 'org.springframework.boot' version '2.7.5'
    id 'io.spring.dependency-management' version '1.0.15.RELEASE'
    id 'java'
}

group = 'com.github.onozaty'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
    mavenCentral()
}

ext {
    set('springCloudAzureVersion', "4.3.0") // 4.4.0 だと問題があって動かないため
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'com.azure.spring:spring-cloud-azure-starter-active-directory'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

dependencyManagement {
    imports {
        mavenBom "com.azure.spring:spring-cloud-azure-dependencies:${springCloudAzureVersion}"
    }
}

tasks.named('test') {
    useJUnitPlatform()
}

spring-cloud-azure-starterの最新は4.4.0ですが、4.4.0では下記の問題があって、OAuth2でエラーが発生します。1つ前の4.3.0だと問題が無いので、4.3.0を使って回避しています。

Azure Active Directoryの情報をアプリケーション側に設定

Active Directoryの情報を設定します。以下は例です。

src/main/resources/application.properties
# Enable related features.
spring.cloud.azure.active-directory.enabled=true
# Specifies your Active Directory ID:
spring.cloud.azure.active-directory.profile.tenant-id=22222222-2222-2222-2222-222222222222
# Specifies your App Registration's Application ID:
spring.cloud.azure.active-directory.credential.client-id=11111111-1111-1111-1111-1111111111111111
# Specifies your App Registration's secret key:
spring.cloud.azure.active-directory.credential.client-secret=AbCdEfGhIjKlMnOpQrStUvWxYz==

簡単なWebページの作成

アクセスの制御を確認するため、下記の3つのHTMLを作成します。

  • src/main/resources/static/index.html トップページ
  • src/main/resources/static/admin/admin.html admin向けのページ
  • src/main/resources/static/user/user.html user向けのページ
src/main/resources/static/index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Top</title>
  </head>
  <body>
    <ul>
      <li><a href="./admin/admin.html">Admin</a></li>
      <li><a href="./user/user.html">User</a></li>
      <li><a href="./logout">Logout</a></li>
    </ul>
  </body>
</html>
src/main/resources/static/admin/admin.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Admin</title>
  </head>
  <body>
    <ul>
      <li><a href="../">Top</a></li>
      <li><a href="../user/user.html">User</a></li>
      <li><a href="../logout">Logout</a></li>
    </ul>
  </body>
</html>
src/main/resources/static/user/user.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>User</title>
  </head>
  <body>
    <ul>
      <li><a href="../">Top</a></li>
      <li><a href="../admin/admin.html">Admin</a></li>
      <li><a href="../logout">Logout</a></li>
    </ul>
  </body>
</html>

Spring Security の設定

ロール毎にアクセス先の制限を行うため、独自のセキュリティ構成を設定します。

src/main/java/com/example/azure/AzureAdOAuthLoginSecurityConfig.java
package com.example.azure;

import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import com.azure.spring.cloud.autoconfigure.aad.AadWebSecurityConfigurerAdapter;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AzureAdOAuthLoginSecurityConfig extends AadWebSecurityConfigurerAdapter {

    // https://learn.microsoft.com/ja-jp/azure/developer/java/spring-framework/spring-boot-starter-for-azure-active-directory-developer-guide#protect-a-resource-serverapi
    // 独自のセキュリティ構成にしたい場合は、AadWebSecurityConfigurerAdapterを継承して指定する
    // (デフォルトはDefaultAadResourceServerWebSecurityConfigurerAdapterで構成したものになっている)

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);

        // https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html
        http.authorizeHttpRequests(
                authz -> authz
                        // admin配下は要adminロール(アプリロールで指定した名前がAPPROLE_の後に付く)
                        .mvcMatchers("/admin/**").hasAnyAuthority("APPROLE_admin")
                        // user配下は要userロール
                        .mvcMatchers("/user/**").hasAnyAuthority("APPROLE_user")
                        // それ以外のリクエストも全て認証必要(ロールは何でもOK(無くてもOK))
                        .anyRequest().authenticated());

        http.logout(
                // GETでもログアウトが受け付けられるように
                logout -> logout.logoutRequestMatcher(new AntPathRequestMatcher("/logout")));
    }
}

動作確認

アプリロールでadminとuserの両方が割り当てられているユーザでログインします。
権限があるのでAdminのページも問題無く表示されます。

アプリロールでuserのみが割り当てられているユーザでログインします。
Adminのページに遷移すると、権限が無いので 403 Forbidden となります。

参考

Discussion