在Kotlin+Koin+Retrofit中如何为请求头传入Access/Refresh Token
在Koin+Retrofit环境下给后续API请求添加Token请求头
1. 修正登录相关数据类
原LoginModel仅适用于登录请求体,登录响应包含额外字段,需单独定义响应类:
// 登录请求体(保留原结构) data class LoginModel(val username: String, val password: String) // 匹配接口返回格式的登录响应类 data class LoginResponse( val refreshToken: String, val accessToken: String, val status: String, val user: User ) data class User( val username: String, val email: String, val id: String, val kind: String, val details: UserDetails, val status: String ) data class UserDetails( val firstName: String, val lastName: String, val id: String )
同步更新MeloApi的登录方法返回类型:
interface MeloApi { @Headers("Content-Type: application/json") @POST("/login") suspend fun makeLogin(@Body loginModel: LoginModel): Response<LoginResponse> }
2. 创建Token存储类
实现一个全局可访问的单例类,用于保存和读取Token,通过Koin注入:
class UserSession { var accessToken: String? = null var refreshToken: String? = null }
在Koin模块中注册这个单例:
val appModule = module { single { UserSession() } // 新增Token存储实例 // ... 其他原有代码 }
3. 编写Token拦截器
创建OkHttp拦截器,自动为非登录接口添加Token请求头(假设接口采用Bearer Token格式,可根据实际需求调整):
class TokenInterceptor(private val userSession: UserSession) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val originalRequest = chain.request() // 登录接口无需携带Token,直接放行 if (originalRequest.url.encodedPath == "/login") { return chain.proceed(originalRequest) } // 为其他接口添加Authorization请求头 val modifiedRequest = originalRequest.newBuilder() .addHeader("Authorization", "Bearer ${userSession.accessToken ?: ""}") // 若部分接口需要refreshToken,可按路径单独处理,示例: // if (originalRequest.url.encodedPath == "/refresh-token") { // addHeader("Refresh-Token", userSession.refreshToken ?: "") // } .build() return chain.proceed(modifiedRequest) } }
4. 配置NetworkModule
将Token拦截器添加到OkHttpClient,并通过Koin注入依赖:
val appModule = module { single { UserSession() } // 注册Token拦截器 single { TokenInterceptor(get()) } // 配置OkHttpClient single { val httpInterceptor = HttpLoggingInterceptor() httpInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY) OkHttpClient.Builder() .addInterceptor(get<TokenInterceptor>()) // 添加Token拦截器 .addInterceptor(httpInterceptor) .build() } // 配置Retrofit single { Retrofit.Builder() .client(get<OkHttpClient>()) // 使用配置好的OkHttpClient .addConverterFactory(GsonConverterFactory.create()) .baseUrl(BASE_URL) .build() .create(MeloApi::class.java) } }
5. 登录成功后保存Token
在登录业务逻辑中,将接口返回的Token存入UserSession:
class LoginViewModel(private val meloApi: MeloApi, private val userSession: UserSession) : ViewModel() { suspend fun login(username: String, password: String) { val response = meloApi.makeLogin(LoginModel(username, password)) if (response.isSuccessful) { response.body()?.let { loginResponse -> userSession.accessToken = loginResponse.accessToken userSession.refreshToken = loginResponse.refreshToken // 可选:添加持久化逻辑(如存入SharedPreferences),避免App重启后Token丢失 } } } }
补充:Token持久化(可选但推荐)
若需要App重启后保留Token,可在UserSession中集成SharedPreferences:
class UserSession(private val context: Context) { private val prefs = context.getSharedPreferences("user_session", Context.MODE_PRIVATE) var accessToken: String? get() = prefs.getString("access_token", null) set(value) = prefs.edit().putString("access_token", value).apply() var refreshToken: String? get() = prefs.getString("refresh_token", null) set(value) = prefs.edit().putString("refresh_token", value).apply() }
同步更新Koin模块的注入代码:
single { UserSession(androidContext()) }
内容的提问来源于stack exchange,提问作者Edgar
相关产品推荐
相关产品推荐

