Http 在Dio中使用Interceptor for Flatter刷新令牌

Http 在Dio中使用Interceptor for Flatter刷新令牌,http,flutter,dart,firebase-authentication,Http,Flutter,Dart,Firebase Authentication,我正在尝试在颤振中使用Dio拦截器,我必须处理令牌过期。 下面是我的代码 Future getApiClient()异步{ token=等待存储.read(key:USER\u token); _dio.interceptors.clear(); _拦截机 .add(拦截器包装器(onRequest:(RequestOptions){ //在发送请求之前做些什么 选项.头文件[“授权”]=“持有人”+令牌; 返回选项; },onResponse:(Response-Response){ //处理

我正在尝试在颤振中使用Dio拦截器,我必须处理令牌过期。 下面是我的代码

Future getApiClient()异步{
token=等待存储.read(key:USER\u token);
_dio.interceptors.clear();
_拦截机
.add(拦截器包装器(onRequest:(RequestOptions){
//在发送请求之前做些什么
选项.头文件[“授权”]=“持有人”+令牌;
返回选项;
},onResponse:(Response-Response){
//处理响应数据
返回响应;//继续
},onError:(DioError错误)异步{
//处理响应错误
if(error.response?.statusCode==403){
//更新令牌并重复
//锁定以阻止传入请求,直到令牌更新
_interceptors.requestLock.lock();
_dio.interceptors.responseLock.lock();
RequestOptions=error.response.request;
FirebaseUser=等待FirebaseAuth.instance.currentUser();
token=wait user.getIdToken(刷新:true);
等待writeAuthKey(令牌);
选项.头文件[“授权”]=“持有人”+令牌;
_interceptors.requestLock.unlock();
_dio.interceptors.responseLock.unlock();
_请求(options.path,options:options);
}否则{
返回误差;
}
}));
_dio.options.baseUrl=baseUrl;
返回_dio;
}

问题是,Dio没有使用新令牌重复网络调用,而是将错误对象返回给调用方法,而调用方法又呈现了错误的小部件,关于如何使用Dio处理令牌刷新的任何线索?

我还没有完成自定义Http客户机实现,但它可以帮助您


您将获得令牌到期的响应状态代码401。为了请求新的访问令牌,您需要使用post方法以及表单数据和所需的Dio选项(内容类型和标题)。下面的代码显示了如何请求新令牌

请求成功后,如果您获得的响应状态代码为200,那么您将获得新的访问令牌值以及刷新令牌值,并将它们保存在您喜欢使用的任何存储器中。例如,共享首选项

一旦您保存了新的访问令牌,您就可以使用它来使用下面相同代码中显示的get方法获取数据

onError(DioError error) async {
    if (error.response?.statusCode == 401) {
      Response response;
      var authToken = base64
          .encode(utf8.encode("username_value" + ":" + "password_value"));
      FormData formData = new FormData.from(
          {"grant_type": "refresh_token", "refresh_token": refresh_token_value});
      response = await dio.post(
        url,
        data: formData,
        options: new Options(
            contentType: ContentType.parse("application/x-www-form-urlencoded"),
            headers: {HttpHeaders.authorizationHeader: 'Basic $authToken'}),
      );
      if (response.statusCode == 200) {
        response = await dio.get(
          url,
          options: new Options(headers: {
            HttpHeaders.authorizationHeader: 'Bearer access_token_value'
          }),
        );
        return response;
      } else {
        print(response.data);
        return null;
      }
    }
    return error;
  }

我用拦截器解决了这个问题,方法如下:-

  Future<Dio> getApiClient() async {
    token = await storage.read(key: USER_TOKEN);
    _dio.interceptors.clear();
    _dio.interceptors
        .add(InterceptorsWrapper(onRequest: (RequestOptions options) {
      // Do something before request is sent
      options.headers["Authorization"] = "Bearer " + token;
      return options;
    },onResponse:(Response response) {
        // Do something with response data
        return response; // continue
    }, onError: (DioError error) async {
      // Do something with response error
      if (error.response?.statusCode == 403) {
        _dio.interceptors.requestLock.lock();
        _dio.interceptors.responseLock.lock();
        RequestOptions options = error.response.request;
        FirebaseUser user = await FirebaseAuth.instance.currentUser();
        token = await user.getIdToken(refresh: true);
        await writeAuthKey(token);
        options.headers["Authorization"] = "Bearer " + token;

        _dio.interceptors.requestLock.unlock();
        _dio.interceptors.responseLock.unlock();
        return _dio.request(options.path,options: options);
      } else {
        return error;
      }
    }));
    _dio.options.baseUrl = baseUrl;
    return _dio;
  }
Future getApiClient()异步{
token=等待存储.read(key:USER\u token);
_dio.interceptors.clear();
_拦截机
.add(拦截器包装器(onRequest:(RequestOptions){
//在发送请求之前做些什么
选项.头文件[“授权”]=“持有人”+令牌;
返回选项;
},onResponse:(Response-Response){
//处理响应数据
返回响应;//继续
},onError:(DioError错误)异步{
//处理响应错误
if(error.response?.statusCode==403){
_interceptors.requestLock.lock();
_dio.interceptors.responseLock.lock();
RequestOptions=error.response.request;
FirebaseUser=等待FirebaseAuth.instance.currentUser();
token=wait user.getIdToken(刷新:true);
等待writeAuthKey(令牌);
选项.头文件[“授权”]=“持有人”+令牌;
_interceptors.requestLock.unlock();
_dio.interceptors.responseLock.unlock();
返回请求(options.path,options:options);
}否则{
返回误差;
}
}));
_dio.options.baseUrl=baseUrl;
返回_dio;
}

我找到了一个简单的解决方案,如下所示:

  Future<Response<dynamic>> _retry(RequestOptions requestOptions) async {
    final options = new Options(
      method: requestOptions.method,
      headers: requestOptions.headers,
    );
    return this.api.request<dynamic>(requestOptions.path,
        data: requestOptions.data,
        queryParameters: requestOptions.queryParameters,
        options: options);
  }

this.api=Dio();
this.api.interceptors.add(interceptorswapper(
onError:(错误)异步{
if(error.response?.statusCode==403||
错误。响应?.statusCode==401){
等待刷新令牌();
返回_重试(错误。请求);
}
返回error.response;
}));
基本上,它检查错误是
401
还是
403
,这是常见的身份验证错误,如果是,它将刷新令牌并重试响应。我对
refreshtToken()
的实现如下所示,但这可能因您的api而异:

Future<void> refreshToken() async {
    final refreshToken = await this._storage.read(key: 'refreshToken');
    final response =
        await this.api.post('/users/refresh', data: {'token': refreshToken});

    if (response.statusCode == 200) {
      this.accessToken = response.data['accessToken'];
    }
  }


下面是我的拦截器的一个片段

 dio.interceptors
        .add(InterceptorsWrapper(onRequest: (RequestOptions options) async {

/* Write your request logic setting your Authorization header from prefs*/

      String token = await prefs.accessToken;
      if (token != null) {
        options.headers["Authorization"] = "Bearer " + token;
      return options; //continue
    }, onResponse: (Response response) async {
// Write your response logic

      return response; // continue
    }, onError: (DioError dioError) async {

      // Refresh Token
      if (dioError.response?.statusCode == 401) {
        Response response;
        var data = <String, dynamic>{
          "grant_type": "refresh_token",
          "refresh_token": await prefs.refreshToken,
          'email': await prefs.userEmail
        };
        response = await dio
            .post("api/url/for/refresh/token", data: data);
        if (response.statusCode == 200) {
          var newRefreshToken = response.data["data"]["refresh_token"]; // get new refresh token from response
          var newAccessToken = response.data["data"]["access_token"]; // get new access token from response
          prefs.refreshToken = newRefreshToken;
          prefs.accessToken = newAccessToken; // to be used in the request section of the interceptor
          return dio.request(dioError.request.baseUrl + dioError.request.path,
              options: dioError.request);
        }
      }
      return dioError;
    }));
    return dio;
  }
}
import 'package:dio/dio.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import '../settings/globals.dart';


class AuthInterceptor extends Interceptor {
  static bool isRetryCall = false;

  @override
  void onRequest(
      RequestOptions options, RequestInterceptorHandler handler) async {
    bool _token = isTokenExpired(token);
    bool _refresh = isTokenExpired(refresh);
    bool _refreshed = true;

    if (_refresh) {
      appAuth.logout();
      EasyLoading.showInfo(
          'Expired session');
      DioError _err;
      handler.reject(_err);
    } else if (_token) {
      _refreshed = await appAuth.refreshToken();
    }
    if (_refreshed) {
      options.headers["Authorization"] = "Bearer " + token;
      options.headers["Accept"] = "application/json";

      handler.next(options);
    }
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) async {
    handler.next(response);
  }

  @override
  void onError(DioError err, ErrorInterceptorHandler handler) async {
    handler.next(err);
  }
}
dio.interceptors
.add(拦截器包装器(onRequest:(RequestOptions选项)异步{
/*写入请求逻辑,从prefs设置授权标头*/
String token=wait prefs.accessToken;
if(令牌!=null){
选项.头文件[“授权”]=“持有人”+令牌;
返回选项;//继续
},onResponse:(响应-响应)异步{
//写下你的回应逻辑
返回响应;//继续
},onError:(DioError DioError)异步{
//刷新令牌
if(dioError.response?.statusCode==401){
反应;
风险值数据={
“授权类型”:“刷新令牌”,
“刷新令牌”:等待prefs.refreshToken,
“电子邮件”:等待prefs.userEmail
};
响应=等待dio
.post(“api/url/for/refresh/token”,数据:数据);
如果(response.statusCode==200){
var newrefreshttoken=response.data[“data”][“refresh_token”];//从response获取新的刷新令牌
var newAccessToken=response.data[“data”][“access_token”];//从response获取新的访问令牌
prefs.refreshToken=newRefreshToken;
prefs.accessToken=newAccessToken;//将在拦截器的请求部分中使用
返回dio.request(dioError.request.baseUrl+dioError.request.path,
选项:dioError.request);
}
}
返回错误;
}));
 RestClient client;
        
          static BaseOptions options = new BaseOptions(
            connectTimeout: 5000,
            receiveTimeout: 3000,
          );
          RemoteService() {
            // or new Dio with a BaseOptions instance.
            final dio = Dio(options);
        
            dio.interceptors
                .add(InterceptorsWrapper(onRequest: (RequestOptions options) async {
              // Do something before request is sent
              return options; //continue
            }, onResponse: (Response response) async {
              // Do something with response data
              return response; // continue
            }, onError: (DioError error) async {
              // Do something with response error
              if (error.response.statusCode == 401) {
                Response response =
                    await dio.post("http://addrees-server/oauth/token",
                        options: Options(
                          headers: {
                            'Authorization': ApiUtils.BASIC_TOKEN,
                            'Content-Type': ApiUtils.CONTENT_TYPE,
                          },
                        ),
                        queryParameters: {
                      "grant_type": ApiUtils.GRANT_TYPE,
                      "username": AppConstants.LOGIN,
                      "password": AppConstants.PASSWORD
                    });
        
                Sessions.access_token = response.data['access_token'];
        
                error.response.request.queryParameters
                    .update('access_token', (value) => Sessions.access_token);
                RequestOptions options = error.response.request;
        
                return dio.request(options.path, options: options); //continue
              } else {
                return error;
              }
            }));
            client = RestClient(dio);
          }
http: ^0.13.3
dio: ^4.0.0
flutter_secure_storage: ^4.2.0
jwt_decode: ^0.3.1
flutter_easyloading: ^3.0.0 
AuthService appAuth = new AuthService();

class AuthService {
  Future<void> logout() async {
    token = '';
    refresh = '';

    await Future.delayed(Duration(milliseconds: 100));

    Navigator.of(cnt).pushAndRemoveUntil(
      MaterialPageRoute(builder: (context) => LoginPage()),
      (_) => false,
    );
  }

  Future<bool> login(String username, String password) async {
    var headers = {'Accept': 'application/json'};
    var request = http.MultipartRequest('POST', Uri.parse(baseURL + 'token/'));
    request.fields.addAll({'username': username, 'password': password});
    request.headers.addAll(headers);
    http.StreamedResponse response = await request.send();

    if (response.statusCode == 200) {
      var resp = await response.stream.bytesToString();
      final data = jsonDecode(resp);
      token = data['access'];
      refresh = data['refresh'];
      secStore.secureWrite('token', token);
      secStore.secureWrite('refresh', refresh);
      return true;
    } else {
      return (false);
    }
  }

  Future<bool> refreshToken() async {
    var headers = {'Accept': 'application/json'};
    var request =
        http.MultipartRequest('POST', Uri.parse(baseURL + 'token/refresh/'));
    request.fields.addAll({'refresh': refresh});

    request.headers.addAll(headers);

    http.StreamedResponse response = await request.send();

    if (response.statusCode == 200) {
      final data = jsonDecode(await response.stream.bytesToString());
      token = data['access'];
      refresh = data['refresh'];

      secStore.secureWrite('token', token);
      secStore.secureWrite('refresh', refresh);
      return true;
    } else {
      print(response.reasonPhrase);
      return false;
    }
  }
}
import 'package:dio/dio.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import '../settings/globals.dart';


class AuthInterceptor extends Interceptor {
  static bool isRetryCall = false;

  @override
  void onRequest(
      RequestOptions options, RequestInterceptorHandler handler) async {
    bool _token = isTokenExpired(token);
    bool _refresh = isTokenExpired(refresh);
    bool _refreshed = true;

    if (_refresh) {
      appAuth.logout();
      EasyLoading.showInfo(
          'Expired session');
      DioError _err;
      handler.reject(_err);
    } else if (_token) {
      _refreshed = await appAuth.refreshToken();
    }
    if (_refreshed) {
      options.headers["Authorization"] = "Bearer " + token;
      options.headers["Accept"] = "application/json";

      handler.next(options);
    }
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) async {
    handler.next(response);
  }

  @override
  void onError(DioError err, ErrorInterceptorHandler handler) async {
    handler.next(err);
  }
}
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

SecureStorage secStore = new SecureStorage();

class SecureStorage {
  final _storage = FlutterSecureStorage();
  void addNewItem(String key, String value) async {
    await _storage.write(
      key: key,
      value: value,
      iOptions: _getIOSOptions(),
    );
  }

  IOSOptions _getIOSOptions() => IOSOptions(
        accountName: _getAccountName(),
      );

  String _getAccountName() => 'blah_blah_blah';

  Future<String> secureRead(String key) async {
    String value = await _storage.read(key: key);
    return value;
  }

  Future<void> secureDelete(String key) async {
    await _storage.delete(key: key);
  }

  Future<void> secureWrite(String key, String value) async {
    await _storage.write(key: key, value: value);
  }
}
bool isTokenExpired(String _token) {
  DateTime expiryDate = Jwt.getExpiryDate(_token);
  bool isExpired = expiryDate.compareTo(DateTime.now()) < 0;
  return isExpired;
}
var dio = Dio();

Future<Null> getTasks() async {
EasyLoading.show(status: 'Wait ...');
    
Response response = await dio
    .get(baseURL + 'tasks/?task={"foo":"1","bar":"30"}');
    
if (response.statusCode == 200) {
    print('success');
} else {
    print(response?.statusCode);
}}