The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

Spring Security API の Quarkus エクステンション

セキュリティ認可にはJava標準のアノテーションを使用することが推奨されていますが、Quarkusは、 spring-security エクステンションという形で、Spring Securityの互換性レイヤーを提供しています。

このガイドでは、Quarkusアプリケーションで、よく知られているSpring Securityアノテーションを活用し、ロールを使用してRESTfulサービスの認可を定義する方法について説明します。

前提条件

このガイドを完成させるには、以下が必要です:

  • 約15分

  • IDE

  • JDK 17+がインストールされ、 JAVA_HOME が適切に設定されていること

  • Apache Maven 3.9.6

  • 使用したい場合は、 Quarkus CLI

  • ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること

  • Spring Web エクステンションにある程度精通している

ソリューション

次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、完成した例にそのまま進んでも構いません。

Git リポジトリーのクローンを作成: git clonehttps://github.com/quarkusio/quarkus-quickstarts.git、または アーカイブ をダウンロードします。

ソリューションは spring-security-quickstart ディレクトリ にあります。

Maven プロジェクトの作成

まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します:

コマンドラインインタフェース
quarkus create app org.acme.spring.security:spring-security-quickstart \
    --extension='spring-web,spring-security,quarkus-elytron-security-properties-file,rest-jackson'
cd spring-security-quickstart

Gradleプロジェクトを作成するには、 --gradle または --gradle-kotlin-dsl オプションを追加します。

Quarkus CLIのインストールと使用方法の詳細については、 Quarkus CLI ガイドを参照してください。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.9.3:create \
    -DprojectGroupId=org.acme.spring.security \
    -DprojectArtifactId=spring-security-quickstart \
    -Dextensions='spring-web,spring-security,quarkus-elytron-security-properties-file,rest-jackson'
cd spring-security-quickstart

Gradleプロジェクトを作成するには、 -DbuildTool=gradle または -DbuildTool=gradle-kotlin-dsl オプションを追加します。

Windowsユーザーの場合:

  • cmdを使用する場合、(バックスラッシュ \ を使用せず、すべてを同じ行に書かないでください)。

  • Powershellを使用する場合は、 -D パラメータを二重引用符で囲んでください。例: "-DprojectArtifactId=spring-security-quickstart"

このコマンドは、spring-webspring-security、および security-properties-file エクステンションをインポートするプロジェクトを生成します。

すでに Quarkus プロジェクトが設定されている場合は、プロジェクトのベースディレクトリーで以下のコマンドを実行することで、プロジェクトに spring-webspring-security および security-properties-file エクステンションを追加することができます。

コマンドラインインタフェース
quarkus extension add spring-web,spring-security,quarkus-elytron-security-properties-file,rest-jackson
Maven
./mvnw quarkus:add-extension -Dextensions='spring-web,spring-security,quarkus-elytron-security-properties-file,rest-jackson'
Gradle
./gradlew addExtension --extensions='spring-web,spring-security,quarkus-elytron-security-properties-file,rest-jackson'

これにより、 pom.xml に以下が追加されます:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-spring-web</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-spring-security</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-elytron-security-properties-file</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-rest-jackson</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-spring-web")
implementation("io.quarkus:quarkus-spring-security")
implementation("io.quarkus:quarkus-elytron-security-properties-file")
implementation("io.quarkus:quarkus-rest-jackson")

security-properties-file の詳細については、quarkus-elytron-security-properties-file エクステンションのガイドを確認してください。

GreetingController

The Quarkus Maven plugin automatically generated a controller with the Spring Web annotations to define our REST endpoint (instead of the Jakarta REST ones used by default). First create a src/main/java/org/acme/spring/security/GreetingController.java, a controller with the Spring Web annotations to define our REST endpoint, as follows:

package org.acme.spring.security;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/greeting")
public class GreetingController {

    @GetMapping
    public String hello() {
        return "Hello Spring";
    }
}

GreetingControllerTest

コントローラーのテストも作成されていることに注意してください。

package org.acme.spring.security;

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;

@QuarkusTest
class GreetingControllerTest {
    @Test
    void testHelloEndpoint() {
        given()
          .when().get("/greeting")
          .then()
             .statusCode(200)
             .body(is("Hello Spring"));
    }

}

アプリケーションをパッケージ化して実行する

アプリケーションを実行します。

コマンドラインインタフェース
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

ブラウザで http://localhost:8080/greeting を開きます。

結果は {"message": "hello"} のようになります。

コントローラーを変更して、hello メソッドを保護します。

hello メソッドへのアクセスを特定のロールを持つユーザーに制限するために、@Secured アノテーションが使用されます。更新されたコントローラーは次のようになります。

package org.acme.spring.security;

import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/greeting")
public class GreetingController {

    @Secured("admin")
    @GetMapping
    public String hello() {
        return "hello";
    }
}

この例でユーザーとロールを設定する最も簡単な方法は、security-properties-file エクステンションを使用することです。このエクステンションにより、基本的に、ユーザーとロールをメインの Quarkus 設定ファイル (application.properties) で定義できます。このエクステンションの詳細については、the associated guide を確認してください。設定例は以下のとおりです。

quarkus.security.users.embedded.enabled=true
quarkus.security.users.embedded.plain-text=true
quarkus.security.users.embedded.users.scott=jb0ss
quarkus.security.users.embedded.roles.scott=admin,user
quarkus.security.users.embedded.users.stuart=test
quarkus.security.users.embedded.roles.stuart=user

テストも更新する必要があることに注意してください。以下のようになります。

GreetingControllerTest

package org.acme.spring.security;

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;

@QuarkusTest
public class GreetingControllerTest {

    @Test
    public void testHelloEndpointForbidden() {
        given().auth().preemptive().basic("stuart", "test")
                .when().get("/greeting")
                .then()
                .statusCode(403);
    }

    @Test
    public void testHelloEndpoint() {
        given().auth().preemptive().basic("scott", "jb0ss")
                .when().get("/greeting")
                .then()
                .statusCode(200)
                .body(is("hello"));
    }

}

変更をテストする

Automatically

Press r, while in dev mode, or run the application with:

Maven
./mvnw test
Gradle
./gradlew test

All tests should succeed.

Manually

アクセスが許可されました

ブラウザーを http://localhost:8080/greeting で再度開いて、表示されたダイアログに scottjb0ss を導入します。

hello という単語が表示されます。

アクセス禁止

ブラウザーを http://localhost:8080/greeting で再度開いて、表示されたダイアログを空にします。

結果は次のようになります。

Access to localhost was denied
You don't have authorization to view this page.
HTTP ERROR 403

Some browsers save credentials for basic authentication. If the dialog is not displayed, try to clear saved logins or use the Private mode

サポートされているSpring Securityアノテーション

Quarkus currently only supports a subset of the functionality that Spring Security provides with more features being planned. More specifically, Quarkus supports the security related features of role-based authorization semantics (think of @Secured instead of @RolesAllowed).

アノテーション

下の表は、サポートされているアノテーションをまとめたものです。

Table 1. サポートされているSpring Securityアノテーション
名前 Comments Spring documentation

@Secured

See above

Authorizing Method Invocation with @Secured

@PreAuthorize

詳細については、次のセクションを参照してください

Authorizing Method Invocation with @PreAuthorize

@PreAuthorize

Quarkus は、Spring Security の @PreAuthorize アノテーションで最もよく使用される機能のいくつかをサポートします。サポートされている式は次のとおりです。

hasRole

現在のユーザーが特定のロールを持っているかどうかをテストするには、hasRole 式を @PreAuthorize 内で使用します。

一部の例として、@PreAuthorize("hasRole('admin')")@PreAuthorize("hasRole(@roles.USER)") があります。この roles は、以下のように定義できる Bean になります。

import org.springframework.stereotype.Component;

@Component
public class Roles {

    public final String ADMIN = "admin";
    public final String USER = "user";
}
hasAnyRole

hasRole と同じように、ユーザーは hasAnyRole を使用して、ログインしているユーザーが指定されたロールのいずれかを持っているかどうかを確認できます。

一部の例として、@PreAuthorize("hasAnyRole('admin')")@PreAuthorize("hasAnyRole(@roles.USER, 'view')") があります。

permitAll

メソッドに @PreAuthorize("permitAll()") を追加すると、そのメソッドはどのユーザー(匿名ユーザーを含む)からもアクセスできるようになります。クラスにこれを追加すると、クラスの他のSpring Securityアノテーションが付けられていないすべてのパブリックメソッドに確実にアクセスできるようになります。

denyAll

メソッドに @PreAuthorize("denyAll()") を追加すると、そのメソッドがどのユーザーからもアクセスできないようになります。クラスにこれを追加すると、クラスの他のSpring Securityアノテーションが付けられていないすべてのパブリックメソッドは、どのユーザーからもアクセスできないようになります。

isAnonymous

Bean メソッドに @PreAuthorize("isAnonymous()") のアノテーションを付ける場合、メソッドにアクセスできるのは、現在のユーザーが匿名の場合、つまりログインしていないユーザーの場合のみです。

isAuthenticated

Bean メソッドに @PreAuthorize("isAuthenticated()") でアノテーションを付ける場合、現在のユーザーがログインしているユーザーである場合にのみ、メソッドにアクセスできます。基本的に、このメソッドは匿名ユーザーのみが利用できません。

#paramName == authentication.principal.username

この構文により、ユーザーは、保護されたメソッドのパラメーター (またはパラメーターのフィールド) がログインしたユーザー名と等しいかどうかを確認できます。

このユースケースの例は次のとおりです:

public class Person {

    private final String name;

    public Person(String name) {
        this.name = name;
    }

    // this syntax requires getters for field access
    public String getName() {
        return name;
    }
}

@Component
public class MyComponent {

    @PreAuthorize("#username == authentication.principal.username") (1)
    public void doSomething(String username, String other){

    }

    @PreAuthorize("#person.name == authentication.principal.username") (2)
    public void doSomethingElse(Person person){

    }
}
1 現在ログインしているユーザーが username メソッドパラメーターと同じである場合、doSomething を実行できます
2 現在ログインしているユーザーが person メソッドパラメーターの name フィールドと同じである場合、doSomethingElse を実行できます
authentication. の使用はオプションであるため、principal.username を使用しても同じ結果になります。
#paramName != authentication.principal.username

これは前の式と似ていますが、メソッドパラメーターがログインしているユーザー名とは異なる必要があるという違いがあります。

@beanName.method()

この構文により、開発者は、特定の Bean のメソッドの実行によって、現在のユーザーが保護されたメソッドにアクセスできるかどうかを決定するように指定できます。

構文の説明には、例を使用するとわかりやすいでしょう。MyComponent Bean が以下のように作成されたと仮定しましょう。

@Component
public class MyComponent {

    @PreAuthorize("@personChecker.check(#person, authentication.principal.username)")
    public void doSomething(Person person){

    }
}

doSomething メソッドは、現在のユーザーが doSomething メソッドを呼び出すことを許可されているかどうかを判断するために、personChecker`という名前の Bean のメソッド `check を呼び出す必要があることを示す式を使用して、@PreAuthorize のアノテーションが付けられています。

PersonChecker の例は次のとおりです。

@Component
public class PersonChecker {

    public boolean check(Person person, String username) {
        return person.getName().equals(username);
    }
}

check メソッドの場合、パラメータータイプは @PreAuthorize で指定されたものと一致する必要があり、戻り型は boolean である必要があることに注意してください。

式を組み合わせる

@PreAuthorize アノテーションは、論理 AND / OR を使用した式の組み合わせを可能にします。現在、単一の論理演算しか使用できないという制限があります (つまり、ANDOR を混在させることはできません)。

許可される式の例は次のとおりです。

    @PreAuthorize("hasAnyRole('user', 'admin') AND #user == principal.username")
    public void allowedForUser(String user) {

    }

    @PreAuthorize("hasRole('user') OR hasRole('admin')")
    public void allowedForUserOrAdmin() {

    }

    @PreAuthorize("hasAnyRole('view1', 'view2') OR isAnonymous() OR hasRole('test')")
    public void allowedForAdminOrAnonymous() {

    }

Currently, expressions do not support parentheses for logical operators and are evaluated from left to right

重要な技術的な注意点

Quarkus での Spring サポートは、Spring Application Context を開始せず、Spring インフラストラクチャークラスも実行しないことに注意してください。Spring クラスとアノテーションは、メタデータの読み取りにのみ使用されるか、ユーザーコードメソッドの戻り値の型またはパラメーター型として使用されます。エンドユーザーにとってそれが意味することは、任意の Spring ライブラリーを追加しても効果がないということです。さらに、Spring インフラストラクチャークラス (たとえば、org.springframework.beans.factory.config.BeanPostProcessor など) は実行されません。

変換テーブル

次の表は、Spring SecurityアノテーションをJakarta RESTアノテーションに変換する方法を示しています。

Spring Jakarta REST Comments

@Secured("admin")

@RolesAllowed("admin")

@PreAuthorize

No direct replacement

Quarkus handles complex authorisation differently, see this guide for details

関連コンテンツ