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

如何使用Bearer令牌通过Retrofit连接加密Web服务?

使用Retrofit携带Bearer令牌访问加密Web服务的正确方式

我正在尝试用Retrofit连接加密的Web服务拉取数据,该服务需要通过Bearer令牌访问,且令牌要定期刷新。我已经成功获取到Bearer令牌,想确认正确的使用方式来连接服务。


现有代码模块

ServiceAPI接口

interface ServiceAPI {

    @FormUrlEncoded
    @POST("GetToken")
    fun getGetToken(
        @Field("grant_type") grant_type: String,
        @Field("username") username: String,
        @Field("password") password: String
    ): Call<Token>

    @GET("api/Users")
    fun getUsers(@Header("Authorization") authorization: String): Call<UsersModel>
}

初始化与令牌获取代码

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    getServiceExample()
}

fun getServiceExample(){
    val BASE_URL = "https://www.exampleserviceid.com/"
    val api = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .build()

    val service = api.create(ServiceAPI::class.java)
        .getGetToken("password","myusername","mypassword")

    service.enqueue(object : Callback<Token>{
        override fun onResponse(call: Call<Token>, response: Response<Token>) {
            if (response.isSuccessful){
                val gettoken: Token? = response.body()
                val token = gettoken?.accessToken  // 通过此方式获取Bearer令牌
            }
        }

        override fun onFailure(call: Call<Token>, t: Throwable) {
            TODO("Not yet implemented")
        }
    })
}

Token实体类

class Token : Serializable {
    @SerializedName("access_token")
    @Expose
    var accessToken: String? = null

    @SerializedName("token_type")
    @Expose
    var tokenType: String? = null

    @SerializedName("expires_in")
    @Expose
    var expiresIn: Int? = null

    constructor() {}

    constructor(accessToken: String?, tokenType: String?, expiresIn: Int?) : super() {
        this.accessToken = accessToken
        this.tokenType = tokenType
        this.expiresIn = expiresIn
    }
}

UsersModel实体类

class UsersModel : Serializable {
    @SerializedName("username")
    @Expose
    var username: String? = null

    @SerializedName("userage")
    @Expose
    var userage: String? = null

    constructor() {}

    constructor(username: String?, userage: String?) : super() {
        this.username= username
        this.userage= userage
    }
}

我尝试的更新代码(待确认)

fun getServiceExample(){
    val BASE_URL = "https://www.exampleserviceid.com/"
    val api = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .build()

    val service = api.create(ServiceAPI::class.java)
        .getGetToken("password","myusername","mypassword")

    service.enqueue(object : Callback<Token>{
        override fun onResponse(call: Call<Token>, response: Response<Token>) {
            if (response.isSuccessful){
                val gettoken: Token? = response.body()
                val token = gettoken?.accessToken  // 通过此方式获取Bearer令牌

                val httpClient: OkHttpClient.Builder = OkHttpClient.Builder()
                    .callTimeout(60, TimeUnit.SECONDS)
                    .connectTimeout(60, TimeUnit.SECONDS)
                    .readTimeout(60, TimeUnit.SECONDS)
                    .writeTimeout(60, TimeUnit.SECONDS)

                httpClient.addInterceptor(object: Interceptor {
                    override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
                        val original = chain.request();

                        val request = original.newBuilder()
                            .header("Authorization","Bearer " + token)
                            .method(original.method(), original.body())
                            .build();

                        val Log = Logger.getLogger(MainFragment::class.java.name)
                        Log.warning("Bearer " + token)

                        return chain.proceed(request);
                    }
                })

                val client = httpClient.build();
                val retrofit = Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .client(client)
                    .build();

                val service2 = retrofit.create(ServiceAPI::class.java).getUsers()

                service2.enqueue(object : Callback<UsersModel>{
                    override fun onResponse(call: Call<UsersModel>, response: Response<UsersModel>) {
                        if (response.isSuccessful){
                            val usersmodeltoken: UsersModel? = response.body()
                            val usernametoken = usersmodeltoken?.username
                            Toast.makeText(context, usernametoken, Toast.LENGTH_SHORT).show()
                        }
                    }

                    override fun onFailure(call: Call<UsersModel>, t: Throwable) {
                        /*
                        TODO("Not yet implemented")
                         */
                    }
                })
            }
        }

        override fun onFailure(call: Call<Token>, t: Throwable) {
            TODO("Not yet implemented")
        }
    })
}

正确实现方案

方式1:直接传入Header(简单场景)

你的ServiceAPI已经定义了getUsers需要Authorization参数,获取令牌后直接拼接Bearer 前缀传入即可,无需重复创建Retrofit实例:

// 在令牌获取成功的回调中
val authHeader = "Bearer ${gettoken?.accessToken}"
val userService = api.create(ServiceAPI::class.java).getUsers(authHeader)
userService.enqueue(object : Callback<UsersModel> {
    override fun onResponse(call: Call<UsersModel>, response: Response<UsersModel>) {
        if (response.isSuccessful) {
            response.body()?.let {
                Toast.makeText(context, it.username, Toast.LENGTH_SHORT).show()
            }
        }
    }

    override fun onFailure(call: Call<UsersModel>, t: Throwable) {
        // 处理请求失败逻辑
    }
})

方式2:全局拦截器注入令牌(推荐,多接口场景)

如果多个接口都需要令牌,全局拦截器更高效,还能统一处理令牌刷新逻辑。注意不要每次请求都创建新的Retrofit/OkHttpClient实例,全局单例更节省资源:

1. 全局配置OkHttpClient

// 传入令牌获取逻辑,方便后续刷新令牌后自动生效
private fun createOkHttpClient(tokenProvider: () -> String?): OkHttpClient {
    return OkHttpClient.Builder()
        .callTimeout(60, TimeUnit.SECONDS)
        .connectTimeout(60, TimeUnit.SECONDS)
        .readTimeout(60, TimeUnit.SECONDS)
        .writeTimeout(60, TimeUnit.SECONDS)
        .addInterceptor { chain ->
            val originalRequest = chain.request()
            val token = tokenProvider()
            val requestBuilder = originalRequest.newBuilder()
            // 自动注入Bearer令牌
            token?.let {
                requestBuilder.header("Authorization", "Bearer $it")
            }
            chain.proceed(requestBuilder.build())
        }
        // 可选:添加日志拦截器,方便调试请求详情
        .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
        .build()
}

2. 全局单例Retrofit与ServiceAPI

// 持久化存储令牌(建议用SharedPreferences或ViewModel)
private var currentToken: String? = null

// 全局单例Retrofit
private val retrofit by lazy {
    val client = createOkHttpClient { currentToken }
    Retrofit.Builder()
        .baseUrl("https://www.exampleserviceid.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .client(client)
        .build()
}

// 全局单例ServiceAPI
private val serviceAPI by lazy { retrofit.create(ServiceAPI::class.java) }

3. 完整使用流程

fun getServiceExample() {
    // 第一步:获取令牌
    serviceAPI.getGetToken("password", "myusername", "mypassword")
        .enqueue(object : Callback<Token> {
            override fun onResponse(call: Call<Token>, response: Response<Token>) {
                if (response.isSuccessful) {
                    currentToken = response.body()?.accessToken
                    // 令牌获取成功后,直接调用用户接口
                    fetchUsers()
                }
            }

            override fun onFailure(call: Call<Token>, t: Throwable) {
                // 处理令牌获取失败逻辑
            }
        })
}

private fun fetchUsers() {
    // 拦截器会自动注入Authorization Header,这里传空字符串即可
    serviceAPI.getUsers("")
        .enqueue(object : Callback<UsersModel> {
            override fun onResponse(call: Call<UsersModel>, response: Response<UsersModel>) {
                if (response.isSuccessful) {
                    response.body()?.let {
                        Toast.makeText(context, it.username, Toast.LENGTH_SHORT).show()
                    }
                } else {
                    // 处理令牌过期:收到401响应时触发刷新逻辑
                    if (response.code() == 401) {
                        refreshTokenAndRetry()
                    }
                }
            }

            override fun onFailure(call: Call<UsersModel>, t: Throwable) {
                // 处理请求失败逻辑
            }
        })
}

// 令牌过期刷新逻辑
private fun refreshTokenAndRetry() {
    // 重新调用getGetToken获取新令牌,更新currentToken后重试请求
    serviceAPI.getGetToken("password", "myusername", "mypassword")
        .enqueue(object : Callback<Token> {
            override fun onResponse(call: Call<Token>, response: Response<Token>) {
                if (response.isSuccessful) {
                    currentToken = response.body()?.accessToken
                    fetchUsers() // 重试用户接口请求
                }
            }

            override fun onFailure(call: Call<Token>, t: Throwable) {
                // 处理刷新令牌失败逻辑
            }
        })
}

关键注意事项

  • 避免重复创建Retrofit/OkHttpClient实例,全局单例能减少资源消耗
  • 令牌要持久化存储(比如SharedPreferences),避免APP重启后重新获取
  • 必须处理令牌过期场景(收到401响应时自动刷新并重试)
  • 可以修改ServiceAPI的getUsers方法,去掉@Header参数,完全依赖拦截器注入,避免冗余代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 08:45:21