使用 okhttp-signpost 改造 OAuth 签名获取特殊字符的 OAuthMessageSignerException
Retrofit OAuth signing with okhttp-signpost get OAuthMessageSignerException for special character
我正在使用 Retrofit 2
在 Android 应用程序中发出 http 请求。我与之交谈的服务器需要 OAuth 1.0
授权。我使用此处的 okhttp-signpost
来处理 OAuth 签名。
这是我的 build.gradle
库:
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'se.akerfeldt:okhttp-signpost:1.1.0'
compile 'com.squareup.okhttp3:okhttp:3.0.0-RC1'
compile 'oauth.signpost:signpost-core:1.2.1.2'
在MyApi
class中,我定义dailyChart
为Retrofit GET request
:
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
@GET("chart")
Call<ChartResponse> dailyChart(@Query("symbol") String symbol);
我是这样制作的 dailyChart() GET request
:
import se.akerfeldt.okhttp.signpost.OkHttpOAuthConsumer;
import se.akerfeldt.okhttp.signpost.SigningInterceptor;
// for OAuth signing
OkHttpOAuthConsumer consumer = new OkHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new SigningInterceptor(consumer))
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(.....)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
MyApi myApi = retrofit.create(MyApi.class);
String symbol = '^KKKL';
Call<ChartResponse> call = myApi.dailyChart(symbol);
但是由于 ^KKKL
中的 ^
字符,我作为 @Query
参数传递给改装,因此出现以下错误:
D/OkHttp: --> GET https://......chart?symbol=^KKKL http/1.1
D/OkHttp: --> END GET
D/OkHttp: <-- HTTP FAILED: java.io.IOException: Could not sign request
E/.......: java.io.IOException: Could not sign request
at se.akerfeldt.okhttp.signpost.SigningInterceptor.intercept(SigningInterceptor.java:48)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:211)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
at okhttp3.RealCall.execute(RealCall.java:69)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:180)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall.execute(ExecutorCallAdapterFactory.java:91)
.......
Caused by: oauth.signpost.exception.OAuthMessageSignerException: java.net.URISyntaxException: Illegal character in query at index ...: https://.....chart?symbol=^KKKL
at oauth.signpost.signature.SignatureBaseString.generate(SignatureBaseString.java:60)
at oauth.signpost.signature.HmacSha1MessageSigner.sign(HmacSha1MessageSigner.java:51)
at oauth.signpost.AbstractOAuthConsumer.sign(AbstractOAuthConsumer.java:109)
at oauth.signpost.AbstractOAuthConsumer.sign(AbstractOAuthConsumer.java:120)
at se.akerfeldt.okhttp.signpost.SigningInterceptor.intercept(SigningInterceptor.java:46)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:211)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
at okhttp3.RealCall.execute(RealCall.java:69)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:180)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall.execute(ExecutorCallAdapterFactory.java:91)
.......
at oauth.signpost.signature.SignatureBaseString.normalizeRequestUrl(SignatureBaseString.java:65)
at oauth.signpost.signature.SignatureBaseString.generate(SignatureBaseString.java:54)
at oauth.signpost.signature.HmacSha1MessageSigner.sign(HmacSha1MessageSigner.java:51)
at oauth.signpost.AbstractOAuthConsumer.sign(AbstractOAuthConsumer.java:109)
at oauth.signpost.AbstractOAuthConsumer.sign(AbstractOAuthConsumer.java:120)
at se.akerfeldt.okhttp.signpost.SigningInterceptor.intercept(SigningInterceptor.java:46)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:211)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
at okhttp3.RealCall.execute(RealCall.java:69)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:180)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall.execute(ExecutorCallAdapterFactory.java:91)
所以我尝试 url-encode
symbol
,然后再传递给 Retrofit:
String symbol = '^KKKL';
try {
query = java.net.URLEncoder.encode(symbol, "UTF-8");
} catch (UnsupportedEncodingException ex) {
throw new StockHistoryNotFoundException(null, ex);
}
Call<ChartResponse> call = myApi.dailyChart(symbol);
然后我得到另一个错误如下。我认为 Retrofit 再次对我传入的已经编码的 @Query
参数进行编码。
D/OkHttp: <-- 404 Not Found https:.....chart?symbol=%255EKKKL
有人知道解决这个问题的办法吗?
我最终解决了这个问题。在 Retrofit 2
documentation 中,有一个名为 encoded
的可选元素。它的作用是:
encoded
Specifies whether the parameter name and value are already URL
encoded.
在 MyApi
class 中,我将 @Query symbol
更改为使用 encoded=true
:
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
@GET("chart")
Call<ChartResponse> dailyChart(
@Query(value="symbol", encoded=true) String symbol
);
在调用方代码中,在传入 dailyChart
之前对 symbol
进行编码:
String symbol = '^KKKL';
try {
query = java.net.URLEncoder.encode(symbol, "UTF-8");
} catch (UnsupportedEncodingException ex) {
throw new StockHistoryNotFoundException(null, ex);
}
Call<ChartResponse> call = myApi.dailyChart(symbol);
然后 okhttp-signpost
不再抱怨特殊字符问题,& Retrofit 不会对参数进行双重编码。
我正在使用 Retrofit 2
在 Android 应用程序中发出 http 请求。我与之交谈的服务器需要 OAuth 1.0
授权。我使用此处的 okhttp-signpost
来处理 OAuth 签名。
这是我的 build.gradle
库:
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'se.akerfeldt:okhttp-signpost:1.1.0'
compile 'com.squareup.okhttp3:okhttp:3.0.0-RC1'
compile 'oauth.signpost:signpost-core:1.2.1.2'
在MyApi
class中,我定义dailyChart
为Retrofit GET request
:
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
@GET("chart")
Call<ChartResponse> dailyChart(@Query("symbol") String symbol);
我是这样制作的 dailyChart() GET request
:
import se.akerfeldt.okhttp.signpost.OkHttpOAuthConsumer;
import se.akerfeldt.okhttp.signpost.SigningInterceptor;
// for OAuth signing
OkHttpOAuthConsumer consumer = new OkHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new SigningInterceptor(consumer))
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(.....)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
MyApi myApi = retrofit.create(MyApi.class);
String symbol = '^KKKL';
Call<ChartResponse> call = myApi.dailyChart(symbol);
但是由于 ^KKKL
中的 ^
字符,我作为 @Query
参数传递给改装,因此出现以下错误:
D/OkHttp: --> GET https://......chart?symbol=^KKKL http/1.1
D/OkHttp: --> END GET
D/OkHttp: <-- HTTP FAILED: java.io.IOException: Could not sign request
E/.......: java.io.IOException: Could not sign request
at se.akerfeldt.okhttp.signpost.SigningInterceptor.intercept(SigningInterceptor.java:48)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:211)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
at okhttp3.RealCall.execute(RealCall.java:69)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:180)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall.execute(ExecutorCallAdapterFactory.java:91)
.......
Caused by: oauth.signpost.exception.OAuthMessageSignerException: java.net.URISyntaxException: Illegal character in query at index ...: https://.....chart?symbol=^KKKL
at oauth.signpost.signature.SignatureBaseString.generate(SignatureBaseString.java:60)
at oauth.signpost.signature.HmacSha1MessageSigner.sign(HmacSha1MessageSigner.java:51)
at oauth.signpost.AbstractOAuthConsumer.sign(AbstractOAuthConsumer.java:109)
at oauth.signpost.AbstractOAuthConsumer.sign(AbstractOAuthConsumer.java:120)
at se.akerfeldt.okhttp.signpost.SigningInterceptor.intercept(SigningInterceptor.java:46)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:211)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
at okhttp3.RealCall.execute(RealCall.java:69)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:180)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall.execute(ExecutorCallAdapterFactory.java:91)
.......
at oauth.signpost.signature.SignatureBaseString.normalizeRequestUrl(SignatureBaseString.java:65)
at oauth.signpost.signature.SignatureBaseString.generate(SignatureBaseString.java:54)
at oauth.signpost.signature.HmacSha1MessageSigner.sign(HmacSha1MessageSigner.java:51)
at oauth.signpost.AbstractOAuthConsumer.sign(AbstractOAuthConsumer.java:109)
at oauth.signpost.AbstractOAuthConsumer.sign(AbstractOAuthConsumer.java:120)
at se.akerfeldt.okhttp.signpost.SigningInterceptor.intercept(SigningInterceptor.java:46)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:211)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
at okhttp3.RealCall.execute(RealCall.java:69)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:180)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall.execute(ExecutorCallAdapterFactory.java:91)
所以我尝试 url-encode
symbol
,然后再传递给 Retrofit:
String symbol = '^KKKL';
try {
query = java.net.URLEncoder.encode(symbol, "UTF-8");
} catch (UnsupportedEncodingException ex) {
throw new StockHistoryNotFoundException(null, ex);
}
Call<ChartResponse> call = myApi.dailyChart(symbol);
然后我得到另一个错误如下。我认为 Retrofit 再次对我传入的已经编码的 @Query
参数进行编码。
D/OkHttp: <-- 404 Not Found https:.....chart?symbol=%255EKKKL
有人知道解决这个问题的办法吗?
我最终解决了这个问题。在 Retrofit 2
documentation 中,有一个名为 encoded
的可选元素。它的作用是:
encoded
Specifies whether the parameter name and value are already URL encoded.
在 MyApi
class 中,我将 @Query symbol
更改为使用 encoded=true
:
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
@GET("chart")
Call<ChartResponse> dailyChart(
@Query(value="symbol", encoded=true) String symbol
);
在调用方代码中,在传入 dailyChart
之前对 symbol
进行编码:
String symbol = '^KKKL';
try {
query = java.net.URLEncoder.encode(symbol, "UTF-8");
} catch (UnsupportedEncodingException ex) {
throw new StockHistoryNotFoundException(null, ex);
}
Call<ChartResponse> call = myApi.dailyChart(symbol);
然后 okhttp-signpost
不再抱怨特殊字符问题,& Retrofit 不会对参数进行双重编码。