Flutter/Dart: вызвать функцию внутри себя, чтобы многократно запускать таймер для обновления токена JWT?
Вместо того, чтобы проверять, истек ли срок действия моего токена JWT для каждого запроса, я хотел бы проверять его только при первой инициализации приложения в
main
а затем автоматически обновлять его каждые 55 минут.
Вот моя функция обновления, которую я вызываю в верхней части дерева виджетов;
void main() async {
await refreshToken()
};
А вот код refreshToken;
Future<String?> refreshToken() async {
String? refreshToken = await getCryptRefresh(); //gets refresh token from an encrypted box
final http.Response response =
await http.post(Uri.parse('http://example.com/refresh'),
headers: <String, String>{'Content-Type': 'application/json; charset=UTF-8'},
body: jsonEncode(<String?, dynamic>{
'email': currentemail,
'id': currentuserid,
'refreshToken': refreshToken
}),
);
if (response.body.contains('token')) {
Map<String, dynamic> refreshMap = json.decode(response.body);
String token = refreshMap['token'];
putCryptJWT(token); // stores new token in encrypted Hive box
print("token renewed = " + token);
Timer(Duration(minutes: 55), () {
refreshToken();
});
return token;
} else {
String noresponse = 'responsebody doesnt contain token';
print(noresponse);
}
}
Студия Android сначала выдала мне нулевую ошибку с красной линией под
refreshToken()
и предложил нулевую проверку
!
. Но как только я это сделал, я дал мне эту ошибку;
The expression doesn't evaluate to a function, so it can't be invoked.
Было предложено удалить круглые скобки в вызове геттера. Поэтому я удалил круглые скобки и нулевую отметку, чтобы просто
refreshToken
;
Но он не запускается каждые 55 минут, как я надеялся.
Я бы предпочел не проверять срок действия по каждому запросу. И мой JWTToken, и его RefreshToken хранятся в зашифрованном ящике Hive. Следовательно, проверка каждого запроса кажется немного сложной.
3 ответа
Я бы не советовал так поступать. Вы создаете для себя много работы. Лучшим подходом было бы перехватить ответ на ваш запрос. Проверьте, является ли код ответа 401 (что означает несанкционированный), который, я думаю, вернет ваш бэкэнд. Затем обновите токен и снова запустите исходный запрос. Это гораздо более простой способ работы, поэтому нет ненужных проверок срока действия токенов, и пользовательский интерфейс по-прежнему остается безупречным. Вы можете легко сделать это с помощью пакета Dio. Вы можете сделать что-то подобное.
var _http;
void initMethod(){
_http = Dio();
//set some headers
_http.options.contentType = "application/json";
_http.options.headers['Content-Type'] = "application/json";
//Now add interceptors for both request and response to add a token on outgoing request and to handle refresh on failed response.
_http.interceptors
.add(InterceptorsWrapper(onRequest: (RequestOptions options) async {
return await _addAuthenticationToken(options);
}, onResponse: (Response response) async {
if (response.statusCode == 401) return await refreshToken(response);
return response;
}));
}
Future<Response> refreshtoken(Response response){
//Get your new token here
//Once you have the token, retry the original failed request. (After you set the token where ever you store it etc)
return await _retry(response);
}
Future<RequestOptions> _addAuthenticationToken(RequestOptions options) async {
if (tokenPair != null)
options.headers['Authorization'] =
"Bearer " + "yout token goes here";
return options;
}
Future<Response<dynamic>> _retry(RequestOptions requestOptions) async {
final options = new Options(
method: requestOptions.method,
headers: requestOptions.headers,
);
return await this._http.request<dynamic>(requestOptions.path,
data: requestOptions.data,
queryParameters: requestOptions.queryParameters,
options: options);
}
Возможно, вам придется поэкспериментировать, чтобы убедиться, что вы получили желаемый результат, но это должно помочь
В некоторых случаях вы не можете получить ответ от серверной части, как в моем случае. Я добавляю счетчик в приложение, так как начинаю получать токены, и продолжаю считать. Ваш способ - это ответ на тактовую частоту процессора, которую я тестировал, пока она не была точной, некоторые устройства могут считать быстрее, некоторые даже медленнее. Мой способ будет использовать дату и время сейчас и проверять другое время даты сейчас с меткой времени, которую мы сохраняем в начале каждые 15 секунд, поэтому она все еще не совсем точна, но, по крайней мере, ошибка не должна быть более 15 секунд.
DateTime _timeoutAt;
start() {
_timeoutAt = DateTime.now().add(Duration(minutes: 55);
Timer _timerCheckIsTimeout = Timer.periodic(Duration(seconds: 15), (timer) {
final isTimeout = DateTime.now().isAfter(_timeoutAt)
if (isTimeout) {
//Call back from here
await refreshToken();
timer.cancel();
}
}
Я знаю, что это не лучшая практика, но у людей разные условия, и они не могут применить лучшую практику. Используйте приведенный выше пример, если вы можете управлять серверной частью, чтобы ответить на него.
Я не знаю, предпочтительнее ли это использовать Dio, но я просто сделал это для всех HTTP-запросов post;
else if (response.body.contains('TokenExpiredError')) {
await refreshToken();
return downloadMainJSON(
);
Вроде хорошо работает.