Azureの小ネタ (改)

~Azureネタを中心に、色々とその他の技術的なことなどを~

Spring Boot と Azure AD B2Cの連携

前回はAzure Active Directoryで認証を行いましたが、今回はAzure Active Directory B2C(以下B2C)でやっていきたいと思います。

B2Cそのものの説明はしないので、Azure Active Directory B2C のドキュメント | Microsoft Docs あたりを参考にしてください。

連携方法

連携方法としては、Spring Boot の標準ライブラリのみで行うか、Microsoftが提供しているB2C向けStarterを使うかのいずれかになります。

標準ライブラリを使った方法

構成は前回と同じです。同一プロジェクトに設定を同居できますので、プロジェクトを流用すればいいでしょう。次にプロパティファイルに以下を追加します。 azureadb2cという名前でプロバイダを登録します。この名前は任意なので、変更しても構いません。

spring:
  security:
    oauth2:
      client:
        registration:
          azureadb2c:
            client-id: <<b2c client id>>
            client-secret : <<b2c client secret>>
            client-name: Azure Active Directory B2C
            scope: 
            - openid
            - <<b2c client id>>
            client-authentication-method: post
            authorization-grant-type: authorization_code
            redirect-uri: http://localhost:8080/login/oauth2/code/azureadb2c
        provider:
          azureadb2c:
            token-uri: https://<<b2c tenant name>>.b2clogin.com/morisb2c.onmicrosoft.com/<<user flow name>>/oauth2/v2.0/token
            authorization-uri: https://<<b2c tenant name>>.b2clogin.com/morisb2c.onmicrosoft.com/<<user flow name>>/oauth2/v2.0/authorize
            user-name-attribute: name
            jwk-set-uri: https://<<b2c tenant name>>.b2clogin.com/morisb2c.onmicrosoft.com/<<user flow name>>/discovery/v2.0/keys

注意点としては、以下。

  • Scopeには openid の他にクライアントIDを指定しないと認証に失敗します。ここで少しはまりましたが、B2CのDocsにもどこかにこっそり書いてあるはずです。
  • URIの一部にサインイン用のユーザーフロー名が入るので、B2C側で作成したフロー名を指定してください。

この例では、パスワードリセットとユーザープロファイル編集は無視しています。 設定をするとログイン画面にB2C向けのリンクが表示されるはずなので、あとは普通に認証後にログイン出来ると思います。

B2C Starterを使った方法

以下の依存を追加するとB2C Starterを利用できます。

        <dependency>
            <groupId>com.microsoft.azure</groupId>
            <artifactId>azure-active-directory-b2c-spring-boot-starter</artifactId>
            <version>2.2.5</version>
        </dependency>

この場合は以下の通りプロパティファイルには独自の設定をする必要があります。

azure:
  activedirectory:
    b2c:
      tenant: <<your tenant name>>
      oidc-enabled: true
      client-id: <<client id>>
      client-secret: <<client secret>>
      reply-url: http://localhost:8080/home
      logout-success-url: http://localhost:8080/logout
      user-flows:
        sign-up-or-sign-in: <<sign in flow name>>
        profile-edit: <<profile edit flow name>>
        password-reset: <<password reset flow name>.

こちらのStarterを使うとサインイン、ユーザープロファイル編集、パスワードリセットの3つのフローを設定するだけで扱えるようになります。URIも内部で処理してくれるので面倒くさくはありません。 あと、reply-urlhttp://localhost:8080/にするとうまく動作しなくなるので、 homeで受けて同じページを返すような処理が必要です(いまいちこのあたりの処理に精通していないので失敗原因がわかりません)

実行すると、例のログイン画面に3つでてくるでしょう。実際は、この画面を使うこともないでしょうし、うまく隠蔽してください。

f:id:StateMachine:20200806103142p:plain

まとめ

すべては後半のStarerを使ったやつはDocsにチュートリアルがありますので、そちらを参考にしてくれたほうがはかどると思います。とはいえ、内容が微妙に間違ってたり古くなってたり誤訳だったり、説明不足だったりすることが多いので、実際動かすのは手間がかかる場合もありあmす。

docs.microsoft.com

以上

Azure Active Directory で Spring Boot and OAuth2 改訂版

以前にAzure Active Directory で Spring Boot and OAuth2 - Azureの小ネタ (改) という記事を書いたのですが、Spring 周りも色々アップデートされ、内容が古くなってきているので最新の環境で検証してみた備忘録です。

最近の状況

前記事BLOGで使用したSpring Security OAuth は Deprecatedになっています。またOAuth2 Boot より引用すると以下のようになっています。

The following projects are in maintenance mode:

  • spring-security-oauth2
  • spring-security-oauth2-autoconfigure

You are, of course, welcome to use them, and we will help you out!

However, before selecting spring-security-oauth2 and spring-security-oauth2-autoconfigure, you should check out Spring Security’s feature matrix to see if the new first-class support meets your needs.

マイグレガイドがあるので、これを読めばよいと思われます。 OAuth 2.0 Migration Guide · spring-projects/spring-security Wiki

準備

AADアプリを登録して以下を取得します。

  • テナントID
  • クライアントID(アプリケーションID)
  • シークレットシークレット(パスワード)

ちょっと古いもしますが、 チュートリアル: Azure Active Directory でアプリを登録する (Common Data Service) - Power Apps | Microsoft Docs を参考にしてください。

プロジェクト

Spring Initializr で、適当にWebアプリをSpring Bootで作りましょう。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>

サンプルで使うライブラリはここら辺です。 spring-boot-starter-oauth2-clientspring-boot-starter-securityがキモでしょうか。

設定

GitHubとかGoogleとかは基本的な設定が取り込まれているようですが、AADはそんなことになってないので、フル設定します。 azureadという項目は任意の名前で構いません。が、 registration下とprorivder下は同じ名前にしてください。項目名がそのまま説明になっているので補足は特にないです。

spring:
  devtools:
    add-properties: true
  thymeleaf:
    cache: false
  security:
    oauth2:
      client:
        registration:
        # Azure Active Directory
          azuread:
            client-id: クライアントID
            client-secret : クライアントシークレット
            client-name: Azure Active Directory
            scope: 
            - profile
            client-authentication-method: post
            authorization-grant-type: authorization_code
            redirect-uri: http://localhost:8080/login/oauth2/code/azuread
        provider:
          azuread:
            token-uri: https://login.microsoftonline.com/テナントID/oauth2/token
            authorization-uri: https://login.microsoftonline.com/テナントID/oauth2/authorize
            user-info-uri: https://graph.windows.net/me?api-version=1.6
            user-name-attribute: displayName

プログラムの修正

プログラム上での初期化は以下です。 認証を有効にして、例外ページ、ログアウト等などを設定します。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private ClientRegistrationRepository repo;

    public SecurityConfig(ClientRegistrationRepository repo) {
        this.repo = repo;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/", "/webjars/**", "/error**").permitAll()
                .anyRequest().authenticated()
                .and()
                .logout()
                .logoutSuccessUrl("/")
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID")
                .and()
                .oauth2Login()
                .authorizationEndpoint();
    }
}

次に resources/templates/index.html を作成してます。Controllerで適宜、このページを返すようにしておいてください。

<!DOCTYPE html>
<html lang="en" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Home</title>
    <link rel="stylesheet" th:href="@{/webjars/bulma/0.9.0/css/bulma.min.css}" />
</head>

<body>
    <div class="container is-fluid" style="padding-top:3rem">
        <div class="columns">
            <h3 class="title">Spring boot oauth2 sample application with AADC.</h3>
        </div>

        <div sec:authorize="isAuthenticated()">
            <div class="columns">
                <div class="column">
                    Logged inn
                </div>
            </div>

            <div class="columns">
                <div class="column">
                    Hi <span th:text="${#authentication.name}"></span> san!
              </div>
            </div>
            <div class="columns">
                <div class="column is-2">
                    <a href="/logout" class="button is-info">Logout (GET)</a>
                </div>
                <div class="column is-2">
                    <form th:action="@{/logout}" method="post">
                        <button class="button is-primary" type="submit">Logout (POST)</button>
                    </form>
                </div>

                <logout/>
            </div>

        </div>

        <div class="columns">
            <div class="column">
                <div sec:authorize="isAnonymous()">
                    <div class="controll">
                        <a class="button is-primary" href="/login">Login</a>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>

</html>

実行例

mvn clean package spring-boot:runで実行して、 localhost:8080にアクセスします。

認証されていないので、Loginボタンがでます。

f:id:StateMachine:20200804105644p:plain

ボタンを押すと、Providerの選択画面に飛ばされます。複数設定すると複数表示されますが、今回はAADだけなので、AADのみが表示されます。AADを選択すると、MSの例の認証画面に飛ばされるので、AADのアカウントで認証してください。

f:id:StateMachine:20200804105658p:plain

名前が表示されればOKです。LogoutはGETとPOSTする方法があって、GETすると結局フレームワーク側が用意するページに一旦飛ばされてPOSTする挙動になるだけです。

f:id:StateMachine:20200804105709p:plain

まとめ

ほぼフレームワークがやってくれるので、設定方法さえ分ってしまえば特に難しいことは無いと思います。結局、うまく動作しなくて設定で試行錯誤するわけで、こうやってBLOGに備忘録として記録されるわけです。

またサンプルがまとまったらまたGitHubにでもあげておこうかと思います。次回は、AAD B2Cを使った方法でも。

JavaからAzure Application Insightsを使う(3)

今回もApplication Insights やっていきましょう!(YouTube風)

前回までは必要なライブラリを取り込む形のSDK方式でしたが、今回はエージェント方式を試してみます。度々言及していますが、2.6.x系にもエージェントはありますが、3.0 preview を使います。

準備

執筆時点では、3.0 preview-5が最新です。

Releases · microsoft/ApplicationInsights-Java

はじめに applicationinsights-agent-3.0.0-PREVIEW.5.jar をダウンロードしてプロジェクトのルートフォルダにでも放り込んでおきましょう。

次に、ApplicationInsights.jonを用意します。jarと同じ場に展開します。注意点としては、

  • インストルメンテーションキーは、接続文字列として設定します。環境変数とかに設定する方法もあります(WebAppsにデプロイした時など用)
  • オプションは今のところ、preview キーの階層に定義します。 GAしたときに設定がそのまま利用されないようなポカ除けでしょうか。

のような点があります。

{
    "instrumentationSettings": {
        "connectionString": "InstrumentationKey=<<your-instrumentation-key>>",
        "preview": {
            "roleName": "MyWebApplicaiton with Java Agent 3-preview",
            "instrumentation": {
                "logging": {
                    "threshold": "INFO"
                }
            }
        }
    }
}

プロジェクト

前回のSpring Boot Webプロジェクトでも流用してください。そして実行時オプションに以下を追加します。pom.xmの一部です。maven plugin の配下にconfiguration 以下を追加します。

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <jvmArguments>
                    -javaagent:C:\ws\blog\appInsight\web.sample.agent3\applicationinsights-agent-3.0.0-PREVIEW.5.jar
                    </jvmArguments>
                </configuration>
            </plugin>
        </plugins>
    </build>

java agent とは?ってところは、 古い記事ですが 独自のプロファイリング・ツールを構築する が参考になると思います(安定のdevelopers works)

適当にmvnから実行すればOkでしょう。

mvn clean package spring-boot:run

何が記録されるのか

Docsを参照するとわかるのですが、まるっと引用します。SDKを取り込まなくてもエージェントを指定するだけで以下の情報がApplication Insightsで収集できます。

  • Requests
    • JMS コンシューマー
    • Kafka コンシューマー
    • Netty/WebFlux
    • Servlets
    • Spring スケジュール
  • 分散トレース伝達での依存関係
    • Apache HttpClient と HttpAsyncClient
    • gRPC
    • java.net.HttpURLConnection
    • JMS
    • Kafka
    • Netty クライアント
    • OkHttp
  • その他の依存関係
    • Cassandra
    • JDBC
    • MongoDB (非同期および同期)
    • Redis (Lettuce および Jedis)
  • ログ
    • java.util.logging
    • Log4j
    • SLF4J/Logback
  • メトリック
    • マイクロメーター (Spring Boot アクチュエータ メトリックを含む)
    • JMX メトリック

まあ、いままでやってきたSDKなどの設定はなんだったのか?という話になってくるわけなんですが。

ポータルでの確認

Web + JDBCでSql Databaseにアクセスしてみた例です。リクエストにSQLDBへの依存メトリクスが関連付いて記録されていることが分ると思います。

f:id:StateMachine:20200731165618p:plain

DBへどんなクエリを発行しているのかも分ります。

f:id:StateMachine:20200731165636p:plain

まとめと雑感

自動的に収集してくれるのですが、きちんと設定しないと余分な情報まで収集されてしまうかなと思います。またSDK方式でやったようなリクエストテレメトリにカスタムプロパティを追加するような方法が分ません。このあたりは以下のように言及されているのですが、

3.0 以降での目標は、標準 API を使用してカスタム テレメトリを送信できるようにすることです。 Micrometer、OpenTelemetry API、および一般的なログ記録フレームワークがサポートされています。 Application Insights Java 3.0 では、テレメトリが自動的にキャプチャされ、自動収集されたすべてのテレメトリと連動して、関連付けが行われます。 このため、現時点では、Application Insights 3.0 を使用した SDK をリリースする予定はありません。 Application Insights Java 3.0 では、Application Insights Java SDK 2.x に送信されるテレメトリを既にリッスンしています。 この機能は、既存の 2.x ユーザーのアップグレード ストーリーの重要な部分であり、OpenTelemetry API が GA になるまでの、カスタム テレメトリ サポートの重要なギャップを埋めるものです。

3.0ではSDKをリリースする予定がなく、今のところ 2.x系のSDKを使ってカスタムすれば勝手にマージしてくれるようにも見えるのですが。Open Telemetry API ってよく分ってないのですが、GA後にまた試してみたいと思います。

ここらの話かな。 OpenTelemetry

参考

Java アプリケーションを任意の環境で監視する - Azure Monitor Application Insights - Azure Monitor | Microsoft Docs

任意の場所の Java アプリケーションを監視する - Azure Monitor Application Insights - Azure Monitor | Microsoft Docs