You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hilt场景下为Retrofit注入用户凭据实现BasicAuth认证的问题咨询

解决方案

问题根因

你当前的写法不符合Hilt的依赖注入生命周期规则:

  • 你将OkHttpClient、Retrofit、ApiService都声明为@Singleton,绑定到全局的SingletonComponent,这些实例会在App启动阶段就完成初始化,此时用户还未输入用户名密码,自然无法为provideRetrofitClient提供所需的两个String参数,因此编译器会抛出注入失败的错误。
  • 全局单例的ApiService也无法在每次登录时动态替换底层Client,需要换动态传递凭据的实现思路。

推荐实现:可动态更新凭据的拦截器方案

该方案不需要重建Retrofit实例,开销极低,支持随时更新用户凭据,适配绝大多数业务场景。

第一步:自定义支持动态设置凭据的Basic认证拦截器

import okhttp3.Credentials
import okhttp3.Interceptor
import okhttp3.Response

class BasicAuthInterceptor : Interceptor {
    // 用volatile保证多线程操作的可见性
    @Volatile
    var username: String? = null
    @Volatile
    var password: String? = null

    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        val requestBuilder = if (!username.isNullOrEmpty() && !password.isNullOrEmpty()) {
            // 调用okhttp提供的工具类生成标准Basic认证头
            val authCredential = Credentials.basic(username!!, password!!)
            originalRequest.newBuilder()
                .header("Authorization", authCredential)
        } else {
            originalRequest.newBuilder()
        }
        return chain.proceed(requestBuilder.build())
    }
}

第二步:修改NetworkModule配置

注意补全你原来代码中缺失的@Module注解:

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

    @Singleton
    @Provides
    fun provideGsonBuilder(): Gson = GsonBuilder().setLenient().create()

    // 提供自定义拦截器的全局单例
    @Singleton
    @Provides
    fun provideBasicAuthInterceptor(): BasicAuthInterceptor = BasicAuthInterceptor()

    @Singleton
    @Provides
    fun provideRetrofitClient(authInterceptor: BasicAuthInterceptor): OkHttpClient {
        return OkHttpClient.Builder()
            .addInterceptor(authInterceptor)
            .readTimeout(60, TimeUnit.SECONDS)
            .connectTimeout(60, TimeUnit.SECONDS)
            .build()
    }

    @Singleton
    @Provides
    fun provideRetrofitBuilder(gsonBuilder: Gson, client: OkHttpClient): Retrofit.Builder {
        return Retrofit.Builder()
            .baseUrl(Constants.BASE_URL)
            .addConverterFactory(GsonConverterFactory.create(gsonBuilder))
            .client(client)
    }

    @Singleton
    @Provides
    fun provideApiService(retrofitBuilder: Retrofit.Builder): ApiService {
        return retrofitBuilder
            .build()
            .create(ApiService::class.java)
    }
}

第三步:修改ViewModel,登录前更新拦截器凭据

@HiltViewModel
class AuthenticationViewModel
@Inject
constructor(
    private val apiService: ApiService,
    // 注入自定义拦截器实例
    private val authInterceptor: BasicAuthInterceptor
) : ViewModel() {

    fun performLogin(username:String,password:String){
        viewModelScope.launch {
            // 先更新拦截器的凭据,后续所有请求都会自动携带认证头
            authInterceptor.username = username
            authInterceptor.password = password
            // 直接调用登录接口即可
            apiService.login()
        }
    }
}

注意事项

  • 退出登录时记得将authInterceptor的username和password置空,避免后续请求携带无效认证头
  • 如果业务需要严格区分认证/非认证接口,可通过@Qualifier注解提供两套OkHttpClient/ApiService实例,分别用于认证和非认证场景

内容的提问来源于stack exchange,提问作者Michael m

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 23:45:03