Spring Security API の Quarkus エクステンション
セキュリティ認可にはJava標準のアノテーションを使用することが推奨されていますが、Quarkusは、 spring-security エクステンションという形で、Spring Securityの互換性レイヤーを提供しています。
このガイドでは、Quarkusアプリケーションで、よく知られているSpring Securityアノテーションを活用し、ロールを使用してRESTfulサービスの認可を定義する方法について説明します。
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOMEが適切に設定されていること -
Apache Maven 3.9.11
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
-
Spring Web エクステンションにある程度精通している
Maven プロジェクトの作成
まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します:
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-Dパラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=spring-security-quickstart"
このコマンドは、 spring-web、 spring-security、および security-properties-file エクステンションをインポートするプロジェクトを生成します。
すでに Quarkus プロジェクトが設定されている場合は、プロジェクトのベースディレクトリーで以下のコマンドを実行することで、プロジェクトに spring-web、 spring-security および security-properties-file エクステンションを追加することができます。
quarkus extension add spring-web,spring-security,quarkus-elytron-security-properties-file,rest-jackson
./mvnw quarkus:add-extension -Dextensions='spring-web,spring-security,quarkus-elytron-security-properties-file,rest-jackson'
./gradlew addExtension --extensions='spring-web,spring-security,quarkus-elytron-security-properties-file,rest-jackson'
これにより、 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>
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
Quarkus Mavenプラグインは、RESTエンドポイントを定義するためのSpring Webアノテーションを持つコントローラを自動的に生成します(デフォルトで使用されるJakarta RESTの代わりに)。まず、 RESTエンドポイントを定義するSpring Webアノテーションを持つコントローラ src/main/java/org/acme/spring/security/GreetingController.java を次のように作成します:
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
./mvnw quarkus:dev
./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"));
}
}
変更をテストする
手動で
- アクセスが許可されました
-
ブラウザーを http://localhost:8080/greeting で再度開いて、表示されたダイアログに
scottとjb0ssを導入します。helloという単語が表示されます。 - アクセス禁止
-
ブラウザーを http://localhost:8080/greeting で再度開いて、表示されたダイアログを空にします。
結果は次のようになります。
Access to localhost was denied You don't have authorization to view this page. HTTP ERROR 403
|
ブラウザによっては、Basic認証用のクレデンシャルを保存しています。ダイアログが表示されない場合は、保存されたログイン情報を消去するか、プライベートモードを使用してください。 |
サポートされているSpring Securityアノテーション
Quarkusは現在、Spring Securityが提供する機能のサブセットのみをサポートしています。具体的には、Quarkusはロールベースの認可セマンティクスのセキュリティ関連機能をサポートしています( @RolesAllowed の代わりの @Secured を考えてみてください)。
アノテーション
下の表は、サポートされているアノテーションをまとめたものです。
| 名前 | Comments | Springのドキュメント |
|---|---|---|
@Secured |
上記 参照 |
|
@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 のメソッドの実行によって、現在のユーザーが保護されたメソッドにアクセスできるかどうかを決定するように指定できます。
構文の説明には、例を使用するとわかりやすいでしょう。
MyComponentBean が以下のように作成されたと仮定しましょう。@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 を使用した式の組み合わせを可能にします。現在、単一の論理演算しか使用できないという制限があります (つまり、 AND と OR を混在させることはできません)。
許可される式の例は次のとおりです。
@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() {
}
|
現在、式は論理演算子の括弧をサポートしておらず、左から右に評価されます。 |
重要な技術的な注意点
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 |
直接交換不可 |
Quarkusでは、複雑な認可の処理方法が異なります。詳細は こちらのガイド を参照してください。 |
その他のSpringガイド
Quarkusには、より多くのSpring互換機能があります。詳細については、以下のガイドを参照してください。