ããã¯ããªã«ãããããŠæžãããã®ïŒ
Quarkusã®Extensionã«ãOpenID Connectåãã®ãã®ãã§ããŠãããããªã®ã§ãã¡ãã£ãšè©ŠããŠã¿ããããªãšã
Quarkus - Using OpenID Connect Adapter to Protect JAX-RS Applications
Quarkus 0.27.0ã䜿ããŸãã
Quarkus OpenID Connect Extension
ãã¡ãã®Extensionã§ããã
https://github.com/quarkusio/quarkus/tree/0.27.0/extensions/oidc
ã¬ã€ããåç §ãããšããbearer token authorizationãã§ã¢ããªã±ãŒã·ã§ã³ãä¿è·ãããšãããŸãã
This guide demonstrates how your Quarkus application can use an OpenID Connect Adapter to protect your JAX-RS applications using bearer token authorization, where these tokens are issued by OpenId Connect and OAuth 2.0 compliant Authorization Servers such as Keycloak.
Quarkus - Using OpenID Connect Adapter to Protect JAX-RS Applications
ãã®ã¬ã€ãã«æžãããŠããå
容ãšãè©Šããæãã ãšãäºåã«ã¢ã¯ã»ã¹ããŒã¯ã³ãååŸããŠããã®ããŒã¯ã³ã䜿ã£ãŠã¢ã¯ã»ã¹ããããšãåæã«
ã¢ããªã±ãŒã·ã§ã³ãä¿è·ãããšãã䜿ãæ¹ã®ããã§ãã
ãã®ãããã§ããã
ããã¯ãèªèšŒïŒïŒïŒãšããæ°ã¯ããŸããããã®ãããã¯ãã¡ãã®ã¬ã€ãã«èšèŒãããããã§ãã
Quarkus - Protecting Web Applications Using OpenID Connect
ãŸããKeycloakã®ç®¡çããªã·ãŒã䜿ãããã«ããExtensionããããŸãã
ãããããããã£ãã®ã§ãä»åã¯ãã¹ããããŸã§ãæåã«èšèŒããã¬ã€ãã®ç¯å²ã§è©Šãããšã«ããŸãã
Quarkusã®OpenID Connect Adapterã¯ã以äžã®ã¢ãžã¥ãŒã«ãããŒã¹ã«ãªã£ãŠããããã§ãã
The OAuth2 auth provider - Vert.x
ãŸããQuarkusã®Security Extensionã«ãçµ±åãããŸãã
https://github.com/quarkusio/quarkus/tree/0.27.0/extensions/security
ãŸããCORSã«ã€ããŠã¯ä»åã¯æ±ããŸããããã¬ã€ãã¯ãã¡ãã
ãšãŸããå眮ãã¯ãããããã«ããŠè©ŠããŠã¿ãŸãããã
ç°å¢
ä»åã®ç°å¢ã¯ããã¡ãã§ãã
$ java -version openjdk version "1.8.0_222" OpenJDK Runtime Environment (build 1.8.0_222-8u222-b10-1ubuntu1~18.04.1-b10) OpenJDK 64-Bit Server VM (build 25.222-b10, mixed mode) $ mvn -version Apache Maven 3.6.2 (40f52333136460af0dc0d7232c0dc0bcf0d9e117; 2019-08-28T00:06:16+09:00) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 1.8.0_222, vendor: Private Build, runtime: /usr/lib/jvm/java-8-openjdk-amd64/jre Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "4.15.0-66-generic", arch: "amd64", family: "unix" $ $GRAALVM_HOME/bin/native-image --version GraalVM Version 19.2.1 CE
Keycloakã¯ã7.0.1ã䜿ããŸãã
2019-11-02 10:20:29,839 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: Keycloak 7.0.1 (WildFly Core 9.0.2.Final) started in 20519ms - Started 589 of 884 services (601 services are lazy, passive or on-demand)
Keycloakã¯ã172.17.0.2ã®IPã¢ãã¬ã¹ã§åäœããŠãããã®ãšããŸãã
Keycloakã®ç®¡çãŠãŒã¶ãŒãäœæã
$ bin/add-user-keycloak.sh -u keycloak-admin -p password $ bin/jboss-cli.sh -c --command=reload
ãé¡
Quarkusäžã§ãREST APIãäœæããŸãããäžéšã®APIã¯ã¢ã¯ã»ã¹å¯èœãªããŒã«ãçµã£ãããèªèšŒå¿ é ã«ãããããŸãã
ãŸããAPIã«ãã£ãŠã¯ãèªèšŒãããŠãããŠãŒã¶ãŒã®æ å ±ïŒJWTã¯ã¬ãŒã ã»ã»ããïŒãè¿ãããã«ãããããŸãããã
Keycloakäžã«ã¯ã以äžã®æ å ±ã§RealmããŠãŒã¶ãŒãäœæããŸãã
- Realm ⊠demo-api
- Client-Id ⊠sample-rest-api
- Role ⊠usersãadminsã®2ã€
- User ⊠user001ïŒusers RoleïŒãadmin001ïŒadmins RoleïŒã®2ã€
ãããã®åæã§ãé²ããŠãããŸãã
ãµã³ãã«ã¢ããªã±ãŒã·ã§ã³ã®äœæ
ãŸãã¯ããããžã§ã¯ããäœæããŸããExtensionã«ãoidcããå ããŠããããšããã€ã³ãã§ãã
$ mvn io.quarkus:quarkus-maven-plugin:0.27.0:create \ -DprojectGroupId=org.littlewings \ -DprojectArtifactId=resteasy-oidc \ -Dextensions="oidc, resteasy-jackson" $ cd resteasy-oidc
ãresteasy-jacksonãã¯ãJSONãæ±ãããã§ãããJSON-Bã§ã¯ãªãJacksonã«ããã®ã¯ãªããšãªãã§ãã
ãŸãã¯ãæªèªèšŒç¶æ
ã§ã¢ã¯ã»ã¹ã§ããJAX-RSãªãœãŒã¹ã¯ã©ã¹ãäœæã
src/main/java/org/littlewings/quarkus/oidc/AnonymousResource.java
package org.littlewings.quarkus.oidc; import java.security.Principal; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import io.quarkus.security.identity.SecurityIdentity; @Path("anonymous") public class AnonymousResource { @Inject SecurityIdentity securityIdentity; @GET @Path("greeting") @Produces(MediaType.TEXT_PLAIN) public String hello() { return "Hello World!!"; } @GET @Path("user-info") @Produces(MediaType.APPLICATION_JSON) public Principal userInfo() { System.out.println(securityIdentity.getPrincipal().getClass().getName()); return securityIdentity.getPrincipal(); } }
ãšããããã¡ãã»ãŒãžãè¿ãã ãã®ãªãœãŒã¹ã¡ãœãããšãèªèšŒãããŠããã°ãŠãŒã¶ãŒã®æ å ±ãè¿ãã¡ãœãããçšæããŠã¿ãŸããã
ãŠãŒã¶ãŒã®æ å ±ã«ã¢ã¯ã»ã¹ããããã«ã¯ãSecurityIdentityã䜿ãããã§ãã
@Inject SecurityIdentity securityIdentity; @GET @Path("user-info") @Produces(MediaType.APPLICATION_JSON) public Principal userInfo() { System.out.println(securityIdentity.getPrincipal().getClass().getName()); return securityIdentity.getPrincipal(); }
SecurityIdentity#getPrincipalã§ãçŸåšã®ãŠãŒã¶ãŒæ å ±ã«ã¢ã¯ã»ã¹ããããšãã§ããŸãã
ä»ã«ããçŸåšãå¿åãŠãŒã¶ãŒã§ãããã©ããã ã£ãããããŒã«ãªã©ã®ç¢ºèªãã§ããŸãã
SecurityIdentityãã©ã®ãããªã€ã³ã¿ãŒãã§ãŒã¹ãæã€ãã¯ããã¡ãã§ç¢ºèªãããšããã§ãããã
Quarkusã®Security Extensionã«ããã®ã€ã³ã¿ãŒãã§ãŒã¹ã®å®è£ ããããŸãã
次ã
usersïŒãŸãã¯adminsïŒããŒã«ãæã£ãŠããã°ãã¢ã¯ã»ã¹å¯èœãªJAX-RSãªãœãŒã¹ã¯ã©ã¹ã
src/main/java/org/littlewings/quarkus/oidc/UsersResource.java
package org.littlewings.quarkus.oidc; import java.security.Principal; import javax.annotation.security.RolesAllowed; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import io.quarkus.security.identity.SecurityIdentity; @Path("users") public class UsersResource { @Inject SecurityIdentity securityIdentity; @GET @Path("user-info") @RolesAllowed({"users", "admins"}) @Produces(MediaType.APPLICATION_JSON) public Principal userInfo() { return securityIdentity.getPrincipal(); } }
ä»åã¯ãusersãadminsäž¡æ¹ã¢ã¯ã»ã¹å¯èœã«ããŠããŸãã
@RolesAllowed({"users", "admins"})
@RolesAllowedã¢ãããŒã·ã§ã³ã¯ãJava EEã§å®çŸ©ãããŠãããã®ã§ãããã¯ã©ã¹ãŸãã¯ã¡ãœããã«ä»äžããããšãã§ããŸãã
RolesAllowed (Java(TM) EE 7 Specification APIs)
次ã
adminsããŒã«ã®ã¿ãèš±å¯ãããªãœãŒã¹ã
src/main/java/org/littlewings/quarkus/oidc/AdminsResource.java
package org.littlewings.quarkus.oidc; import java.security.Principal; import javax.annotation.security.RolesAllowed; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import io.quarkus.security.identity.SecurityIdentity; @Path("admins") public class AdminsResource { @Inject SecurityIdentity securityIdentity; @GET @Path("user-info") @RolesAllowed("admins") @Produces(MediaType.APPLICATION_JSON) public Principal userInfo() { return securityIdentity.getPrincipal(); } }
æåŸã«ãèªèšŒãããŠããæã®ã¿ã¢ã¯ã»ã¹å¯èœãªJAX-RSãªãœãŒã¹ã¯ã©ã¹ã
src/main/java/org/littlewings/quarkus/oidc/AuthenticatedResource.java
package org.littlewings.quarkus.oidc; import java.security.Principal; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import io.quarkus.security.Authenticated; import io.quarkus.security.identity.SecurityIdentity; @Path("authenticated") public class AuthenticatedResource { @Inject SecurityIdentity securityIdentity; @GET @Path("user-info") @Authenticated @Produces(MediaType.APPLICATION_JSON) public Principal guardedUserInfo() { return securityIdentity.getPrincipal(); } }
ãã®å Žåã¯ã@Authenticatedã¢ãããŒã·ã§ã³ã䜿çšããŸãã@Authenticatedã¢ãããŒã·ã§ã³ã¯QuarkusãæäŸãããã®ã§ããã¡ãã
ã¯ã©ã¹ããã³ã¡ãœããã«ä»äžããããšãã§ããŸãã
èšå®ã¯ãæäœéã®é
ç®ã«ããŸããã
src/main/resources/application.properties
quarkus.oidc.auth-server-url=http://172.17.0.2:8080/auth/realms/demo-api quarkus.oidc.client-id=sample-rest-api
OpenID Connectã®èªèšŒProviderãžã®ã¢ã¯ã»ã¹URLãšãClient IDãããã°å©çšããããšãã§ããŸãã
èšå®é ç®ã¯ããã¡ãã
Configuring using the application.properties file
ãšãããããã®äœ¿ãæ¹ã ãšClient SecretããèŠããŸããâŠã
ããã2ã€ãèšå®ããªãã£ãå Žåã¯ãã¢ããªã±ãŒã·ã§ã³ãããŸãåäœããªããªããŸãã
auth-server-urlãæªå®çŸ©ã«ããå Žåã
java.lang.RuntimeException: java.net.MalformedURLException: no protocol: /.well-known/openid-configuration at io.vertx.ext.auth.oauth2.impl.OAuth2API.makeRequest(OAuth2API.java:112) at io.vertx.ext.auth.oauth2.providers.OpenIDConnectAuth.discover(OpenIDConnectAuth.java:42) at io.vertx.ext.auth.oauth2.providers.KeycloakAuth.discover(KeycloakAuth.java:120) at io.quarkus.oidc.runtime.OidcRecorder.setup(OidcRecorder.java:49) at io.quarkus.deployment.steps.OidcBuildStep$setup24.deploy_0(OidcBuildStep$setup24.zig:92) at io.quarkus.deployment.steps.OidcBuildStep$setup24.deploy(OidcBuildStep$setup24.zig:36) at io.quarkus.runner.ApplicationImpl1.doStart(ApplicationImpl1.zig:137) at io.quarkus.runtime.Application.start(Application.java:94) at io.quarkus.runner.RuntimeRunner.run(RuntimeRunner.java:143) at io.quarkus.dev.DevModeMain.doStart(DevModeMain.java:176) at io.quarkus.dev.DevModeMain.start(DevModeMain.java:94) at io.quarkus.dev.DevModeMain.main(DevModeMain.java:66) Caused by: java.net.MalformedURLException: no protocol: /.well-known/openid-configuration at java.net.URL.<init>(URL.java:600) at java.net.URL.<init>(URL.java:497) at java.net.URL.<init>(URL.java:446) at io.vertx.ext.auth.oauth2.impl.OAuth2API.makeRequest(OAuth2API.java:93) ... 11 more
client-idãæªå®çŸ©ã«ããå Žåã
Caused by: java.lang.IllegalArgumentException: Configuration missing. You need to specify [clientId] at io.vertx.ext.auth.oauth2.impl.flow.AbstractOAuth2Flow.throwIfNull(AbstractOAuth2Flow.java:48) at io.vertx.ext.auth.oauth2.impl.flow.AuthCodeImpl.<init>(AuthCodeImpl.java:39) at io.vertx.ext.auth.oauth2.impl.OAuth2AuthProviderImpl.<init>(OAuth2AuthProviderImpl.java:68) at io.vertx.ext.auth.oauth2.OAuth2Auth.create(OAuth2Auth.java:132) at io.vertx.ext.auth.oauth2.providers.OpenIDConnectAuth.lambda$discover$1(OpenIDConnectAuth.java:79) at io.vertx.ext.auth.oauth2.impl.OAuth2API.lambda$null$1(OAuth2API.java:129) at io.vertx.core.http.impl.HttpClientResponseImpl$BodyHandler.notifyHandler(HttpClientResponseImpl.java:292) at io.vertx.core.http.impl.HttpClientResponseImpl.lambda$bodyHandler$0(HttpClientResponseImpl.java:193) at io.vertx.core.http.impl.HttpClientResponseImpl.handleEnd(HttpClientResponseImpl.java:248) ... 35 more
確èªãã
ã§ã¯ãåãããŠç¢ºèªããŠã¿ãŸãããã
ããã±ãŒãžã³ã°ããŠèµ·åã
$ mvn package $ java -jar target/resteasy-oidc-1.0-SNAPSHOT-runner.jar
èµ·åãããšãSecurity ExtensionãVert.x Extensionãæå¹ã«ãªã£ãŠããããšãããããŸãã
2019-11-02 20:35:41,365 INFO [io.quarkus] (main) Installed features: [cdi, oidc, resteasy, resteasy-jackson, security, vertx]
ã§ã¯ãããã¥ã¡ã³ãã«æ²¿ã£ãŠè©ŠããŠã¿ãŸãããã
ãŸãã¯ãæªèªèšŒç¶æ ã§ã¢ã¯ã»ã¹ã
$ curl localhost:8080/anonymous/greeting Hello World!!
ãŠãŒã¶ãŒæ å ±ãååŸããããšããŠã¿ãŸãã
$ curl localhost:8080/anonymous/user-info {"name":""}
ç¡åã®ãŠãŒã¶ãŒã¿ããã§ãã
ãã®æã®ãPrincipalã¯ä»¥äžã®ããã§ãã
io.quarkus.security.runtime.AnonymousIdentityProvider$1
ãœãŒã¹ã³ãŒãã¯ããã¡ãã§ããã
SecurityIdentityããããã£ãœãã®ããããŸãã
ããŒã«ã§ã¢ã¯ã»ã¹æš©éãå¶éããããèªèšŒå¿ é ã«ããŠãããªãœãŒã¹ã¡ãœããã«å¯ŸããŠã¯ããã¹ãŠHTTPã¹ããŒã¿ã¹ã³ãŒã401ãè¿ã£ãŠããŸãã
$ curl -i localhost:8080/users/user-info HTTP/1.1 401 Unauthorized Content-Length: 0 $ curl -i localhost:8080/admins/user-info HTTP/1.1 401 Unauthorized Content-Length: 0 $ curl -i localhost:8080/authenticated/user-info HTTP/1.1 401 Unauthorized Content-Length: 0
ã¡ãªã¿ã«ãèªèšŒãµãŒããŒãžãªãã€ã¬ã¯ãããšããããšã«ã¯ãªããªãããã§ãã
â»ãããããããã£ãããç¶ãã®ã¬ã€ãâŠã¿ãããªã®ã§ããâŠ
ã§ã¯ãKeycloakãããã¢ã¯ã»ã¹ããŒã¯ã³ãååŸããŠã¿ãŸãã
ãŸãã¯ãuser001ããã
$ access_token=$( \ curl -X POST http://172.17.0.2:8080/auth/realms/demo-api/protocol/openid-connect/token \ --user sample-rest-api:client-secret \ -H 'content-type: application/x-www-form-urlencoded' \ -d 'username=user001&password=password&grant_type=password' | jq --raw-output '.access_token' \ )
ååŸããã¢ã¯ã»ã¹ããŒã¯ã³ã䜿ã£ãŠããŠãŒã¶ãŒæ å ±ãååŸããŠã¿ãŸããæåã«ãèªèšŒãããŒã«ã®å¶éããããŠããªããªãœãŒã¹ã¡ãœããããã
$ curl localhost:8080/anonymous/user-info -H "Authorization: Bearer "$access_token {"rawToken":null,"claims":{"claimsMap":{"jti":"3026f20d-76be-4676-a5a9-d6e3535da1c8","exp":1572699465,"nbf":0,"iat":1572699165,"iss":"http://172.17.0.2:8080/auth/realms/demo-api","aud":"account","sub":"f4a47f71-c5c3-4364-920e-58b284df1523","typ":"Bearer","azp":"sample-rest-api","auth_time":0,"session_state":"78ebf1fc-247a-4f80-a56f-1c53b681fc2f","acr":"1","allowed-origins":[{"string":"http://localhost:8080","valueType":"STRING","chars":"http://localhost:8080"}],"realm_access":{"roles":[{"string":"offline_access","valueType":"STRING","chars":"offline_access"},{"string":"uma_authorization","valueType":"STRING","chars":"uma_authorization"},{"string":"users","valueType":"STRING","chars":"users"}]},"resource_access":{"account":{"roles":[{"string":"manage-account","valueType":"STRING","chars":"manage-account"},{"string":"manage-account-links","valueType":"STRING","chars":"manage-account-links"},{"string":"view-profile","valueType":"STRING","chars":"view-profile"}]}},"scope":"profile email","email_verified":false,"preferred_username":"user001"},"rawJson":"{\"jti\":\"3026f20d-76be-4676-a5a9-d6e3535da1c8\",\"exp\":1572699465,\"nbf\":0,\"iat\":1572699165,\"iss\":\"http://172.17.0.2:8080/auth/realms/demo-api\",\"aud\":\"account\",\"sub\":\"f4a47f71-c5c3-4364-920e-58b284df1523\",\"typ\":\"Bearer\",\"azp\":\"sample-rest-api\",\"auth_time\":0,\"session_state\":\"78ebf1fc-247a-4f80-a56f-1c53b681fc2f\",\"acr\":\"1\",\"allowed-origins\":[\"http://localhost:8080\"],\"realm_access\":{\"roles\":[\"offline_access\",\"uma_authorization\",\"users\"]},\"resource_access\":{\"account\":{\"roles\":[\"manage-account\",\"manage-account-links\",\"view-profile\"]}},\"scope\":\"profile email\",\"email_verified\":false,\"preferred_username\":\"user001\"}","notBefore":{"value":0,"valueInMillis":0},"issuedAt":{"value":1572699165,"valueInMillis":1572699165000},"jwtId":"3026f20d-76be-4676-a5a9-d6e3535da1c8","expirationTime":{"value":1572699465,"valueInMillis":1572699465000},"issuer":"http://172.17.0.2:8080/auth/realms/demo-api","audience":["account"],"subject":"f4a47f71-c5c3-4364-920e-58b284df1523","claimNames":["jti","exp","nbf","iat","iss","aud","sub","typ","azp","auth_time","session_state","acr","allowed-origins","realm_access","resource_access","scope","email_verified","preferred_username"]},"audience":["account"],"groups":[],"claimNames":["sub","resource_access","email_verified","allowed-origins","raw_token","iss","typ","preferred_username","aud","acr","nbf","realm_access","azp","auth_time","scope","exp","session_state","iat","jti"],"name":"user001","expirationTime":1572699465,"issuer":"http://172.17.0.2:8080/auth/realms/demo-api","subject":"f4a47f71-c5c3-4364-920e-58b284df1523","tokenID":"3026f20d-76be-4676-a5a9-d6e3535da1c8","issuedAtTime":1572699165}
é·ãã®ã§ããŠãŒã¶ãŒåãçµã£ãŠååŸããŠã¿ãŸãããã
$ curl -s localhost:8080/anonymous/user-info -H "Authorization: Bearer "$access_token | jq --raw-output '.name' user001
OKããã§ãã
ãªãããã®æã®Principalã¯ã以äžã®ããã«ãªããŸãã
io.quarkus.oidc.runtime.OidcJwtCallerPrincipal
ãã¡ãã§ããã
usersïŒããã³adminsïŒããŒã«ã§ã¢ã¯ã»ã¹å¶éããããŠãããªãœãŒã¹ã¡ãœããããã³ãèªèšŒã®ã¿å¶éãããããªãœãŒã¹ã¡ãœããã«ã¯
ã¢ã¯ã»ã¹ããããšãã§ããŸãã
$ curl -s localhost:8080/users/user-info -H "Authorization: Bearer "$access_token | jq --raw-output '.name' user001 $ curl -s localhost:8080/authenticated/user-info -H "Authorization: Bearer "$access_token | jq --raw-output '.name' user001
adminsããŒã«ã®ã¿ã¢ã¯ã»ã¹å¯èœãªãªãœãŒã¹ã¡ãœããã«å¯ŸããŠã¯ãHTTPã¹ããŒã¿ã¹ã³ãŒã403ãšãªããŸãã
$ curl -i localhost:8080/admins/user-info -H "Authorization: Bearer "$access_token HTTP/1.1 403 Forbidden content-length: 0
ã§ã¯ãä»åºŠã¯admin001ãŠãŒã¶ãŒã«åãæ¿ããŸãã
$ access_token=$( \ curl -X POST http://172.17.0.2:8080/auth/realms/demo-api/protocol/openid-connect/token \ --user sample-rest-api:client-secret \ -H 'content-type: application/x-www-form-urlencoded' \ -d 'username=admin001&password=password&grant_type=password' | jq --raw-output '.access_token' \ )
ãã®å Žåããããã®ãªãœãŒã¹ã¡ãœããã«ãã¢ã¯ã»ã¹ã§ããããã«ãªããŸãã
$ curl -s localhost:8080/anonymous/user-info -H "Authorization: Bearer "$access_token | jq --raw-output '.name' admin001 $ curl -s localhost:8080/users/user-info -H "Authorization: Bearer "$access_token | jq --raw-output '.name' admin001 $ curl -s localhost:8080/admins/user-info -H "Authorization: Bearer "$access_token | jq --raw-output '.name' admin001 $ curl -s localhost:8080/authenticated/user-info -H "Authorization: Bearer "$access_token | jq --raw-output '.name' admin001
ãã€ãã£ãã€ã¡ãŒãžã§å®è¡ããå Žåã¯ããã¡ãã
$ mvn -P native package $ ./target/resteasy-oidc-1.0-SNAPSHOT-runner
çµæã¯ãå€ãããâŠãšèšããããšããã§ãããå
ã»ã©ã®ã³ãŒãã ãšPrincipalã®JSONã·ãªã¢ã©ã€ãºã«å€±æããã®ã§ãåãªãœãŒã¹ã¯ã©ã¹ã«
ã¡ãœãããè¿œå ããŸããâŠã
Principal#getNameã®çµæã ããè¿ãã¡ãœããã§ã
@Path("anonymous") public class AnonymousResource { /* çç¥ */ @GET @Path("name") @Produces(MediaType.TEXT_PLAIN) public String name() { return securityIdentity.getPrincipal().getName(); } } @Path("users") public class UsersResource { /* çç¥ */ @GET @Path("name") @RolesAllowed({"users", "admins"}) @Produces(MediaType.TEXT_PLAIN) public String name() { return securityIdentity.getPrincipal().getName(); } } @Path("admins") public class AdminsResource { /* çç¥ */ @GET @Path("name") @RolesAllowed("admins") @Produces(MediaType.TEXT_PLAIN) public String name() { return securityIdentity.getPrincipal().getName(); } } @Path("authenticated") public class AuthenticatedResource { /* çç¥ */ @GET @Path("name") @Authenticated @Produces(MediaType.TEXT_PLAIN) public String name() { return securityIdentity.getPrincipal().getName(); } }
ãã¡ãã䜿ã£ãŠã確èªããããã«ããŸããã
æªèªèšŒç¶æ ã
$ curl -i localhost:8080/anonymous/name HTTP/1.1 200 OK Content-Length: 0 Content-Type: text/plain;charset=UTF-8 $ curl -i localhost:8080/users/name HTTP/1.1 401 Unauthorized Content-Length: 0 $ curl -i localhost:8080/admins/name HTTP/1.1 401 Unauthorized Content-Length: 0 $ curl -i localhost:8080/authenticated/name HTTP/1.1 401 Unauthorized Content-Length: 0
user001ã§ã¢ã¯ã»ã¹ã
access_token=$( \ curl -X POST http://172.17.0.2:8080/auth/realms/demo-api/protocol/openid-connect/token \ --user sample-rest-api:client-secret \ -H 'content-type: application/x-www-form-urlencoded' \ -d 'username=user001&password=password&grant_type=password' | jq --raw-output '.access_token' \ ) $ curl localhost:8080/anonymous/name -H "Authorization: Bearer "$access_token user001 $ curl localhost:8080/users/name -H "Authorization: Bearer "$access_token user001 $ curl localhost:8080/authenticated/name -H "Authorization: Bearer "$access_token user001 $ curl -i localhost:8080/admins/name -H "Authorization: Bearer "$access_token HTTP/1.1 403 Forbidden Content-Length: 9 Content-Type: text/plain;charset=UTF-8 Forbidden
admin001ã§ã¢ã¯ã»ã¹ã
access_token=$( \ curl -X POST http://172.17.0.2:8080/auth/realms/demo-api/protocol/openid-connect/token \ --user sample-rest-api:client-secret \ -H 'content-type: application/x-www-form-urlencoded' \ -d 'username=admin001&password=password&grant_type=password' | jq --raw-output '.access_token' \ ) $ curl localhost:8080/anonymous/name -H "Authorization: Bearer "$access_token admin001 $ curl localhost:8080/users/name -H "Authorization: Bearer "$access_token admin001 $ curl localhost:8080/admins/name -H "Authorization: Bearer "$access_token admin001 $ curl localhost:8080/authenticated/name -H "Authorization: Bearer "$access_token admin001
ã¡ãã£ãšãœãŒã¹ã³ãŒããå€ããŠããŸããŸããããèªèšŒã®æç¡ãããŒã«ã«ããåäœãåãããšã¯ç¢ºèªã§ããŸããããšã
ããå°ããã³ãŒããè¿œã
OpenID Connect Extensionãè¿œå ããQuarkusã¯ãèµ·åæã«èªèšŒãµãŒããŒãæ¢ãã«ãããŸãã
ãã®ãããã§ãããåŠçã«äœ¿ãããŠããã¯ã©ã¹åã ãèŠããšãKeycloakâŠïŒ
ãŸãã@RolesAllowedã@Authenticatedã¢ãããŒã·ã§ã³ãä»äžãããšãã€ã³ã¿ãŒã»ãã¿ãŒãèµ·åããŸãã
ããã«ãããŸããã
ä»ã®ã¢ãããŒã·ã§ã³ãšããŠã¯ã@DenyAllã@PermitAllãããããã§ãã
ãŸããã€ã³ã¿ãŒã»ãã¿ãŒå ããåŒã³åºãããããŒã¯ã³ã®æ å ±ã«å¯ŸããŠãèªèšŒã»èªå¯ã«é¢ãã確èªãè¡ã£ãŠããã®ã¯ãã¡ãã®ã¯ã©ã¹ã§ãã
èªåã§äœæããããã°ã©ã å ã§ããããã£ãããšãããããå Žåã¯SecurityIdentityã䜿ã£ãŠifæãªã©ãæžãããããããšã«ãªãã®ã§ãããã
ãŸãšã
Quarkusã®OpenID Connect ExtensionãããªãœãŒã¹ä¿è·ã®ã¿ã§ãããè©ŠããŠã¿ãŸããã
æªèªèšŒç¶æ
ã§ã¢ã¯ã»ã¹ããããKeycloakã®ãã°ã€ã³ããŒãžã«ãªãã€ã¬ã¯ãããããããŠæ¬²ããã®ã§ããããã®ãããã¯ãŸãä»åºŠ
確èªããŠã¿ãããšæããŸãã
ãã®ããããèŠãããšã«ãªããšæããŸãã
Quarkus - Protecting Web Applications Using OpenID Connect
ããä»ã¯å°ãæ©ããã§ãã