4.5. MicroProfile JWT 应用程序开发
4.5.1. 启用 microprofile-jwt-smallrye 子系统 复制链接链接已复制到粘贴板!
MicroProfile JWT 集成由 microprofile-jwt-smallrye 子系统提供,包含在默认配置中。如果默认配置中没有子系统,您可以添加它,如下所示:
先决条件
- EAP XP 已安装。
流程
在 JBoss EAP 中启用 MicroProfile JWT 小扩展:
/extension=org.wildfly.extension.microprofile.jwt-smallrye:add启用
microprofile-jwt-smallrye子系统:/subsystem=microprofile-jwt-smallrye:add重新载入服务器:
reload
microprofile-jwt-smallrye 子系统已启用。
4.5.2. 配置 Maven 项目以开发 JWT 应用程序 复制链接链接已复制到粘贴板!
创建一个具有所需依赖项的 Maven 项目,以及用于开发 JWT 应用的目录结构。
先决条件
- 已安装 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-microprofileBOM 中 MicroProfile JWT 构件的版本,请将 BOM 导入到项目 POM 文件的 <dependencyManagement> 部分。<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 管理的 MicroProfile JWT 构件添加到项目 POM 文件的 &
lt;dependency> 部分。以下示例演示了将 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. 使用 MicroProfile JWT 创建应用 复制链接链接已复制到粘贴板!
创建一个应用,以基于 JWT 令牌验证请求,并根据令牌 bearer 的身份实施授权。
以下流程提供了生成令牌的示例代码。对于生产环境,请使用红帽单点登录(SSO)等身份提供程序。
先决条件
- Maven 项目配置有正确的依赖项。
流程
创建令牌生成器。
此步骤充当参考。对于生产环境,请使用 Red Hat SSO 等身份提供程序。
为 token the generator 实用程序创建一个目录
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注解的方法是 Jakarta RESTful Web Services 端点。注释
@Claim定义 JWT 声明。创建类文件
App.java以启用 Jakarta RESTful Web Services: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测试应用。
使用 bearer 令牌调用
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