java 匕首+改造。在运行时添加身份验证标头
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/43051558/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Dagger + Retrofit. Adding auth headers at runtime
提问by AIntel
I'm wondering if there is a way for Dagger to know that it should recreate an object when new data is available.
我想知道 Dagger 是否有办法知道它应该在新数据可用时重新创建一个对象。
The instance I am speaking of is with the request headers I have for retrofit. At some point (when the user logs in) I get a token that I need to add to the headers of retrofit to make authenticated requests. The issue is, I'm left with the same unauthenticated version of retrofit. Here's my injection code:
我所说的实例是我用于改造的请求标头。在某些时候(当用户登录时)我得到一个令牌,我需要将它添加到改造的标头中以发出经过身份验证的请求。问题是,我留下了相同的未经身份验证的改造版本。这是我的注入代码:
@Provides
@Singleton
OkHttpClient provideOkHttpClient(Cache cache) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.cache(cache).build();
client
.newBuilder()
.addInterceptor(
chain -> {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.addHeader("Accept", "Application/JSON");
Request request = requestBuilder.build();
return chain.proceed(request);
}).build();
return client;
}
@Provides
@Singleton
Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxErrorHandlingCallAdapterFactory.create())
.baseUrl(mBaseUrl)
.client(okHttpClient)
.build();
return retrofit;
}
@Provides
@Singleton
public NetworkService providesNetworkService(Retrofit retrofit) {
return retrofit.create(NetworkService.class);
}
Any ideas on how to make this work?
关于如何使这项工作的任何想法?
采纳答案by azizbekian
Please consider using the approachmentioned by @oldergodas it is the "official"and much better way, whereas the approaches mentioned below are notadvised, they may be considered as workarounds.
请考虑使用@oldergod提到的方法,因为它是“官方”且更好的方法,但不建议使用下面提到的方法,它们可能被视为解决方法。
You have a couple of options.
你有几个选择。
- As soon as you get the token, you have to null out the component that provided you the
Retrofit
instance, create a new component and ask for a newRetrofit
instance, which will be instantiated with necessaryokhttp
instance. - A fast and bad one - Save the token in
SharedPreferences
, createokHttp
header, which will apply token reading fromSharedPreferences
. If there is none - send no token header. - Even uglier solution - declare a
static volatile String
field, and do the same thing like in step 2.
- 获得令牌后,您必须立即清空为您提供
Retrofit
实例的组件,创建一个新组件并请求一个新Retrofit
实例,该实例将使用必要的okhttp
实例进行实例化。 - 一个快速而糟糕的方法 - 将令牌保存在 中
SharedPreferences
,创建okHttp
标头,它将应用从SharedPreferences
. 如果没有 - 不发送令牌标头。 - 更丑陋的解决方案 - 声明一个
static volatile String
字段,并执行与步骤 2 中相同的操作。
Why the second option is bad? Because on each request you would be polling SD card and fetch data from there.
为什么第二个选项不好?因为在每个请求中,您都会轮询 SD 卡并从那里获取数据。
回答by oldergod
I personally created an okhttp3.Interceptor
that does that for me, which I update once I have the required token. It looks something like:
我个人创建了一个okhttp3.Interceptor
为我做这件事的人,一旦我拥有所需的令牌,我就会更新它。它看起来像:
@Singleton
public class MyServiceInterceptor implements Interceptor {
private String sessionToken;
@Inject public MyServiceInterceptor() {
}
public void setSessionToken(String sessionToken) {
this.sessionToken = sessionToken;
}
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request.Builder requestBuilder = request.newBuilder();
if (request.header(NO_AUTH_HEADER_KEY) == null) {
// needs credentials
if (sessionToken == null) {
throw new RuntimeException("Session token should be defined for auth apis");
} else {
requestBuilder.addHeader("Cookie", sessionToken);
}
}
return chain.proceed(requestBuilder.build());
}
}
In the corresponding dagger component, I expose this interceptor so I can set the sessionToken
when I need to.
在相应的匕首组件中,我公开了这个拦截器,以便我可以设置sessionToken
需要的时间。
That is some stuff that Jake talked about it his talk Making Retrofit Work For You.
这就是 Jake 在他的演讲让改造为您工作时谈到的一些内容。
回答by Faraz Ahmed
Based on @oldergod solution kotlin version with different classes and structure
基于@oldergod 解决方案 kotlin 版本,具有不同的类和结构
Make Retrofit instance like this
像这样制作 Retrofit 实例
object RetrofitClientInstance {
private var retrofit: Retrofit? = null
private val BASE_URL = "http://yoururl"
val retrofitInstance: Retrofit?
get() {
if (retrofit == null) {
var client = OkHttpClient.Builder()
.addInterceptor(ServiceInterceptor())
//.readTimeout(45,TimeUnit.SECONDS)
//.writeTimeout(45,TimeUnit.SECONDS)
.build()
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return retrofit
}
}
Add ServiceInterceptor
class like below
添加ServiceInterceptor
如下类
class ServiceInterceptor : Interceptor{
var token : String = "";
fun Token(token: String ) {
this.token = token;
}
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
if(request.header("No-Authentication")==null){
//val token = getTokenFromSharedPreference();
//or use Token Function
if(!token.isNullOrEmpty())
{
val finalToken = "Bearer "+token
request = request.newBuilder()
.addHeader("Authorization",finalToken)
.build()
}
}
return chain.proceed(request)
}
}
Login Interface and data class implementation
登录接口和数据类实现
interface Login {
@POST("Login")
@Headers("No-Authentication: true")
fun login(@Body value: LoginModel): Call<LoginResponseModel>
@POST("refreshToken")
fun refreshToken(refreshToken: String):
Call<APIResponse<LoginResponseModel>>
}
data class LoginModel(val Email:String,val Password:String)
data class LoginResponseModel (val token:String,val
refreshToken:String)
call this in any activity like this
在任何这样的活动中调用它
val service = RetrofitClientInstance.retrofitInstance?.create(Login::class.java)
val refreshToken = "yourRefreshToken"
val call = service?.refreshToken(refreshToken)
call?.enqueue(object: Callback<LoginResponseModel>{
override fun onFailure(call: Call<LoginResponseModel>, t: Throwable) {
print("throw Message"+t.message)
Toast.makeText(applicationContext,"Error reading JSON",Toast.LENGTH_LONG).show()
}
override fun onResponse(call: Call<LoginResponseModel>, response: Response<LoginResponseModel>) {
val body = response?.body()
if(body!=null){
//do your work
}
}
})
回答by Naveen Dew
Created custom RequestInterceptor with @Inject constructor
使用 @Inject 构造函数创建自定义 RequestInterceptor
RequestInterceptor
请求拦截器
@Singleton
class
RequestInterceptor @Inject constructor(
private val preferencesHelper: PreferencesHelper,
) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
var newRequest: Request = chain.request()
newRequest = newRequest.newBuilder()
.addHeader(
"AccessToken",
preferencesHelper.getAccessTokenFromPreference()
)
.build()
Log.d(
"OkHttp", String.format(
"--> Sending request %s on %s%n%s",
newRequest.url(),
chain.connection(),
newRequest.headers()
)
);
return chain.proceed(newRequest)
}
ApplicationModule
应用模块
@Module(includes = [AppUtilityModule::class])
class ApplicationModule(private val application: AppController) {
@Provides
@Singleton
fun provideApplicationContext(): Context = application
@Singleton
@Provides
fun provideSharedPreferences(): SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(application.applicationContext)
}
PreferencesHelper
首选项助手
@Singleton
class PreferencesHelper
@Inject constructor(
private val context: Context,
private val sharedPreferences: SharedPreferences
) {
private val PREF_KEY_ACCESS_TOKEN = "PREF_KEY_ACCESS_TOKEN"
fun getAccessTokenFromPreference(): String? {
return sharedPreferences.getString(PREF_KEY_ACCESS_TOKEN, null)
}
}
回答by Ankit Dubey
Well tested and working
经过良好测试和工作
public OkHttpClient getHttpClient(Context context) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
return new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.callTimeout(60,TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.addInterceptor(logging)
.addInterceptor(chain -> {
Request newRequest = chain.request().newBuilder()
.addHeader("Authorization", "Bearer " + Utility.getSharedPreferencesString(context, API.AUTHORIZATION))
.build();
return chain.proceed(newRequest);
})
.build();
}
Earlier I was wondering, if session expires and user login again, will this interceptor replace the existing auth, but fortunately it is working fine.
早些时候我想知道,如果会话过期并且用户再次登录,这个拦截器会取代现有的身份验证,但幸运的是它工作正常。