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

在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 23:27:33