4.5. Eclipse MicroProfile JWT アプリケーション開発

4.5.1. microprofile-jwt-smallrye サブシステムの有効化

Eclipse MicroProfile JWT 統合は microprofile-jwt-smallrye サブシステムによって提供され、デフォルト設定に含まれています。サブシステムがデフォルト設定に存在しない場合は、以下のように追加できます。

前提条件

  • EAP XP がインストールされている。

手順

  1. JBoss EAP で MicroProfile JWT smallrye 拡張を有効にします。

    /extension=org.wildfly.extension.microprofile.jwt-smallrye:add
  2. microprofile-jwt-smallrye サブシステムを有効にします。

    /subsystem=microprofile-jwt-smallrye:add
  3. サーバーをリロードします。

    reload

microprofile-jwt-smallrye サブシステムが有効になります。

4.5.2. JWT アプリケーションを開発するための Maven プロジェクトの設定

必要な依存関係と JWT アプリケーションを開発するためのディレクトリー構造で Maven プロジェクトを作成します。

前提条件

  • Maven がインストールされている。
  • microprofile-jwt-smallrye サブシステムが有効になっている。

手順

  1. 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 設定ファイルを作成します。

  2. 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 のバージョンに置き換えます。

  3. 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 プロジェクトが正しい依存関係で設定されている。

手順

  1. トークンジェネレーターを作成します。

    この手順は参照用です。実稼働環境の場合は、独自のトークンジェネレーターを実装します。

    1. トークンジェネレーターユーティリティーの src/test/java ディレクトリーを作成し、これに移動します。

      $ mkdir -p src/test/java
      $ cd src/test/java
    2. 以下の内容でクラスファイル 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));
          }
      }
  2. 以下の内容を含む 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>
  3. 以下の内容でクラスファイル 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 要求を定義します。

  4. クラスファイル 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 を有効にします。

  5. 以下の Maven コマンドを使用してアプリケーションをコンパイルします。

    $ mvn package
  6. トークンジェネレーターユーティリティーを使用して JWT トークンを生成します。

    $ mvn exec:java -Dexec.mainClass=org.wildfly.quickstarts.mpjwt.TokenUtil -Dexec.classpathScope=test -Dexec.args="testUser 2017-09-15 Echoer Subscriber"
  7. 以下の Maven コマンドを使用してアプリケーションをビルドおよびデプロイします。

    $ mvn package wildfly:deploy
  8. アプリケーションをテストします。

    • ベアラートークンを使用して 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