一、前言
在上一篇博文webView系列(九)—-登录验证中,我们将到了登录验证,验证token失效后,就会到登陆页面重新登陆,这样用户觉得登陆的比较麻烦,想达到登陆一次,就很久都不需要登陆了,只要一直使用APP就不会,几天不使用都不会退出登录。为达到这样的效果,我们将讲解自动登录。
二、自动登录
/**
* api>=21调用
* @param view
* @param request
* @return
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, final WebResourceRequest request) {
if (request != null && request.getUrl() != null && request.getMethod().equalsIgnoreCase("get")) {
String scheme = request.getUrl().getScheme().trim();
if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")) {
if (request.getUrl().toString().contains(".html")) {
try {
URL url = new URL(InjectParamsUtil.verrificationToken(request.getUrl().toString()));
URLConnection connection = url.openConnection();
if (connection.getInputStream().toString().length() < 120) {
String value = IOUtils.toString(connection.getInputStream(), "UTF-8");
if (value.startsWith("{")) {
HttpResult httpResult = JSONObject.parseObject(value, HttpResult.class);
if (httpResult != null) {
if (Constants.LOGIN_OUT_CODE.equals(httpResult.getCode())) {
HttpResult refreshHttpResult = RefreshTokenSynchronizationRequest.synchronizationRefreshToken();
if (refreshHttpResult != null && Constants.SUCCESS_CODE.equals(refreshHttpResult.getCode())) {
runOnUiThread(new Runnable() {
@Override
public void run() {
String newUrl = InjectParamsUtil.injectIsParams(request.getUrl().toString());
LogUtil.e("newUrl-->" + newUrl);
//刷新
isRefresh = true;
mWebView.loadUrl(newUrl);
}
});
} else {
setResult(LOGIN_OUT_RESULT);
finish();
}
}
}
}
} else {
connection.getInputStream().close();
}
return null;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
return null;
}
/**
* api<21调用
* @param view
* @param url
* @return
*/
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
if (url.contains(".html")) {
try {
URL url1 = new URL(InjectParamsUtil.verrificationToken(url));
URLConnection connection = url1.openConnection();
if (connection.getInputStream().toString().length() < 120) {
String value = IOUtils.toString(connection.getInputStream(), "UTF-8");
if (value.startsWith("{")) {
HttpResult httpResult = JSONObject.parseObject(value, HttpResult.class);
if (httpResult != null) {
if (Constants.LOGIN_OUT_CODE.equals(httpResult.getCode())) {
HttpResult refreshHttpResult = RefreshTokenSynchronizationRequest.synchronizationRefreshToken();
if (refreshHttpResult != null && Constants.SUCCESS_CODE.equals(refreshHttpResult.getCode())) {
runOnUiThread(new Runnable() {
@Override
public void run() {
String newUrl = InjectParamsUtil.injectIsParams(url);
LogUtil.e("newUrl-->" + newUrl);
//刷新
isRefresh = true;
mWebView.loadUrl(newUrl);
}
});
} else {
setResult(LOGIN_OUT_RESULT);
finish();
}
}
}
}
} else {
connection.getInputStream().close();
}
return null;
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
看了上面的代码,是不是觉得很熟悉,没错,这个就是登陆验证的代码,只不过有了一点点的不同而已。
三、自动登录流程
- 登录验证
- token失效,刷新token
- 刷新token成功,替换掉token,进行重新加载;刷新token失败,则退出登录,让用户自己重新登录。
从上面的流程来看,流程很清晰明了,注意的就是在刷新token的那一块。
四、刷新token
- 刷新token我们这里就不能使用异步请求了,这里要使用同步请求
4.1、RefreshTokenSynchronizationRequest类
/**
* 同步请求刷新token
*
* @author Freak
* @date 2019/6/15.
*/
public class RefreshTokenSynchronizationRequest {
private static ApiServer apiService = HttpMethods.getInstance().create(ApiServer.class);
public static synchronized HttpResult synchronizationRefreshToken() throws IOException {
final Call<ResponseBody> call = apiService.synchronizationRefreshToken(
(String) SPUtils.get(App.getInstance().getApplicationContext(), Constants.REFRESH_TOKEN, ""));
// call对象执行同步请求
String result = RefreshTokenResponse.getResponseBody(call.execute().body());
HttpResult refreshHttpResult = JSONObject.parseObject(result, HttpResult.class);
LogUtil.e("result-->" + refreshHttpResult);
if (refreshHttpResult != null) {
if (Constants.SUCCESS_CODE.equals(refreshHttpResult.getCode())) {
//登陆信息的保存
LoginEntity loginEntity = JSONObject.parseObject(new Gson().toJson(refreshHttpResult.getData()), LoginEntity.class);
// SPUtils.saveLoginEntity(App.getInstance().getApplicationContext(), loginEntity);
}
}
return refreshHttpResult;
}
}
我们都知道response.body().string(),只能读取一次,这里我们需要做一下出来,把response的数据clone了一份,这样就不会只能读取一次了。这里是为了复用接口请求(后面再讲,具体的可以查看okhttp系列的博文)
五、response.body().string()只能读取一次问题解决
public class RefreshTokenResponse {
public static String getResponseBody(ResponseBody responseBody) throws IOException {
BufferedSource source = responseBody.source();
// 获取全部body的数据
source.request(Long.MAX_VALUE);
Buffer buffer = source.buffer();
// 在读取缓存去之前clone数据,解决response.body().string()只能读取一次的问题
String responseBodyString = buffer.clone().readString(Charset.forName("UTF-8"));
return responseBodyString;
}
}
六、刷新token机制介绍
一般的情况下,我们做的接口可能就只会有一个token的字段,需要刷新token的话,就要多一个字段去刷新token,这里才有的是OAuth 2登陆授权。具体的OAuth 2了解可到博文OAuth 2 深入介绍中去了解,这里就不做介绍了。
七、注意事项
自动登录并不是说登录一次就不再需要登陆了,这个登陆的失效与刷新token的那个字段refreshToken的过期时长有关系。
举个栗子:
在你登陆成功之后,后台会给你返回access_token、refresh_token、expires_in(token过期时长)等字段,我们在access_token过期之后,我们会拿refresh_token去进行access_token的刷新,更新新的token和更新token的过期时长。但是在用户很久没有使用APP的时候,此时,refresh_token的有效时长也到期了,这时候,你拿refresh_token去刷新token,就会返回刷新失败,这时候,就需要用户去重新登录了。
所以,自动登录并不是能达到永久登录,登录时长跟后台设置的access_token和refresh_token有效时长有关。