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
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
}