前言:服务器
以前分享过在Android中使用Retrofit实现token失效刷新的处理方案,如今Flutter项目也有“token验证过时”的需求。刚开始我使用的是EventBus来通知弹出登陆页面,可是发如今refresh token过时后并无去登陆,缘由是EventBus须要在有生命周期的State状态中才能发送消息,在这里我构造了全局的上下文,以便弹出登陆页面。因此接下来我简单总结一下在Flutter项目中如何实现自动刷新token并重发请求的拦截器功能,但愿对你们有所帮助。网络
需求:异步
1.有两个token, 分别为access_token和refresh_Token, access_token的有效期为1小时,refresh_Token的有效期为7天;async
2.若是access_token1个小时后过时了,服务器会返回401,此时客户端要根据刷新access_token的retrofit接口去从新请求新的access_token;ide
3.若是refresh_Token7天后也过时了,则要求跳到登陆页面。工具
思路: gradle
1.Dio实现网络请求ui
2.自定义token拦截器,实现token自动刷新并重发请求url
3.RefreshToken过时,弹出登陆页面。调试
实现的步骤:
1.配置Android目录的gradle依赖
dependencies { ... implementation 'de.greenrobot:eventbus:3.0.0-beta1' }
2.在pubspec.yaml添加sdk
dependencies: ... cupertino_icons: ^0.1.0 dio: ^2.1.7
3.封装一个获取新的accessToken方法
Future<String> getToken() async { String refreshToken = DataUtil.getRefreshToken; //获取当前的refreshToken String accessToken; Dio tokenDio = new Dio(); //建立新Dio实例 tokenDio.options.headers['refresh-token'] = refreshToken;//设置当前的refreshToken try { String url = url; //refreshToken url var response = await tokenDio.get(url); //请求refreshToken刷新的接口 accessToken = response.data['access_token']; //新的accessToken refreshToken = response.data['refresh_token'];//新的refreshToken DataUtil.saveRefreshToken(refreshToken); //保存新的refreshToken } on DioError catch (e) { if(e.response.statusCode==401){ //401表明refresh_token过时 //refreshToken过时,弹出登陆页面 //解决方法一:封装一个全局的context //return Navigator.of(Util.context).push(new MaterialPageRoute( builder: (ctx) => new LoginPage())); //解决方法二:当refresh token过时,把退出的登陆的操做放在dio网络请求的工具类去操做 } } return accessToken; }
4.token失效时,异步获取accessToken
onError(DioError error) async { if (error.response != null && error.response.statusCode == 401) { 401表明access token过时 Dio dio = DioUtil().dio;//获取Dio单例 dio.lock(); //加锁 String accessToken = await getToken(); //异步获取新的accessToken ... dio.unlock(); //解锁 } super.onError(error); }
5.从新发起一个请求获取数据
//从新发起一个请求获取数据 Dio tokenDio2 = new Dio(); //建立新的dio实例 tokenDio2.options.headers['access-token'] = accessToken; try { var newRequest = await tokenDio2.request(request.path); return newRequest; } on DioError catch (e) { return e; }
6.Dio拦截器的完整代码
typedef void ChildContext(BuildContext context); class TokenInterceptor extends Interceptor { ChildContext context; //上下文 @override onError(DioError error) async { if (error.response != null && error.response.statusCode == 401) { //401表明token过时 Dio dio = DioUtil().dio;//获取Dio单例 dio.lock(); //加锁 String accessToken = await getToken(); //异步获取新的accessToken //从新发起一个请求获取数据 Dio tokenDio2 = new Dio();//建立新的Dio实例 tokenDio2.options.headers['access-token'] = accessToken; try { var newRequest = await tokenDio2.request(request.path); return newRequest; } on DioError catch (e) { return e; } dio.unlock(); //解锁 } super.onError(error); } Future<String> getToken() async { String refreshToken = DataUtil.getRefreshToken; //获取当前的refreshToken String accessToken ; Dio tokenDio =new Dio(); //建立新的Dio实例 tokenDio.options.headers['refresh-token'] = refreshToken ;//设置当前的refreshToken try { String url = url; //refreshToken url var response = await tokenDio.get(url); //请求refreshToken刷新的接口 accessToken = response.data['access_token']; //新的accessToken refreshToken = response.data['refresh_token'];//新的refreshToken DataUtil.saveAccessToken(accessToken); //保存新的accessToken DataUtil.saveRefreshToken(refreshToken); //保存新的refreshToken } on DioError catch (e) { if(e.response.statusCode==401){//401表明refresh_token过时 //refreshToken过时,弹出登陆页面 //解决方法一:封装一个全局的context //return Navigator.of(Util.context).push(new MaterialPageRoute( builder: (ctx) => new LoginPage())); //解决方法二:当refresh token过时,把退出的登陆的操做放在dio网络请求的工具类去操做 } } return accessToken; } }
7.添加自定义的token拦截器
/* *Dio网络请求的工具类 */ class DioUtil { Dio dio; static DioUtil _instance; static DioUtil getInstance() { if (_instance == null) { _instance = DioUtil(); } return _instance; } //get方法 Future<Response> get(url, {data, options, cancelToken}) async { String accessToken = DataUtil.getAccessToken; //获取当前的accessToken options = BaseOptions( connectTimeout: 15000, headers: { Constants.accessToken: accessToken }, ); dio = new Dio(options); //添加自定义的token拦截器 dio.interceptors.add(new TokenInterceptor()); Response response; try { response = await dio.get(url, cancelToken: cancelToken); } on DioError catch (e) { print(e.response.data); } return response; } }
8.总结
在Flutter项目中自定义一个自动刷新并重发请求的Dio拦截器,通过不断调试,最终实现了功能。若是有什么疑问的话,欢迎留言联系我哦!