【发布时间】: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