8.5. アプリケーションの作成
まず、ProtectedResource
を実装します。
package org.acme.security.openid.connect.client; import jakarta.annotation.security.RolesAllowed; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import io.quarkus.security.Authenticated; import io.smallrye.mutiny.Uni; import org.eclipse.microprofile.jwt.JsonWebToken; @Path("/protected") @Authenticated public class ProtectedResource { @Inject JsonWebToken principal; @GET @RolesAllowed("user") @Produces("text/plain") @Path("userName") public Uni<String> userName() { return Uni.createFrom().item(principal.getName()); } @GET @RolesAllowed("admin") @Produces("text/plain") @Path("adminName") public Uni<String> adminName() { return Uni.createFrom().item(principal.getName()); } }
ProtectedResource
は、userName()
メソッドと adminName()
メソッドの両方から名前を返します。名前は現在の JsonWebToken
から展開されます。
次に、FrontendResource
が ProtectedResource
を呼び出すために使用する 2 つの REST クライアント OidcClientRequestReactiveFilter
および AccessTokenRequestReactiveFilter
を追加します。
OidcClientRequestReactiveFilter
REST クライアントを追加します。
package org.acme.security.openid.connect.client; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import io.quarkus.oidc.client.reactive.filter.OidcClientRequestReactiveFilter; import io.smallrye.mutiny.Uni; @RegisterRestClient @RegisterProvider(OidcClientRequestReactiveFilter.class) @Path("/") public interface RestClientWithOidcClientFilter { @GET @Produces("text/plain") @Path("userName") Uni<String> getUserName(); @GET @Produces("text/plain") @Path("adminName") Uni<String> getAdminName(); }
RestClientWithOidcClientFilter
インターフェイスは、トークンを取得して伝播するために OidcClientRequestReactiveFilter
に依存します。
AccessTokenRequestReactiveFilter
REST クライアントを追加します。
package org.acme.security.openid.connect.client; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import io.quarkus.oidc.token.propagation.reactive.AccessTokenRequestReactiveFilter; import io.smallrye.mutiny.Uni; @RegisterRestClient @RegisterProvider(AccessTokenRequestReactiveFilter.class) @Path("/") public interface RestClientWithTokenPropagationFilter { @GET @Produces("text/plain") @Path("userName") Uni<String> getUserName(); @GET @Produces("text/plain") @Path("adminName") Uni<String> getAdminName(); }
RestClientWithTokenPropagationFilter
インターフェイスは、受信する既存のトークンを伝播するために AccessTokenRequestReactiveFilter
に依存します。
RestClientWithOidcClientFilter
インターフェイスと RestClientWithTokenPropagationFilter
インターフェイスは、どちらも同じであることに注意してください。これは、同じ REST Client 上で OidcClientRequestReactiveFilter
および AccessTokenRequestReactiveFilter
を組み合わせると、両方のフィルターが相互に干渉する可能性があるため、副作用が発生するためです。たとえば、OidcClientRequestReactiveFilter
は、AccessTokenRequestReactiveFilter
によって伝播されたトークンをオーバーライドできます。また、伝播できるトークンがなく、代わりに OidcClientRequestReactiveFilter
が新しいトークンを取得することが予想されるときにそれが呼び出されると、AccessTokenRequestReactiveFilter
が失敗する可能性があります。
次に、FrontendResource
を追加してアプリケーションの作成を完了します。
package org.acme.security.openid.connect.client; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import org.eclipse.microprofile.rest.client.inject.RestClient; import io.smallrye.mutiny.Uni; @Path("/frontend") public class FrontendResource { @Inject @RestClient RestClientWithOidcClientFilter restClientWithOidcClientFilter; @Inject @RestClient RestClientWithTokenPropagationFilter restClientWithTokenPropagationFilter; @GET @Path("user-name-with-oidc-client-token") @Produces("text/plain") public Uni<String> getUserNameWithOidcClientToken() { return restClientWithOidcClientFilter.getUserName(); } @GET @Path("admin-name-with-oidc-client-token") @Produces("text/plain") public Uni<String> getAdminNameWithOidcClientToken() { return restClientWithOidcClientFilter.getAdminName(); } @GET @Path("user-name-with-propagated-token") @Produces("text/plain") public Uni<String> getUserNameWithPropagatedToken() { return restClientWithTokenPropagationFilter.getUserName(); } @GET @Path("admin-name-with-propagated-token") @Produces("text/plain") public Uni<String> getAdminNameWithPropagatedToken() { return restClientWithTokenPropagationFilter.getAdminName(); } }
FrontendResource
は、/frontend/user-name-with-oidc-client-token
または /frontend/admin-name-with-oidc-client-token
のいずれかが呼び出されたときに、OIDC トークン伝播 Reactive フィルターを備えた REST クライアントを使用して、ProtectedResource
へのアクセストークンを取得して伝播します。また、FrontendResource
は、/frontend/user-name-with-propagated-token
または /frontend/admin-name-with-propagated-token
のいずれかが呼び出されたときに OpenID Connect Token Propagation Reactive Filter
を備えた REST Client を使用して、現在の受信アクセストークンを ProtectedResource
に伝播します。
最後に、Jakarta REST ExceptionMapper
を追加します。
package org.acme.security.openid.connect.client; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.ext.ExceptionMapper; import jakarta.ws.rs.ext.Provider; import org.jboss.resteasy.reactive.ClientWebApplicationException; @Provider public class FrontendExceptionMapper implements ExceptionMapper<ClientWebApplicationException> { @Override public Response toResponse(ClientWebApplicationException t) { return Response.status(t.getResponse().getStatus()).build(); } }
この例外マッパーは、トークンに予期されるロールがない場合に ProtectedResource
が 403
を返すことをテスト中に検証するためにのみ追加されます。このマッパーがなければ、RESTEasy Reactive
は、REST クライアント呼び出しから逃れた例外を 500
に正しく変換し、ProtectedResource
などのダウンストリームリソースからの情報の漏洩を回避します。ただし、テストでは、500
が何らかの内部エラーではなく、認可例外によって発生したと断言することはできません。