So übertragen oder umgehen Sie den Sicherheitskontext, wenn Sie eine protected Quarkus Rest -Methode von einem gleichzeiJava

Java-Forum
Anonymous
 So übertragen oder umgehen Sie den Sicherheitskontext, wenn Sie eine protected Quarkus Rest -Methode von einem gleichzei

Post by Anonymous »

Ich spiele mit Quarkus JWT herum und möchte den Sicherheitskontext auf einen vervollständigbaren Async -Lieferanten übertragen, bei dem Methoden aufgerufen werden, die eine Autorisierung erfordern. Im Frühjahr habe ich dieses Problem nicht, aber mit Quarkus wird der Sicherheitskontext nicht übertragen, und ruft aufgrund von Autorisierungsbedenken auf geschützte Methoden aus einem asynchronen Lieferanten fehl. Hinzufügen, dass GetTreasureCount () und getalibabaSreasureCount () Methode den Fehler umgeht, führt jedoch zu einer nicht autorisierten Ausnahme von 401 (vielleicht ist dies zu erwarten, wenn die sekundären Methoden die Auth -Beschränkungen anwenden, und die Token haben diese Berechtigungen, aber die Token, die von den Takten bezeichnet werden. />
Was ist der beste (-Practice) -Ansatz hier? < /li>
Was passiert eigentlich? Es scheint, dass die Sicherheitsanmerkungen auf alle Aufrufe einer Methode angewendet werden, nicht nur aus dem Resteintrag.

Code: Select all

@RequestScoped
@Path("/api/cave")
public class CaveRestController {

@Inject
private RedisService redis;

@Inject
JsonWebToken accessToken;

@GET
@Produces(MediaType.APPLICATION_JSON)
@Path(value="/authorities")
public Map getPrincipalInfo() {

//accessToken.getClaimNames().forEach(claim -> System.out.printf("Claim: %s, Value: %s%n", claim, accessToken.getClaim(claim)));

Collection authorities = accessToken.getClaimNames();

Map info = new HashMap();
info.put("name", accessToken.getSubject());
info.put("authorities", authorities);

return info;
}

@GET
@Produces(MediaType.APPLICATION_JSON)
@Path(value="/thieves-treasure")
@RolesAllowed("treasure-hunter")
public CompletableFuture getTreasureCount() {
return getTreasure("thieves-treasure", 1000);
}

@GET
@Produces(MediaType.APPLICATION_JSON)
@Path(value="/alibaba-treasure")
@PermissionsAllowed("see:alibaba-treasure")
public CompletableFuture getAliBabasTreasureCount() {
return getTreasure("alibaba-treasure", 0);
}

@POST
@Path(value="/take-treasure")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@PermissionsAllowed("take:thieves-treasure")
@ActivateRequestContext
public CompletableFuture takeTreasure(TreasureModel takeTreasure) {
return CompletableFuture.supplyAsync(() -> {

CompletableFuture thievesCountFuture = getTreasureCount(); // ERROR!!!
CompletableFuture alibabaCountFuture = getAliBabasTreasureCount();

// Wait for both to complete and retrieve results
CompletableFuture combinedFuture = CompletableFuture.allOf(thievesCountFuture, alibabaCountFuture);

// Block until all are done
combinedFuture.join();

TreasureModel thievesTreasure = null;
TreasureModel alibabaTreasure = null;
try {
thievesTreasure = thievesCountFuture.get();
alibabaTreasure = alibabaCountFuture.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
if (thievesTreasure.amount() < takeTreasure.amount()) {
throw new IllegalArgumentException("Not enough treasure to take");
}

alibabaTreasure = new TreasureModel(alibabaTreasure.owner(), alibabaTreasure.amount() + takeTreasure.amount());
thievesTreasure = new TreasureModel(thievesTreasure.owner(), thievesTreasure.amount() - takeTreasure.amount());

redis.set(alibabaTreasure.owner(), alibabaTreasure.amount());
redis.set(thievesTreasure.owner(), thievesTreasure.amount());
Map results = new HashMap();
results.put(alibabaTreasure.owner(), alibabaTreasure.amount());
results.put(thievesTreasure.owner(), thievesTreasure.amount());
return results;
});
}

private CompletableFuture getTreasure(String key, Integer initialValue) {
return CompletableFuture.supplyAsync(() -> {
try {
return redis.get(key)
.thenApply((value) -> {
if (value.isEmpty()) {
redis.set(key, initialValue);
}
return value.orElse(initialValue);
}).thenApply(amount ->  new TreasureModel(key, amount)).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
});
}

}

< /code>
Hier ist der Fehler: < /p>
2025-03-26 14:38:12,445 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (vert.x-eventloop-thread-3) HTTP Request to /api/cave/take-treasure failed, error id: 29f5d35b-e0ee-4b39-b721-a373813eea8a-1

Exception in CaveRestController.java:71
69          return CompletableFuture.supplyAsync(() -> {
70
→ 71              CompletableFuture thievesCountFuture = getTreasureCount();
72              CompletableFuture alibabaCountFuture = getAliBabasTreasureCount();
73  : jakarta.enterprise.context.ContextNotActiveException: RequestScoped context was not active when trying to obtain a bean instance for a client proxy of CLASS bean [class=io.quarkus.vertx.http.runtime.CurrentVertxRequest, id=0_6n6EmChCiiDdd8HelptG_A0AE]
- you can activate the request context for a specific method using the @ActivateRequestContext interceptor binding
Übrigens verwende ich einen Token-Augmentor, um das Auth0-Token zu quarkusfreundlichen Rollen/Berechtigungen zu formen:

Code: Select all


@ApplicationScoped
public class CustomJWTIdentityAugmentor implements SecurityIdentityAugmentor {

@ConfigProperty(name = "app.config.server.auth.auth0.custom-jwt-namespace.roles")
private String customRolesNamespace;
@ConfigProperty(name = "app.config.server.auth.auth0.custom-jwt-namespace.permissions")
private String customPermissionsNamespace;

@Override
public Uni augment(SecurityIdentity identity, AuthenticationRequestContext context) {
//return Uni.createFrom().item(build(identity));
return context.runBlocking(build(identity));
}

private Supplier build(SecurityIdentity identity) {
if(identity.isAnonymous()) {
return () -> identity;
} else {
// create a new builder and copy principal, attributes, credentials and roles from the original identity
QuarkusSecurityIdentity.Builder builder = QuarkusSecurityIdentity.builder(identity);
Object principal = identity.getPrincipal();
if (principal instanceof JsonWebToken accessToken) {

// Extract custom roles and permissions from the JWT claims
Set customRoles = convertClaimToSet(accessToken.getClaim(customRolesNamespace));
Set permissions = convertClaimToSet(accessToken.getClaim(customPermissionsNamespace));

// Add custom roles and permissions.
if (!customRoles.isEmpty()) {
builder.addRoles(customRoles);
}
if (!permissions.isEmpty()) {
permissions.forEach(permission -> System.out.println("Permission: " + permission));
builder.addPermissionsAsString(permissions);
}
}

return builder::build;
}
}

private Set convertClaimToSet(Object claimValue) {
Set result = new HashSet();
if (claimValue instanceof JsonArray jsonArray) {
for (JsonValue jv : jsonArray) {
if (jv.getValueType() == JsonValue.ValueType.STRING) {
result.add(((JsonString) jv).getString());
}
}
} else if (claimValue instanceof Iterable iterable) {
for (Object item : iterable) {
if (item instanceof String) {
result.add((String) item);
}
}
}
return result;
}

}
< /code>
Die Berechtigungen sind vorhanden: < /p>
Permission: take:thieves-treasure
Permission: see:thieves-treasure
Permission: see:alibaba-treasure
< /code>
Zuletzt die Rückgabe von Daten funktioniert: < /p>

@POST
@Path(value="/take-treasure")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@PermissionsAllowed("take:thieves-treasure")
public CompletableFuture  takeTreasure(TreasureModel takeTreasure) {

return CompletableFuture.supplyAsync(() -> {
Map map = new HashMap();
map.put("test1", 980);
map.put("test2", 20);
return map;
});
}
< /code>
> Content-Length: 42
>
} [42 bytes data]
* upload completely sent off: 42 bytes
< HTTP/1.1 200 OK
< content-length: 24
< Content-Type: application/json;charset=UTF-8
<
{ [24 bytes data]
100    66  100    24  100    42   4811   8420 --:--:-- --:--:-- --:--:-- 16500
* Connection #0 to host localhost left intact
{
"test2": 20,
"test1": 980
}

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post