原理解析
一般token的使用整体逻辑是这样的,登陆接口获取一个token 和一个刷新token用的refresh_token,
在接下来的token请求中,所有的接口都需要携带token请求,以便服务端来验证请求的来源是合法的。
这个token是有时效性的,服务端会给他一个时效性,一般设为2小时,在这个时间段内,
这个token是有效的,可以请求成功,而过了这个时间段,这个token就失效了。
这时候需要我们使用refresh_token 请求刷新token的接口来获取新的token 和 refresh_token,
这样就可以源源不断的保持我们持有的token是有效合法的
设计思想
由于token过期这个事情应该是让用户无察觉的,所以我们android端需要偷偷的跑在后台,
换句话说就是,在请求接口的时候,如果返回了TOKEN过期异常之后,我们需要立即再请求刷新接口,
然后再使用新TOKEN 重新请求该接口,那么可以想想,这整个逻辑,如果每个接口都需要这么写,
意味着我们会有很大的工作量,那么这是不科学的,所以我们需要使用一个拦截器,那样就很简单了
接下来我放上我的代码,并根据代码解释下
代码解析
public class TokenInterceptor implements Interceptor {
private static final Charset UTF8 = Charset.forName("UTF-8");
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//步骤一
String token = (String) SPUtils.get(AppContext.getInstance(), SPUtils.TOKEN, "");
if (!TextUtils.isEmpty(token) {
HttpUrl.Builder authorizedUrlBuilder = request.url()
.newBuilder()
.scheme(request.url().scheme())
.host(request.url().host())
.addQueryParameter("access_token", token);
request = request.newBuilder()
.method(request.method(), request.body())
.url(authorizedUrlBuilder.build())
.build();
}
//步骤二
Response originalResponse = chain.proceed(request);
StatusVo func = getResult(originalResponse);
if (Status.TOKEN_Invalid.getCode() == func.getCode()) {
//步骤三
String newToken = getNewToken(loginBean);
if (TextUtils.isEmpty(newToken)) {
return originalResponse;
} else {
//步骤四
HttpUrl.Builder newUrlBuilder = request.url()
.newBuilder()
.scheme(request.url().scheme())
.host(request.url().host())
.removeAllQueryParameters("access_token")
.addQueryParameter("access_token", newToken);
Request newRequest = request.newBuilder()
.method(request.method(), request.body())
.url(newUrlBuilder.build())
.build();
return chain.proceed(newRequest);
}
}
return originalResponse;
}
/**
* 访问该接口,获取到数据,用来判断token
* @param originalResponse
* @return
* @throws IOException
*/
private StatusVo getResult(final Response originalResponse) throws IOException {
ResponseBody responseBody = originalResponse.body();
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE);
Buffer buffer = source.buffer();
Charset charset = UTF8;
MediaType contentType = responseBody.contentType();
if (contentType != null) {
charset = contentType.charset(UTF8);
}
String bodyString = buffer.clone().readString(charset);
Gson gson = new Gson();
//StatusVo 是个公共类,包含所有接口必定返回的code、msg 如果你们后台
//没有统一这么做,也可以直接从bodyString 取出可以判断token过期的数据
StatusVo func = gson.fromJson(bodyString, StatusVo.class);
return func;
}
//刷新token
private synchronized String getNewToken() throws IOException {
//获取刷新token
final String refreToken = (String) SPUtils.get(AppContext.getInstance(), SPUtils.REFRESH_TOKEN, "");;
//请求刷新token 接口获取新token
Call<RefreshTokenBean> objectObservable = Network.createService(NetWorkService.RefreshToken.class)
.refresh(refreToken);
RefreshTokenBean refreshTokenBean = objectObservable.execute().body();
//保存新token
final String newToken = refreshTokenBean.getResult().getData().getAccess_token();
final String newRefreshToken = refreshTokenBean.getResult().getData().getRefresh_token();
SPUtils.put(AppContext.getInstance(), SPUtils.TOKEN, newToken);
SPUtils.put(AppContext.getInstance(), SPUtils.REFRESH_TOKEN, newRefreshToken);
return newToken;
}
}
- 步骤一:获取本地Token,然后以参数的形式放进请求url中,在此处统一添加token,避免每个接口都要传token
- 步骤二:对返回的数据进行预加载,对Token过期进行拦截处理
- 步骤三:刷新Token,并保存
- 步骤四:使用新的token 对Url进行重定向,再次请求
以上步骤就写好了该拦截器,接下来就把拦截器放进OkHttp里面了;
private OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new TokenInterceptor())
.build();
OK,以上就是OkHttp token的使用就可以搞定了