https://github.com/flutterchina/dio/blob/master/README-ZH.md#%E6%8B%A6%E6%88%AA%E5%99%A8
前言:
之前分享过在Android中使用Retrofit实现token失效刷新的处理方案,现在Flutter项目也有“token验证过期”的需求。刚开始我使用的是EventBus来通知弹出登录页面,但是发现在refresh token过期后并没有去登录,原因是EventBus需要在有生命周期的State状态中才能发送消息,在这里我构造了全局的上下文,以便弹出登录页面。所以接下来我简单总结一下在Flutter项目中如何实现自动刷新token并重发请求的拦截器功能,希望对大家有所帮助。
需求:
1.有两个token, 分别为access_token和refresh_Token, access_token的有效期为1小时,refresh_Token的有效期为7天;
2.如果access_token1个小时后过期了,服务器会返回401,此时客户端要根据刷新access_token的retrofit接口去重新请求新的access_token;
3.如果refresh_Token7天后也过期了,则要求跳到登录页面。
思路:
1.Dio实现网络请求
2.自定义token拦截器,实现token自动刷新并重发请求
3.RefreshToken过期,弹出登录页面。
实现的步骤:
1.配置Android目录的gradle依赖
dependencies {...implementation 'de.greenrobot:eventbus:3.0.0-beta1'}
2.在pubspec.yaml添加sdk
dependencies:...cupertino_icons: ^0.1.0dio: ^2.1.7
3.封装一个获取新的accessToken方法
Future<String> getToken() async {String refreshToken = DataUtil.getRefreshToken; //获取当前的refreshTokenString accessToken;Dio tokenDio = new Dio(); //创建新Dio实例tokenDio.options.headers['refresh-token'] = refreshToken;//设置当前的refreshTokentry {String url = url; //refreshToken urlvar response = await tokenDio.get(url); //请求refreshToken刷新的接口accessToken = response.data['access_token']; //新的accessTokenrefreshToken = response.data['refresh_token'];//新的refreshTokenDataUtil.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; //上下文@overrideonError(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; //获取当前的refreshTokenString accessToken ;Dio tokenDio =new Dio(); //创建新的Dio实例tokenDio.options.headers['refresh-token'] = refreshToken ;//设置当前的refreshTokentry {String url = url; //refreshToken urlvar response = await tokenDio.get(url); //请求refreshToken刷新的接口accessToken = response.data['access_token']; //新的accessTokenrefreshToken = response.data['refresh_token'];//新的refreshTokenDataUtil.saveAccessToken(accessToken); //保存新的accessTokenDataUtil.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; //获取当前的accessTokenoptions = 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拦截器,经过不断调试,最终实现了功能。如果有什么疑问的话,欢迎留言联系我哦!
