如何使用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
相关产品推荐
相关产品推荐

