android - 改造请求在 VPN 后面抛出 UnknownHostException
问题描述
所以我有一个问题,看起来它直接来自暮光之城。
问题
我必须从后端访问 REST API 端点,问题是为了访问该端点,我需要通过 VPN。否则无法访问主机。在桌面上一切正常,我打开 Postman,点击 GET 端点并获得响应。但是,当我尝试通过我的 Android 设备访问相同的端点时,Retrofit 会引发 UnknownHostException。
语境
端点 url 类似于https://api.something.something.net/
. 我正在使用 Dagger 的依赖注入,所以我有一个NetworkModule
看起来像:
...
NetworkModule("https://api.something.something.net/")
...
@Module
class NetworkModule(
private val baseHost: String
) {
...
@Provides
@Named("authInterceptor")
fun providesAuthInterceptor(
@Named("authToken") authToken: String
): Interceptor {
return Interceptor { chain ->
var request = chain.request()
request = request.newBuilder()
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Bearer $authToken")
.build()
val response = chain.proceed(request)
}
}
...
@Provides
@Singleton
fun provideOkHttpClient(
curlInterceptor: CurlInterceptor,
@Named("authInterceptor") authInterceptor: Interceptor
): OkHttpClient {
val builder = OkHttpClient.Builder()
builder.addInterceptor(authInterceptor)
builder.addInterceptor(curlInterceptor)
return builder.build()
}
...
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient, gson: Gson): Retrofit {
return Retrofit.Builder()
.baseUrl(baseHost)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build()
}
}
然后我有一堆存储库,它们是通过 Retrofit 执行请求的存储库:
class MyApiRepositoryImpl(
val myRetrofitApi: MyRetrofitApi,
val uiScheduler: Scheduler,
val backgroundScheduler: Scheduler
) : MyApiRepository {
override fun getSomethingFromTheApi(): Observable<DataResource<List<ApiSomethingResponse>>> {
return myRetrofitApi.getResponseFromEndpoint()
.map {
if (it.isSuccessful) {
DataResource(it.body()?.list!!, ResourceType.NETWORK_SUCCESS)
} else {
throw RuntimeException("Network request failed code ${it.code()}")
}
}
.subscribeOn(backgroundScheduler)
.observeOn(uiScheduler)
.toObservable()
}
}
这是 Retrofit 的 API 接口:
interface MyRetrofitApi {
@GET("/v1/something/")
fun getResponseFromEndpoint(): Single<Response<ApiSomethingResponse>>
}
因此,当我从 Interactor/UseCases 调用此 Repository 方法时,它会直接跳过 onError 并显示 UnknownHostException。
到目前为止我尝试了什么
- 我通过 Volley 和后来的 Ion 切换了 Retrofit,以确保这与其余客户端无关。在所有情况下我都遇到了同样的例外:
java.net.UnknownHostException: Unable to resolve host "api.something.something.net": No address associated with hostname
com.android.volley.NoConnectionError: java.net.UnknownHostException: Unable to resolve host "api.something.something.net": No address associated with hostname
我用 Retrofit 和 OkHttpClient 尝试了所有可能的配置:
在 OkHttpClient 上,我尝试将其设置
followSslRedirects
为 true 和 false。followRedirects
真假。设置hostnameVerifier
为允许任何主机名通过。设置 SSLSocketFactory 以允许任何未签名的证书通过。在我的清单上,我将我设置
android:networkSecurityConfig
为:
https://github.com/commonsguy/cwac-netsecurity/issues/5
我在我的 Android 设备 (Android Nougat)、带有 Nougat、Marshmellow 和 Oreo 的模拟器以及带有 Nougat 的 Genymotion 模拟器上测试了该应用程序。
我尝试点击一个随机的公共端点 (
https://jsonplaceholder.typicode.com/todos/1
),它运行良好。所以这不是互联网连接的问题。我在清单上设置了这三个权限:
android.permission.INTERNET android.permission.ACCESS_NETWORK_STATE android.permission.ACCESS_WIFI_STATE
这非常奇怪,因为我设置了一个拦截器来将所有请求转换为 cURL 请求,我复制并粘贴了相同的请求,该请求在 Postman 中失败并且工作正常。
- 在我的笔记本电脑上,我使用 Cisco AnyConnect,在我的 Android 设备上,我在模拟器和 Genymotion 上使用 Cisco AnyConnect 应用程序和 AFAIK,它应该使用与主机相同的网络。
有几个网站只能通过 VPN 才能看到,我可以在设备和模拟器上看到它们。但是应用程序仍然无法访问端点 URL。
几件奇怪的事
是的,这变得更奇怪了。如果我通过设备或模拟器中的 Chrome 浏览器访问端点,我被重定向到登录页面,那是因为我没有发送授权令牌。现在,如果我通过 chrome://inspect 检查网络响应,我可以获得主机的 IP。现在,如果我通过 IP 更改 NetworkModule 的基本 url,并将这一行添加到授权拦截器:
@Provides
@Named("authInterceptor")
fun providesAuthInterceptor(
@Named("authToken") authToken: String
): Interceptor {
return Interceptor { chain ->
var request = chain.request()
request = request.newBuilder()
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Bearer $authToken")
.build()
val response = chain.proceed(request)
response.newBuilder().code(HttpURLConnection.HTTP_SEE_OTHER).build() // <--- This one
}
}
然后我开始得到:
11-13 13:56:01.084 4867-4867/com.something.myapp D/GetSomethingUseCase: javax.net.ssl.SSLPeerUnverifiedException: Hostname <BASE IP> not verified:
certificate: sha256/someShaString
DN: CN=something.server.net,OU=Title,O=Company Something\, LLC,L=Some City,ST=SomeState,C=SomeCountryCode
subjectAltNames: [someServer1.com, someServer2.com, someServer3.com, someServer4.com, andSoOn.com]
我不确定这是否无关紧要,或者它是否实际上是修复它的一步。
任何提示或建议表示赞赏。
谢谢,
解决方案
推荐阅读
- javascript - 来自 Web 服务器应用程序的 Websocket 连接问题
- java - 重试 repository.save() 时出现 StaleObjectStateException
- dictionary - 在 map 中的每次迭代时将值递增 1
- python - 如何根据某种格式取消堆叠熊猫列?
- mysql - MySQL 错误:索引列大小太大。最大列大小为 767 字节
- python - 使用 python 将列添加到 .csv
- web - Tradingview“无效符号”问题
- .net - .NET 会话在 Chrome 中过期,但不是 IE
- owl - Protege:如何使用数据属性连接两个类
- docker - 使用 kubernetes 操作符重用 Airflow 钩子