如何将Keycloak与Payara Micro集成?实现无状态REST JAX-RS认证授权
Great question! Combining Eclipse MicroProfile JWT Authentication with Java EE’s security annotations like @RolesAllowed for a stateless JAX-RS app backed by Keycloak is totally doable—and it’s a clean, standards-compliant approach for stateless auth. Let me walk you through the step-by-step implementation:
First, make sure you have these in place:
- A running Keycloak server (any recent version works)
- A Java EE/MicroProfile compatible runtime (WildFly, Payara, or Quarkus are solid choices)
- A basic JAX-RS application set up (with
javax.ws.rsdependencies)
Start by setting up Keycloak to issue valid JWTs your app can verify:
- Create a Realm: Log into Keycloak, create a new realm for your application.
- Create a Client: Add a client with these settings:
- Set
Access Typetobearer-only(since this is a stateless backend, no browser redirects needed) - Enable
Service Accounts Enabledif you’ll use client credentials flow
- Set
- Map Roles to JWT Claims: Go to your client’s
Mapperstab, add aGroup Membershipmapper:- Set
Token Claim Nametogroups(this is the default claim MicroProfile JWT looks for) - Check
Full group pathif you want to include realm roles
- Set
- Extract the Public Key: From your realm’s
Keystab, copy the public key (make sure it includes the-----BEGIN PUBLIC KEY-----and-----END PUBLIC KEY-----wrappers) and save it aspublicKey.pemin your app’ssrc/main/resources/META-INF/folder.
Add the MicroProfile JWT API dependency to your pom.xml (Maven):
<dependency> <groupId>org.eclipse.microprofile.jwt</groupId> <artifactId>microprofile-jwt-auth-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency>
For runtime-specific implementations (like WildFly), you don’t need extra dependencies—they include the MP JWT implementation out of the box.
Create a microprofile-config.properties file in src/main/resources/META-INF/ to tell your app how to validate JWTs:
# Path to Keycloak's public key mp.jwt.verify.publickey.location=META-INF/publicKey.pem # Keycloak realm issuer URL (replace with your server/realm) mp.jwt.verify.issuer=https://your-keycloak-server/auth/realms/your-realm-name # Your Keycloak client ID mp.jwt.verify.audience=your-client-id
Then, apply the @LoginConfig annotation to your JAX-RS application class to enable MP-JWT authentication:
import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; import org.eclipse.microprofile.auth.LoginConfig; @ApplicationPath("/api") @LoginConfig(authMethod = "MP-JWT") public class MyJaxRsApp extends Application { // Leave empty, or register resources/providers here if needed }
Now you can use standard Java EE security annotations to protect your JAX-RS endpoints:
import javax.annotation.security.RolesAllowed; import javax.annotation.security.PermitAll; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.core.Response; @Path("/resources") public class ProtectedResource { // Only accessible to users with the "admin" role @GET @Path("/admin") @RolesAllowed("admin") public Response getAdminContent() { return Response.ok("Admin-only data").build(); } // Accessible to any authenticated user with the "user" role @GET @Path("/user") @RolesAllowed("user") public Response getUserContent() { return Response.ok("User-level data").build(); } // Accessible to everyone, no auth required @GET @Path("/public") @PermitAll public Response getPublicContent() { return Response.ok("Public data").build(); } }
Here’s the end-to-end stateless auth flow:
- A client (like a frontend or another service) requests a JWT from Keycloak using a valid grant (password, client credentials, etc.)
- The client sends the JWT in the
Authorizationheader asBearer <your-access-token>with every request to your JAX-RS app - Your runtime’s MP-JWT implementation validates the token:
- Checks the signature against Keycloak’s public key
- Verifies the issuer, audience, expiration, and other claims
- If valid, it extracts roles from the
groupsclaim (or custom claim you configure) and maps them to the caller’s security principal - The container enforces
@RolesAllowedchecks based on the principal’s roles—no server-side session is stored, keeping the app stateless
If you want Keycloak to use a different claim name for roles (instead of groups), update your microprofile-config.properties:
mp.jwt.roles.claim=roles
Then adjust your Keycloak client mapper to use roles as the token claim name.
Use curl to test your endpoints:
- Get an access token from Keycloak:
curl -X POST https://your-keycloak-server/auth/realms/your-realm/protocol/openid-connect/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "username=test-user" \ -d "password=test-pass" \ -d "grant_type=password" \ -d "client_id=your-client-id" \ -d "client_secret=your-client-secret"
- Use the token to access a protected endpoint:
curl -H "Authorization: Bearer <your-access-token>" https://your-app-server/api/resources/admin
内容的提问来源于stack exchange,提问作者Thomás Sousa Silva

