4.5. Eclipse MicroProfile JWT アプリケーション開発
4.5.1. microprofile-jwt-smallrye
サブシステムの有効化
Eclipse MicroProfile JWT 統合は microprofile-jwt-smallrye
サブシステムによって提供され、デフォルト設定に含まれています。サブシステムがデフォルト設定に存在しない場合は、以下のように追加できます。
前提条件
- EAP XP がインストールされている。
手順
JBoss EAP で MicroProfile JWT smallrye 拡張を有効にします。
/extension=org.wildfly.extension.microprofile.jwt-smallrye:add
microprofile-jwt-smallrye
サブシステムを有効にします。/subsystem=microprofile-jwt-smallrye:add
サーバーをリロードします。
reload
microprofile-jwt-smallrye
サブシステムが有効になります。
4.5.2. JWT アプリケーションを開発するための Maven プロジェクトの設定
必要な依存関係と JWT アプリケーションを開発するためのディレクトリー構造で Maven プロジェクトを作成します。
前提条件
- Maven がインストールされている。
-
microprofile-jwt-smallrye
サブシステムが有効になっている。
手順
Maven プロジェクトを設定します。
$ mvn archetype:generate -DinteractiveMode=false \ -DarchetypeGroupId=org.apache.maven.archetypes \ -DarchetypeArtifactId=maven-archetype-webapp \ -DgroupId=com.example -DartifactId=microprofile-jwt \ -Dversion=1.0.0.Alpha1-SNAPSHOT cd microprofile-jwt
このコマンドは、プロジェクトのディレクトリー構造と
pom.xml
設定ファイルを作成します。POM ファイルが
jboss-eap-xp-microprofile
BOM の Eclipse MicroProfile JWT アーティファクトのバージョンを自動的に管理できるようにするには、POM ファイルの<dependencyManagement>
セクションに BOM をインポートします。<dependencyManagement> <dependencies> <!-- importing the microprofile BOM adds MicroProfile specs --> <dependency> <groupId>org.jboss.bom</groupId> <artifactId>jboss-eap-xp-microprofile</artifactId> <version>${version.microprofile.bom}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
${version.microprofile.bom} を、インストールされた BOM のバージョンに置き換えます。
BOM によって管理される Eclipse MicroProfile JWT アーティファクトをプロジェクト POM ファイルの
<dependency>
セクションに追加します。以下の例は、Eclipse MicroProfile JWT 依存関係をファイルに追加する方法を示しています。<!-- Add the MicroProfile JWT API. Set
provided
for the<scope>
tag, as the API is included in the server. --> <dependency> <groupId>org.eclipse.microprofile.jwt</groupId> <artifactId>microprofile-jwt-auth-api</artifactId> <scope>provided</scope> </dependency>
4.5.3. Eclipse MicroProfile JWT を使用したアプリケーションの作成
JWT トークンに基づいてリクエストを認証し、トークンベアラーのアイデンティティーに基づいて承認を実装するアプリケーションを作成します。
以下の手順では、例としてトークンを生成するコードを提供します。独自のトークンジェネレーターを実装する必要があります。
要件
- Maven プロジェクトが正しい依存関係で設定されている。
手順
トークンジェネレーターを作成します。
この手順は参照用です。実稼働環境の場合は、独自のトークンジェネレーターを実装します。
トークンジェネレーターユーティリティーの
src/test/java
ディレクトリーを作成し、これに移動します。$ mkdir -p src/test/java $ cd src/test/java
以下の内容でクラスファイル
TokenUtil.java
を作成します。package com.example.mpjwt; import java.io.FileInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Base64; import java.util.UUID; import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonObjectBuilder; import com.nimbusds.jose.JOSEObjectType; import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSHeader; import com.nimbusds.jose.JWSObject; import com.nimbusds.jose.JWSSigner; import com.nimbusds.jose.Payload; import com.nimbusds.jose.crypto.RSASSASigner; public class TokenUtil { private static PrivateKey loadPrivateKey(final String fileName) throws Exception { try (InputStream is = new FileInputStream(fileName)) { byte[] contents = new byte[4096]; int length = is.read(contents); String rawKey = new String(contents, 0, length, StandardCharsets.UTF_8) .replaceAll("-----BEGIN (.*)-----", "") .replaceAll("-----END (.*)----", "") .replaceAll("\r\n", "").replaceAll("\n", "").trim(); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(rawKey)); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(keySpec); } } public static String generateJWT(final String principal, final String birthdate, final String...groups) throws Exception { PrivateKey privateKey = loadPrivateKey("private.pem"); JWSSigner signer = new RSASSASigner(privateKey); JsonArrayBuilder groupsBuilder = Json.createArrayBuilder(); for (String group : groups) { groupsBuilder.add(group); } long currentTime = System.currentTimeMillis() / 1000; JsonObjectBuilder claimsBuilder = Json.createObjectBuilder() .add("sub", principal) .add("upn", principal) .add("iss", "quickstart-jwt-issuer") .add("aud", "jwt-audience") .add("groups", groupsBuilder.build()) .add("birthdate", birthdate) .add("jti", UUID.randomUUID().toString()) .add("iat", currentTime) .add("exp", currentTime + 14400); JWSObject jwsObject = new JWSObject(new JWSHeader.Builder(JWSAlgorithm.RS256) .type(new JOSEObjectType("jwt")) .keyID("Test Key").build(), new Payload(claimsBuilder.build().toString())); jwsObject.sign(signer); return jwsObject.serialize(); } public static void main(String[] args) throws Exception { if (args.length < 2) throw new IllegalArgumentException("Usage TokenUtil {principal} {birthdate} {groups}"); String principal = args[0]; String birthdate = args[1]; String[] groups = new String[args.length - 2]; System.arraycopy(args, 2, groups, 0, groups.length); String token = generateJWT(principal, birthdate, groups); String[] parts = token.split("\\."); System.out.println(String.format("\nJWT Header - %s", new String(Base64.getDecoder().decode(parts[0]), StandardCharsets.UTF_8))); System.out.println(String.format("\nJWT Claims - %s", new String(Base64.getDecoder().decode(parts[1]), StandardCharsets.UTF_8))); System.out.println(String.format("\nGenerated JWT Token \n%s\n", token)); } }
以下の内容を含む
src/main/webapp/WEB-INF
ディレクトリーにweb.xml
ファイルを作成します。<context-param> <param-name>resteasy.role.based.security</param-name> <param-value>true</param-value> </context-param> <security-role> <role-name>Subscriber</role-name> </security-role>
以下の内容でクラスファイル
SampleEndPoint.java
を作成します。package com.example.mpjwt; import javax.ws.rs.GET; import javax.ws.rs.Path; import java.security.Principal; import javax.ws.rs.core.Context; import javax.ws.rs.core.SecurityContext; import javax.annotation.security.RolesAllowed; import javax.inject.Inject; import java.time.LocalDate; import java.time.Period; import java.util.Optional; import org.eclipse.microprofile.jwt.Claims; import org.eclipse.microprofile.jwt.Claim; import org.eclipse.microprofile.jwt.JsonWebToken; @Path("/Sample") public class SampleEndPoint { @GET @Path("/helloworld") public String helloworld(@Context SecurityContext securityContext) { Principal principal = securityContext.getUserPrincipal(); String caller = principal == null ? "anonymous" : principal.getName(); return "Hello " + caller; } @Inject JsonWebToken jwt; @GET() @Path("/subscription") @RolesAllowed({"Subscriber"}) public String helloRolesAllowed(@Context SecurityContext ctx) { Principal caller = ctx.getUserPrincipal(); String name = caller == null ? "anonymous" : caller.getName(); boolean hasJWT = jwt.getClaimNames() != null; String helloReply = String.format("hello + %s, hasJWT: %s", name, hasJWT); return helloReply; } @Inject @Claim(standard = Claims.birthdate) Optional<String> birthdate; @GET() @Path("/birthday") @RolesAllowed({ "Subscriber" }) public String birthday() { if (birthdate.isPresent()) { LocalDate birthdate = LocalDate.parse(this.birthdate.get().toString()); LocalDate today = LocalDate.now(); LocalDate next = birthdate.withYear(today.getYear()); if (today.equals(next)) { return "Happy Birthday"; } if (next.isBefore(today)) { next = next.withYear(next.getYear() + 1); } Period wait = today.until(next); return String.format("%d months and %d days until your next birthday.", wait.getMonths(), wait.getDays()); } return "Sorry, we don't know your birthdate."; } }
@Path
アノテーション付きのメソッドは JAX-RS エンドポイントです。@Claim
アノテーションは JWT 要求を定義します。クラスファイル
App.java
を作成して JAX-RS を有効にします。package com.example.mpjwt; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; import org.eclipse.microprofile.auth.LoginConfig; @ApplicationPath("/rest") @LoginConfig(authMethod="MP-JWT", realmName="MP JWT Realm") public class App extends Application {}
アノテーション
@LoginConfig(authMethod="MP-JWT", realmName="MP JWT Realm")
は、デプロイメント中に JWT RBAC を有効にします。以下の Maven コマンドを使用してアプリケーションをコンパイルします。
$ mvn package
トークンジェネレーターユーティリティーを使用して JWT トークンを生成します。
$ mvn exec:java -Dexec.mainClass=org.wildfly.quickstarts.mpjwt.TokenUtil -Dexec.classpathScope=test -Dexec.args="testUser 2017-09-15 Echoer Subscriber"
以下の Maven コマンドを使用してアプリケーションをビルドおよびデプロイします。
$ mvn package wildfly:deploy
アプリケーションをテストします。
ベアラートークンを使用して
Sample/subscription
エンドポイントを呼び出します。$ curl -H "Authorization: Bearer ey..rg" http://localhost:8080/microprofile-jwt/rest/Sample/subscription
Sample/birthday
エンドポイントを呼び出します。$ curl -H "Authorization: Bearer ey..rg" http://localhost:8080/microprofile-jwt/rest/Sample/birthday