Skip to content

Commit

Permalink
增加主动填充
Browse files Browse the repository at this point in the history
  • Loading branch information
andych008 committed Dec 17, 2020
1 parent 802af12 commit bc811e2
Show file tree
Hide file tree
Showing 17 changed files with 487 additions and 278 deletions.
65 changes: 20 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
## 引言
我们经常接入一些第三方服务,一般使用相应服务的前提都是通过app_id,app_key**获取access _token**然后每个请求带着access _token来执行
## SmartToken
SmartToken通过使用okhttp的拦截器,自动**获取access _token**并填充到请求的指定位置。获取、填充过程对使用者无感知

通常,我们的做法可能是先判断有没有access _token,并且在有效期内,然后执行请求。但是,请求一定能成功吗?很有可能还会提示**access _token过期或无效**(比如:客户端时间修改。或者“单点登录”导致本机token失效)。所以,我们有可能会在请求的返回结果中判断是否有token失效的错误,然后重新请求token,重新执行业务请求。


**有两种方式:**


## 有没有更好的方案呢?
有,本文要讲的就是**被动获取token**的方案。先执行业务请求,如果返回结果是token失效,那么重新获取token,并重新执行业务请求。

1. 前置条件:依赖okhttp
2. 技术点:使用okhttp的Intercepter。
3. 优点:对业务请求透明。业务功能使用者不需要关心access _token的事情。

注:因为是“被动获取token”,所以本方案相对而言,更适用于token大概率不会过期的服务。在我们实际开发中,多数都是这样。比如本例中用到的baidu ai抠图,token过期时间为一个月。对于token时间很短,在接口访问周期内大概率会过期的情况,我们也可以巧妙地利用okhttp的Intercepter来实现无感知地判断token是否过期。

1. 主动填充

> 判断token是否过期,如果过期,更新主求中的token
2. 被动填充

> 先执行业务请示,如果返回报文是token过期,那么更新token并重新执行业务请求。


## 使用
Expand All @@ -24,54 +20,32 @@
```
allprojects {
repositories {
maven { url 'https://dwvip.github.io/repo' }
maven { url 'https://github.com/dwvip/repo/raw/master' }
}
}
implementation 'wang.unclecat.smarttoken:SmartToken:1.0'
implementation 'wang.unclecat.smarttoken:SmartToken:1.1'
```
1. 继承`AbsAccessTokenInterceptor`,并实现相应的抽像方法(指定token的位置、token对应的key、从服务端获取token的实现)。将之加入okhttp拦截器。
1. 主动填充继承`ActiveSmartToken`,被动填充继承`PassiveSmartToken`,并实现相应的抽像方法(指定token的位置、token对应的key、获取token的具体实现)。将之加入okhttp拦截器。
**支持的token类型**
```java
public static final int TOKEN_IN_QUERY = 1;//token 在query
public static final int TOKEN_IN_HEADER = 2;//token 在header
public static final int TOKEN_IN_QUERY_AND_HEADER = 3;//token 在query和header同时存在
int TOKEN_IN_QUERY = 1;//token 在query
int TOKEN_IN_HEADER = 2;//token 在header
int TOKEN_IN_QUERY_AND_HEADER = 3;//token 在query和header同时存在
//本库不支持把access_token放在body中,因为太另类。
```
**需要实现的抽像方法**
```java
/**
* 解析http resp body,判断是否是“access token无效”错误
*
* @param respBody http 返回实体字符串
* @return true: 是, false: 否
*/
protected abstract boolean checkTokenError(String respBody);
1. 更详细的使用方式,请参考demo中`BaiduAccessToken1.java`和`BaiduAccessToken2.java`
/**
* access token在query或header里对应的key
*/
protected abstract String tokenKey();
/**
* 同步获取access token
*/
protected abstract String getAccessTokenFromServer() throws IOException;
```
详请参考demo中`BaiduAccessAccessTokenInterceptor.java`
## demo截图:
<img src="./raw/Screenshot_2020-12-15-15-24-27-288_wang.unclecat.smarttoken.jpg" width = "600" />
<img src="./raw/Screenshot.jpg" width = "600" />
Expand All @@ -82,3 +56,4 @@
```
2 changes: 1 addition & 1 deletion SmartToken/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ android {
minSdkVersion 19
targetSdkVersion rootProject.targetSdkVersion
versionCode 1
versionName "1.0"
versionName "1.1"

consumerProguardFiles "consumer-rules.pro"
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package wang.unclecat.smarttoken;

import android.util.Log;

import java.io.IOException;

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

/**
* 拦截器实现access token自动获取(主动判断是否过期)
*
* @author: 喵叔catuncle
* @date: 2020/12/16 17:32
*/
public abstract class ActiveSmartToken extends SmartToken implements Interceptor {

public ActiveSmartToken(@Type int tokenType) {
super(tokenType);
}

@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
request.url().toString();
Log.d("SmartToken", "url(处理前) = " + request.url());
Log.d("SmartToken", "headers(处理前) = " + request.headers());

String localToken = getAccessTokenFromLocal();
String token = isOutOfDate(localToken) ? getAccessTokenFromServer() : localToken;
Request updatedRequest = updateToken(request, token);
Log.d("SmartToken", "url(处理后) = " + updatedRequest.url());
Log.d("SmartToken", "headers(处理后) = " + updatedRequest.headers());

return chain.proceed(updatedRequest);
}

/**
* 判断access token是否已过期
*
* @param localToken 本地的token(可能为空)
* @return true:过期, false:未过期
*/
protected abstract boolean isOutOfDate(String localToken);

/**
* 从本地同步获取access token
*/
protected abstract String getAccessTokenFromLocal() throws IOException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package wang.unclecat.smarttoken;

import android.util.Log;

import java.io.IOException;

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;

/**
* 拦截器实现access token自动获取(通过判断结果错误码,被动获取)
*
* @author: 喵叔catuncle
* @date: 2020/12/11 10:12
*/
public abstract class PassiveSmartToken extends SmartToken implements Interceptor {

public PassiveSmartToken(@Type int tokenType) {
super(tokenType);
}

@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response originResponse = chain.proceed(request);
if (checkTokenError(originResponse)) {
//同获取AccessToken
String token = getAccessTokenFromServer();

//更新AccessToken
Request updatedRequest = updateToken(request, token);

return chain.proceed(updatedRequest);
} else {
return originResponse;
}
}

private boolean checkTokenError(Response response) {
ResponseBody responseBody = response.body();
BufferedSource source = responseBody.source();

try {
source.request(Long.MAX_VALUE); // Buffer the entire body.
Buffer buffer = source.buffer().clone();
String s = buffer.readUtf8();
return checkTokenError(s);
} catch (Exception e) {
Log.e("SmartToken", e.getMessage(), e);
}

return false;
}

/**
* 解析http resp body,判断是否是“access token无效”错误
*
* @param respBody http 返回实体字符串
* @return true: 是, false: 否
*/
protected abstract boolean checkTokenError(String respBody);

}
Loading

0 comments on commit bc811e2

Please sign in to comment.