簡體   English   中英

我如何使用 Quarkus Microprofile Rest 客戶端動態傳遞承載令牌?

[英]How do i pass a Bearer Token dynamically with Quarkus Microprofile Rest Client?

我正在嘗試調用 Keycloak Admin REST 端點以在我的代碼中創建一個 keycloak 用戶,它需要請求將 Bearer 令牌添加到授權 header。我需要一種方法將令牌動態傳遞給 rest 客戶端。

我必須調用另一個服務來生成令牌。

這是我嘗試過的以及我遇到的錯誤。 有什么問題或者有更好的方法來做到這一點?

SignUpService.java

@ApplicationScoped
public class SignUpService {
    private static final Logger LOG = Logger.getLogger(SignUpService.class);

    @Inject
    @RestClient
    KeycloakClientAdmin keycloakClientAdmin;

    public Response create(KeycloakUserDTO keyCloakUser) {
    ...
    try {
            keycloakClientAdmin.createKeycloakUser(keyCloakUser);
        } catch (Exception e) {
            LOG.info("Unable to create new user");
            e.printStackTrace();
        }
    }
    return Response.ok().build();
}

KeycloakClientAdmin.java

@Path("/")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@RegisterRestClient(configKey = "config.api.keycloak.admin")
@RegisterClientHeaders(KeycloakClientHeader.class)
public interface KeycloakClientAdmin {
    
    @POST
    @Path("/admin/realms/{realm}/users")
    public void createKeycloakUser(keycloakUserDTO user);
}

KeycloakClient.java

@Path("/")
@Produces(MediaType.APPLICATION_JSON)
@RegisterRestClient(configKey = "config.api.keycloak.token")
public interface KeycloakClient {
    

    @POST
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    @Path("/realms/master/protocol/openid-connect/token")
    public TokenRepresentation createToken(MultivaluedMap<String, String> formMap);
}

KeycloakClientHeader.java

令牌已在此處成功創建,我能夠記錄它,但是從錯誤(狀態代碼 401)我得到它看起來好像沒有添加到 header。堆棧跟蹤在最后。

@ApplicationScoped
public class KeycloakClientHeader implements ClientHeadersFactory{
    private static final Logger LOG = Logger.getLogger(KeycloakClientHeader.class);

    @Inject
    @RestClient
    KeycloakClient keycloakClient;

    @Override
    public MultivaluedMap<String, String> update(
                          MultivaluedMap<String, String> mm1,MultivaluedMap<String, String> mm2) {
        MultivaluedMap<String, String> result = new MultivaluedMapImpl<>();
        MultivaluedMap<String, String> tokenRequest = new MultivaluedMapImpl<>();

        tokenRequest.add("client_id", "admin-cli");
        tokenRequest.add("username", admin);
        tokenRequest.add("password", password);
        tokenRequest.add("grant_type", "password");
        
        String token = null;   
        try {
            token = keycloakClient.createToken(tokenRequest).getAccess_token();
            LOG.info("Token: " + token);
        } catch (Exception e) {
            LOG.info("Unable to create token");
            e.printStackTrace();
        }
        result.add("Authorization", "Bearer  " + token);
        return result;
    }
}

application.properties 文件

config.api.keycloak.token/mp-rest/url=http://localhost:9000
config.api.keycloak.token/mp-rest/scope=javax.inject.Singleton

config.api.keycloak.admin/mp-rest/url=http://localhost:9000
config.api.keycloak.admin/mp-rest/scope=javax.inject.Singleton

錯誤:

Unable to create new user
--
org.jboss.resteasy.client.exception.ResteasyWebApplicationException: Unknown error, status code 401
--
        at org.jboss.resteasy.client.exception.WebApplicationExceptionWrapper.wrap(WebApplicationExceptionWrapper.java:107)
--
        at org.jboss.resteasy.microprofile.client.DefaultResponseExceptionMapper.toThrowable(DefaultResponseExceptionMapper.java:21)
--
        at org.jboss.resteasy.microprofile.client.ExceptionMapping$HandlerException.mapException(ExceptionMapping.java:41)
--
        at org.jboss.resteasy.microprofile.client.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:153)
--
        at com.sun.proxy.$Proxy157.createKeycloakUser(Unknown Source)
--
        at farm.everyfarmer.mobile.useraccount.service.SignUpService.create(SignUpService.java:94)
--
        at farm.everyfarmer.mobile.useraccount.service.SignUpService_Subclass.create$$superforward1(SignUpService_Subclass.zig:137)
--
        at farm.everyfarmer.mobile.useraccount.service.SignUpService_Subclass$$function$$2.apply(SignUpService_Subclass$$function$$2.zig:33)
--
        at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:54)
--
        at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.proceed(InvocationInterceptor.java:62)
--
        at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.monitor(InvocationInterceptor.java:49
--
        at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.intercept(InvocationInterceptor_Bean.zig:521)
--
        at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
--
        at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
--
        at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
--
        at farm.everyfarmer.mobile.useraccount.service.SignUpService_Subclass.create(SignUpService_Subclass.zig:293)
--
        at farm.everyfarmer.mobile.useraccount.service.SignUpService_ClientProxy.create(SignUpService_ClientProxy.zig:157)
--
        at farm.everyfarmer.mobile.useraccount.resource.SignUpResource.createAcccount(SignUpResource.java:45)
--
        at farm.everyfarmer.mobile.useraccount.resource.SignUpResource_Subclass.createAcccount$$superforward1(SignUpResource_Subclass.zig:94)
--
        at farm.everyfarmer.mobile.useraccount.resource.SignUpResource_Subclass$$function$$1.apply(SignUpResource_Subclass$$function$$1.zig:33)
--
        at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:54)
--
        at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.proceed(InvocationInterceptor.java:62)
--
        at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.monitor(InvocationInterceptor.java:49)
--
        at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.intercept(InvocationInterceptor_Bean.zig:521)
--
        at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
--
        at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
--
        at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
--
        at farm.everyfarmer.mobile.useraccount.resource.SignUpResource_Subclass.createAcccount(SignUpResource_Subclass.zig:158)
--
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
--
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
--
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
--
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
--
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:170)
--
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)
--
        at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:660)
--
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:524)
--
        at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:474)
--
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
--
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:476)
--
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:434)
--
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:408)
--
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:69)
--
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:492)
--
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)
--
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)
--
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
--
        at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)
--
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
--
        at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
--
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:138)
--
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:93)
--
        at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:536)
--
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
--
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
--
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
--
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
--
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
--
        at java.base/java.lang.Thread.run(Thread.java:829)

或者,您可以只將keycloak-admin-client模塊依賴項添加到您的項目中,您不必擔心管理您自己所做的大部分事情。 它是為你做的。

您的錯誤是有雙倍空格,而不是承載中的 1:

 HttpHeaders.AUTHORIZATION, "Bearer " + tokens.getAccessToken());

對比

 result.add("Authorization", "Bearer  " + token); <-- Has double space

修復: result.add("Authorization", "Bearer " + token);

規范很明確,應該是 1 Single Space:


2.1。 授權請求 Header 字段

在 HTTP/1.1 [RFC2617] 定義的“授權”請求 header 字段中發送訪問令牌時,客戶端使用“Bearer”
身份驗證方案來傳輸訪問令牌。

例如:

 GET /resource HTTP/1.1 Host: server.example.com Authorization: Bearer mF_9.B5f-4.1JqM

此方案的“授權”header 字段的語法
遵循第 2 節中定義的基本方案的用法
[RFC2617]。 請注意,與 Basic 一樣,它不符合
[RFC2617] 第 1.2 節中定義的通用語法,但兼容
為 HTTP 1.1 [HTTP-AUTH] 開發的通用身份驗證框架,盡管它不遵循其中概述的首選實踐以反映現有部署。 Bearer 憑證的語法如下:

 **b64token    = 1*( ALPHA / DIGIT /
                   "-" / "." / "_" / "~" / "+" / "/" ) *"="
 credentials = "Bearer" 1*SP b64token**

https://datatracker.ietf.org/doc/html/rfc6750#section-2.1

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM