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依赖

    1. dependencies {
    2. ...
    3. implementation 'de.greenrobot:eventbus:3.0.0-beta1'
    4. }

    2.在pubspec.yaml添加sdk

    1. dependencies:
    2. ...
    3. cupertino_icons: ^0.1.0
    4. dio: ^2.1.7

    3.封装一个获取新的accessToken方法

    1. Future<String> getToken() async {
    2. String refreshToken = DataUtil.getRefreshToken; //获取当前的refreshToken
    3. String accessToken;
    4. Dio tokenDio = new Dio(); //创建新Dio实例
    5. tokenDio.options.headers['refresh-token'] = refreshToken;//设置当前的refreshToken
    6. try {
    7. String url = url; //refreshToken url
    8. var response = await tokenDio.get(url); //请求refreshToken刷新的接口
    9. accessToken = response.data['access_token']; //新的accessToken
    10. refreshToken = response.data['refresh_token'];//新的refreshToken
    11. DataUtil.saveRefreshToken(refreshToken); //保存新的refreshToken
    12. } on DioError catch (e) {
    13. if(e.response.statusCode==401){ //401代表refresh_token过期
    14. //refreshToken过期,弹出登录页面
    15. //解决方法一:封装一个全局的context
    16. //return Navigator.of(Util.context).push(new MaterialPageRoute( builder: (ctx) => new LoginPage()));
    17. //解决方法二:当refresh token过期,把退出的登录的操作放在dio网络请求的工具类去操作
    18. }
    19. }
    20. return accessToken;
    21. }

    4.token失效时,异步获取accessToken

    1. onError(DioError error) async {
    2. if (error.response != null && error.response.statusCode == 401) { 401代表access token过期
    3. Dio dio = DioUtil().dio;//获取Dio单例
    4. dio.lock(); //加锁
    5. String accessToken = await getToken(); //异步获取新的accessToken
    6. ...
    7. dio.unlock(); //解锁
    8. }
    9. super.onError(error);
    10. }

    5.重新发起一个请求获取数据

    1. //重新发起一个请求获取数据
    2. Dio tokenDio2 = new Dio(); //创建新的dio实例
    3. tokenDio2.options.headers['access-token'] = accessToken;
    4. try {
    5. var newRequest = await tokenDio2.request(request.path);
    6. return newRequest;
    7. } on DioError catch (e) {
    8. return e;
    9. }

    6.Dio拦截器的完整代码

    1. typedef void ChildContext(BuildContext context);
    2. class TokenInterceptor extends Interceptor {
    3. ChildContext context; //上下文
    4. @override
    5. onError(DioError error) async {
    6. if (error.response != null && error.response.statusCode == 401) { //401代表token过期
    7. Dio dio = DioUtil().dio;//获取Dio单例
    8. dio.lock(); //加锁
    9. String accessToken = await getToken(); //异步获取新的accessToken
    10. //重新发起一个请求获取数据
    11. Dio tokenDio2 = new Dio();//创建新的Dio实例
    12. tokenDio2.options.headers['access-token'] = accessToken;
    13. try {
    14. var newRequest = await tokenDio2.request(request.path);
    15. return newRequest;
    16. } on DioError catch (e) {
    17. return e;
    18. }
    19. dio.unlock(); //解锁
    20. }
    21. super.onError(error);
    22. }
    23. Future<String> getToken() async {
    24. String refreshToken = DataUtil.getRefreshToken; //获取当前的refreshToken
    25. String accessToken ;
    26. Dio tokenDio =new Dio(); //创建新的Dio实例
    27. tokenDio.options.headers['refresh-token'] = refreshToken ;//设置当前的refreshToken
    28. try {
    29. String url = url; //refreshToken url
    30. var response = await tokenDio.get(url); //请求refreshToken刷新的接口
    31. accessToken = response.data['access_token']; //新的accessToken
    32. refreshToken = response.data['refresh_token'];//新的refreshToken
    33. DataUtil.saveAccessToken(accessToken); //保存新的accessToken
    34. DataUtil.saveRefreshToken(refreshToken); //保存新的refreshToken
    35. } on DioError catch (e) {
    36. if(e.response.statusCode==401){//401代表refresh_token过期
    37. //refreshToken过期,弹出登录页面
    38. //解决方法一:封装一个全局的context
    39. //return Navigator.of(Util.context).push(new MaterialPageRoute( builder: (ctx) => new LoginPage()));
    40. //解决方法二:当refresh token过期,把退出的登录的操作放在dio网络请求的工具类去操作
    41. }
    42. }
    43. return accessToken;
    44. }
    45. }

    7.在网络请求的工具类添加自定义的token拦截器

    1. /*
    2. *Dio网络请求的工具类
    3. */
    4. class DioUtil {
    5. Dio dio;
    6. static DioUtil _instance;
    7. static DioUtil getInstance() {
    8. if (_instance == null) {
    9. _instance = DioUtil();
    10. }
    11. return _instance;
    12. }
    13. //get方法
    14. Future<Response> get(url, {data, options, cancelToken}) async {
    15. String accessToken = DataUtil.getAccessToken; //获取当前的accessToken
    16. options = BaseOptions(
    17. connectTimeout: 15000,
    18. headers: {
    19. Constants.accessToken: accessToken
    20. },
    21. );
    22. dio = new Dio(options);
    23. //添加自定义的token拦截器
    24. dio.interceptors.add(new TokenInterceptor());
    25. Response response;
    26. try {
    27. response = await dio.get(url, cancelToken: cancelToken);
    28. } on DioError catch (e) {
    29. print(e.response.data);
    30. }
    31. return response;
    32. }
    33. }

    8.总结
    在Flutter项目中自定义一个自动刷新并重发请求的Dio拦截器,经过不断调试,最终实现了功能。如果有什么疑问的话,欢迎留言联系我哦!