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を使った方法でも。