Azureの小ネタ (改)

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

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

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

昨日に引き続きApplication Insightsネタです。

前回に引き続きJavaからApplication Insightsを使う際の備忘録です。今回はWebアプリケーションのリクエストをApplication Insightsに記録したいと思います。またWebアプリといっても様々なものがあるのですが、ここではSpring Boot を対象です(それが一番楽だから)

プロジェクトの用意

Spring Initializr を使ってひな形を作成しましょう。最近は、Visual Studio Codeから仕える拡張もあるので、そちらから利用してみるのも良いかもしれません。

f:id:StateMachine:20200731102118p:plain
Generate spring boot application

このとき、後で必要な依存を含められるのですが、それは手動でやってきます。

依存

Spring Boot向けに、 Azure Application Insights Spring Boot Starter が用意されているのでこれを利用します。適宜Bootアプリ向けの設定を行ってくれます。

Maven Repository: com.microsoft.azure » applicationinsights-spring-boot-starter » 2.6.1

<dependency>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>applicationinsights-spring-boot-starter</artifactId>
    <version>2.6.1</version>
</dependency>

実はこの依存元になる applicationinsights-web ライブラリがあるのですが、このstarter は Spring Boot ラッパーだと思っていただければ良いと思います。ApplicationInsights.xml での設定ではなく、application.yaml (properties) などで設定可能になっています。

したがって、Bootアプリでなければ上記への依存とApplication.xmlの設定で同じようにテレメトリが取得出来ると思います。(Bootアプリでもstarter使わずwebを使えます)

設定

src/main/resources/application.yamlに以下の設定します。インストルメンテーションキー以外に spring.application.nameを設定しておくと、テレメトリのCloud Role Name プロパティにアプリ名が記録されます。

spring:
    application:
        name: MyWebApplication
azure:
    application-insights:
        instrumentation-key: インストルメンテーションキー

ソース

適当にリクエストを受け付けられるように修正します。 取りあえず文字列を返すだけの簡単なAPIです。ログへの記録とかは必要なく、リクエストがApplication Insightsにリクエストテレメトリとして記録されます。

package com.example.web.sample;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @RequestMapping("/")
    public String home() {
        return "Hello Application Insights World";
    }
}
mvn clean package spring-boot:run

で実行して、localhost:8080/に何度かアクセスしてみてください。記録されるまで数分の遅延があるので注意してください(前回言ってなかった気がする)

Azure ポータルでの確認

localhostからのリクエストが記録されているとおもいます。展開すると詳しい記録を見ることが出来ます。

f:id:StateMachine:20200731102217p:plain
request

このように、とくにプログラムに手を入れることなくいくつかのテレメトリを自動で収集できるようになります。

ログ出力

上記の設定のみですと、前回のようなログがApplication Insightsに記録されません。同じ設定(依存とlogback.xml)をすればちゃんとログが飛びます。 ただし、 logbac.xml の中にインストルメンテーションキーの設定はしなくてもよいです。別のを設定するとどういう挙動をするのかは知らないです

内部実装

SDKの実装的な話をすると、以下のあたりでリクエスト、レスポンスのフィルター処理をしています。内部実装が気になる方は参考にしてください。

リクエストテレメトリにカスタムプロパティを追加する

テレメトリにはカスタムプロパティを追加できるのですが、そのコンテキストでの該当テレメトリインスタンスを取得する方法を紹介します。例えば、独自ヘッダやなどを一緒に記録したい場合に役立つかもしれません。

ApplicationInsights SDK が用意するThreadContextクラスにリクエストテレメトリを取得する静的メソッドがあるので、順次取り出していきます。getPropertyでカスタムプロパティが取り出せますのでputで設定するだけす。ここでは、リクエストを受けるメソッドで直接やっていますが、定型処理ならばSpringの機能であるインターセプターの仕組みで常に記録するのがいいかもしれません。

@RestController
public class HomeController {

    private final Logger log = LoggerFactory.getLogger(HomeController.class);

    @RequestMapping("/")
    public String home() {
        log.info("info message");

        var telemetory = ThreadContext.getRequestTelemetryContext().getHttpRequestTelemetry();
        telemetory.getProperties().put("Some Cusome Property", (new Date().toString()));

        return "Hello Application Insights World";
    }
}

f:id:StateMachine:20200731102313p:plain
custom property

まとめと参考

前回も説明したとおり2.6.x系 と 3.0 previewがあるのですが、ぶっちゃけ3.0 から完全なエージェント方式っぽくなるようで、いずれ2.6.x系は廃れていくのかもしれません(不正確)とはいえ、自分でテレメトリ弄りたい場合もあるだろうし。

つづく...