【问题标题】:How to provide credentials for Spring app via OkHttp and Retrofit如何通过 OkHttp 和 Retrofit 为 Spring 应用程序提供凭据
【发布时间】:2021-08-17 09:24:44
【问题描述】:

我开发了具有 webflux 基本身份验证的 spring 非 web 服务。 它的工作,我能够成功地使用 cURL 到达某些端点,如:

curl -I --user user:password http://localhost:5051/service/v1/health
or
curl -I http://user:password@localhost:5051/service/v1/health

但现在我尝试通过其他使用 OkHttp 和 Retrofit 与我的 spring 服务通信的服务发送帖子。

这个过程比较复杂,在主应用中,创建OkHttpCllient然后分离,调用Retrofit服务客户端提供者。

主要应用:

    httpUrl = url
                .newBuilder()
                .username(Username) // first approach
                .password(Password)
                {..}
                .build();
     ClientProvider
                .getInstance(httpUrl, Username, Password)
                .subscribeOn(Schedulers.io())
                .subscribe(new ProfiledSingleObserver<Event>() {
                    @Override
                    public void profiledOnError(@NotNull Throwable e) {
                        LOG.error("Transport layer error", e);
                        resultFuture.completeExceptionally(e);
                    }

            @Override
                    public void profiledOnSuccess(@NotNull Event event) {
                        resultFuture.complete(Collections.singleton(event));
                    }
                });
public class ClientProvider {
    private static HttpUrl httpUrl = null;
    private static Service instance = null;

    private ClientProvider() {
    }

    public static Service getInstance(final HttpUrl url, String username, String password) {
      
                instance = Client.createService(url, HttpClient.getInstance(username, password));
         
        return instance;
    }
}
  public static OkHttpClient getInstance(String username, String password) {
        if (instance == null) {
            synchronized (lock) {
                instance = new OkHttpClient.Builder()

//                        .authenticator(new Authenticator() { // second approach
//                            @Override
//                            public Request authenticate(@Nullable Route route, @NotNull Response response) throws IOException {
//                                return response
//                                        .request().newBuilder()
//                                        .header("Authorization", Credentials.basic(username, password))
//                                        .build();
//                            }
//                        })
//                        .addInterceptor(new BasicAuthInterceptor(username,password)) // third approach
                        .addInterceptor(createHttpBodyLoggingInterceptor())
                        .addInterceptor(createHttpBasicLoggingInterceptor())
                        .writeTimeout(10, TimeUnit.SECONDS)
                        .readTimeout(10, TimeUnit.SECONDS)
                        .connectTimeout(10, TimeUnit.SECONDS)
                        .dispatcher(createDispatcher())
                        .connectionPool(createConnectionPool())
                        .build();
            }
        }
        return instance;
    }
public class BasicAuthInterceptor implements Interceptor {

    private final String credentials;

    public BasicAuthInterceptor(String user, String password) {
        this.credentials = Credentials.basic(user, password);
    }

    @NotNull
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Request authenticatedRequest = request.newBuilder()
                .header(HttpHeaders.AUTHORIZATION, credentials).build();
        return chain.proceed(authenticatedRequest);
    }
}

以及改造服务客户提供商:

 public static Service createService(final HttpUrl baseUrl, final OkHttpClient okHttpClient) {
        return createRetrofit(baseUrl, okHttpClient).create(Service.class);
    }

    protected static Retrofit createRetrofit(HttpUrl baseUrl, OkHttpClient client) {

        return new Retrofit
                .Builder()
                .baseUrl(baseUrl)
                .client(client)
                .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
                .addConverterFactory(JacksonConverterFactory.create())
                .build();
    }

我采用了 3 种不同的方法来解决这个问题,你可以在它们旁边的 cmets 中看到。 首先只是通过 url 传递用户名和密码,看起来像:

 http://user:password@localhost:5051/service/v1/health

但即使使用这种表示法的 curl 工作正常,身份验证也没有通过。

第二种方法是在 OkHttpClient.Builder() 中创建 Authenticator。 结果还是一样。

第三个是在 OkHttpClient 中创建拦截器。 在这种方法中,我能够通过带有存根服务器的单元测试, 这在其他解决方案中是不可能的(状态 404,未找到):

    @BeforeClass
    public static void setUpClass() {
        serviceStubServer = new StubServer();
        whenHttp(serviceStubServer)
                .match(Condition.basicAuth("user", "pass"), Condition.post("/service/v1/health"))
                .then(
                        Action.status(HttpStatus.OK_200),
                );
        serviceStubServer.run();
    }

但我仍然无法通过我的主应用程序将记录发送到我的 Spring 服务(状态 401,未授权)。

我的问题是,通过 OkHttp 和 Retrofit 传递凭据以能够到达 Spring 非 Web、webflux 基本身份验证安全应用程序中的端点的正确方法是什么?

【问题讨论】:

    标签: java spring-boot retrofit2 basic-authentication okhttp


    【解决方案1】:

    似乎问题在于创建 OkHttpClient 的单例方法。该系统使用两个不同的 Spring 服务运行并记住了其中一个凭据,因此他无法正确访问第二个服务。 为了解决这个问题,我创建了第二个 OkHttpClient 类并使用“Interceptor”方法来提供授权数据。

    【讨论】:

      猜你喜欢
      • 2012-01-12
      • 1970-01-01
      • 2012-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多