flutter 在dio QueuedInterceptor返回401后重试获取新的访问令牌

93ze6v8z  于 2022-11-17  发布在  Flutter
关注(0)|答案(1)|浏览(624)

我正在尝试使用flutter实现JWT访问/刷新标记流。在我的访问标记过期后,我的QueuedInterceptor使用刷新标记获取新的访问标记。一切都正常,但它没有重试获取请求的资源并返回401。在刷新该页面后,资源加载。如何使用QueuedInterceptor实现重试?

class AuthInterceptor extends QueuedInterceptor {
  final Dio _dio;

  AuthInterceptor(this._dio);

  @override
  void onRequest(
      RequestOptions options, RequestInterceptorHandler handler) async {
    final accessToken = await storage.read(key: "accessToken");
    final refreshToken = await storage.read(key: "refreshToken");

    if (accessToken == null || refreshToken == null) {
      const AuthState.unauthenticated();

      final error = DioError(requestOptions: options, type: DioErrorType.other);
      return handler.reject(error);
    }

    final accessTokenHasExpired = JwtDecoder.isExpired(accessToken);
    final refreshTokenHasExpired = JwtDecoder.isExpired(refreshToken);

    var _refreshed = true;

    if (refreshTokenHasExpired) {
      const AuthState.unauthenticated();

      final error = DioError(requestOptions: options, type: DioErrorType.other);

      return handler.reject(error);
    } else if (accessTokenHasExpired) {
      // regenerate new access token
      _refreshed = await _regenerateAccessToken();
    }

    if (_refreshed) {
      options.headers["Authorization"] = "Bearer $accessToken";

      return handler.next(options);
    } else {
      final error = DioError(requestOptions: options, type: DioErrorType.other);
      return handler.reject(error);
    }
  }

  Future<bool> _regenerateAccessToken() async {
    try {
      var dio = Dio();

      final refreshToken = await storage.read(key: "refreshToken");

      final response = await dio.post(
        "https://localhost:7104/api/Login/Token/Refresh",
        options: Options(headers: {"Authorization": "Bearer $refreshToken"}),
      );

      if (response.statusCode == 200 || response.statusCode == 201) {
        final newAccessToken = response.data["accessToken"];
        storage.write(key: "accessToken", value: newAccessToken);
        return true;
      } else if (response.statusCode == 401 || response.statusCode == 403) {
        const AuthState.unauthenticated();
        return false;
      } else {
        return false;
      }
    } on DioError {
      return false;
    } catch (e) {
      return false;
    }
  }
}

这就是我用拦截器创建请求的方式。如果我的访问令牌过期,拦截器将抛出401:

final dio = Dio();

    dio.options.baseUrl = authenticationBackend;
    dio.interceptors.addAll([
      AuthInterceptor(dio),
    ]);

    var response = await dio.get('$host/animals');
bvn4nwqk

bvn4nwqk1#

class RefreshTokenInterceptor extends Interceptor {
  final Dio dio;

  RefreshTokenInterceptor({
    required this.dio,
  });

  @override
  void onError(DioError err, ErrorInterceptorHandler handler) async {
    if (err.response == null) {
      return;
    }
    if (err.response!.statusCode == 401) {
      var res = await refreshToken();
      if (res != null && res) {
        await _retry(err.requestOptions);
      }
    }
    return handler.next(err);
  }

  /// Api to get new token from refresh token
  ///
  Future<bool?> refreshToken() async {
  ///call your refesh token api here
  }

  /// For retrying request with new token
  ///
  Future<Response<dynamic>> _retry(RequestOptions requestOptions) async {
    final options = Options(
      method: requestOptions.method,
      headers: requestOptions.headers,
    );
    return dio.request<dynamic>(requestOptions.path,
        data: requestOptions.data,
        queryParameters: requestOptions.queryParameters,
        options: options);
  }
}

然后用它

dio.interceptors.addAll(
    [
      /// interceptor for refreshing token
      ///
      RefreshTokenInterceptor(dio: dio),

    ],
  );

相关问题