Retrofit2 Token过期自动刷新

Posted by アライさん on 2019年10月22日
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import android.util.Base64
import com.testapp.BuildConfig
import com.testapp.MyAppcation
import com.testapp.utils.HMACSHA1
import com.testapp.utils.Utils
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import org.json.JSONObject
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.nio.charset.Charset
import java.util.concurrent.TimeUnit

/*
* 单例联网客户端
* */
object NetService {
//token错误时的code
const val TokenErrorCode = 401

val service by lazy {
getService(ApiInterface::class.java)
}

//解析code,如果为token错误,请求token,然后重新请求原接口
private val tokenInterceptor by lazy {
object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)

val responseBody = response.body
val source = responseBody?.source()
source?.request(Long.MAX_VALUE)
val buffer = source?.buffer
val contentType = responseBody?.contentType()
var charset: Charset = java.nio.charset.StandardCharsets.UTF_8
if (contentType != null) {
charset = contentType.charset(java.nio.charset.StandardCharsets.UTF_8)
?: java.nio.charset.StandardCharsets.UTF_8
}

val bodyString = buffer?.clone()?.readString(charset)


var code = 0
try {
code = JSONObject(bodyString).getInt("code")
} catch (e: Exception) {

}

if (code == TokenErrorCode) {
//token错误,重新请求
val time = Utils.getNetTimestamp(System.currentTimeMillis())
val appId = MyAppcation.instance.appId
val data = "appId${appId}timestamp$time"
val appKey = MyAppcation.instance.appKey
val sign =
Base64.encodeToString(
HMACSHA1.calculateRFC2104HMAC(data, appKey),
Base64.NO_WRAP
)

val token = service.getToken(
appId = appId,
appKey = appKey,
timestamp = time,
sign = sign
).execute().body()?.data?.token?.value ?: ""

if (token.isEmpty()) {
throw java.lang.Exception("refresh token fail")
}
MyAppcation.instance.token = token
val retryRequest = chain.request()
.newBuilder()
.removeHeader("token")
.addHeader("token", token)
.build()
return chain.proceed(retryRequest)
}

return response
}
}
}


private val loggingInterceptor by lazy {
HttpLoggingInterceptor()
}

/* 创建默认的api client */
private fun <S> getService(serviceClass: Class<S>): S {
return Retrofit.Builder()
.baseUrl("http://dev.cloud.zhibankeji.com/")
.addConverterFactory(GsonConverterFactory.create())
.client(createHttpClient())
.build()
.create(serviceClass)
}

/* 创建api联网客户端 */
private fun createHttpClient(): OkHttpClient {
//控制网络 log开关
if (BuildConfig.DEBUG) {
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
} else {
loggingInterceptor.level = HttpLoggingInterceptor.Level.NONE
}

return OkHttpClient.Builder()
.addInterceptor(object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder()
.addHeader("token", MyAppcation.instance.token)
.addHeader("appId", MyAppcation.instance.appId)
.addHeader("deviceType", MyAppcation.instance.deviceType)
.addHeader("deviceId", MyAppcation.instance.deviceId)
.addHeader("deviceDetail", MyAppcation.instance.deviceDetail)
.build()
return chain.proceed(request)
}
})
.readTimeout(30, TimeUnit.SECONDS)
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.addInterceptor(tokenInterceptor)
.build()
}
}