Flutter中基于Dio实现Token Refresh
1. 背景介绍
目前项目在采用Flutter开发一款App,该工程中采用Dio框架作为网络请求框架,用户登录方面采用 OAuth2 协议。众所周知, OAuth2 协议中是用户初次登录时获取 access_token,之后当 access_token 过期后采用 refresh_token 再获取新的 access_token 。现在问题的难点是用户在使用过程中如果出现票据过期了,服务就不能正常返回数据,此时需要自动刷新票据,且刷新过程需要对用户透明。
说到这里,有几种解决思路:
- 简单的方式:在用户打开App时计算过期时间,始终保证票据过期前更新票据。
这种方式存在的问题是如果账号在其它终端登录了会使当前终端的票据提前失效(看服务实现,一般来说会限制为一个账号只允许一个终端登录)。
- 正规的方式:用户在App端访问服务时服务端对access_token校验,当票据失效后给出对应状态码提示。 App端对返回状态码解析,根据状态码提示执行刷新票据的请求,当获取到新的票据后再将用户当前访问请求重发。
下面介绍的就是第2种方式,直接刷新Token的方式。
2. Token刷新流程
Token刷新流程如下所示:
- 用户通过App查询服务
- App端调用App服务端接口进行查询
- App服务端调用OAuth服务端接口校验access_token是否失效
- OAuth服务端校验出access_token已经失效,返回错误提示
- App服务端将OAuth服务端错误提示返回给App端
- App端收到票据过期提示后,调用OAuth服务端接口刷新access_token
- OAuth服务端返回新的access_token
- App端将新的access_token保存到本地cache
- App端携带新的access_token并重发查询服务请求到App服务端
- App服务端校验access_token通过后并将服务查询结构返回给App端
- App端将服务查询结构展示给用户
从图中可以看出,关键部分在于第6步刷新Token和第9步重发请求
,下面逐一介绍这2处的实现。
3. 刷新Token
由于不确定票据失效时是哪个请求触发的,所以可以全局拦截请求,对请求的响应信息进行解析判断。 Dio 中有拦截器,可以达到拦截请求的目的。
1. 添加拦截器 TokenInterceptor
Dio添加拦截器
Dio dio = new Dio(); // with default Options
// Set default configs
dio.options.baseUrl = baseURL ?? ApiPath.baseURL;
dio.options.connectTimeout = 100000; // 100s
dio.options.receiveTimeout = 100000; // 100s
dio.interceptors.clear();
// 添加拦截器TokenInterceptor
dio.interceptors.add(TokenInterceptor(dio));