我有一个带有两个端点的 REST API 服务器,用于 JWT 身份验证(/auth/token/ 和 /auth/token/refresh)。我想创建一个使用该休息服务器的应用程序。到目前为止我所做的是: 创建了一个 userManager 类。当用户登录时,我保存了两个令牌的用户名、密码、访问令牌、刷新令牌和到期时间(我使用的是安全存储包)。每当我发送请求(如果请求需要身份验证)时,我都会验证访问令牌是否有效。如果它无效,我会刷新它并发送请求。访问令牌有 5 分钟的 exp 时间,刷新令牌有 24 小时(django 的默认值)。
我的代码:
import 'dart:convert';
import 'package:ads_site_app/utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart';
import 'data.dart';
class UserManager {
static final _storage = FlutterSecureStorage();
static const _keyUsername = 'username';
static const _keyPassword = 'password';
static const _keyEmail = 'email';
static const _keyAccessToken = 'accesstoken';
static const _keyRefreshToken = 'refreshtoken';
static const _keyAccessExpTime = 'accesstokenExptime';
static const _keyRefreshExpTime = 'refreshtokenExptime';
static String _loginError = "";
static String get loginError => _loginError;
static set loginError(String loginError) {
_loginError = loginError;
}
static Future setUsername(String username) async =>
await _storage.write(key: _keyUsername, value: username);
static Future<String?> getUsername() async =>
await _storage.read(key: _keyUsername);
static Future _setPassword(String password) async =>
await _storage.write(key: _keyPassword, value: password);
static Future<String?> _getPassword() async {
final value = await _storage.read(key: _keyPassword);
return value;
}
static Future _setAccessToken(String token) async {
final value = token;
await _storage.write(key: _keyAccessToken, value: value);
}
static Future<String?> getAccessToken() async {
final value = await _storage.read(key: _keyAccessToken);
return value;
}
static Future _setRefreshToken(String token) async {
final value = token;
await _storage.write(key: _keyRefreshToken, value: value);
}
static Future<String?> getRefreshToken() async {
final value = await _storage.read(key: _keyRefreshToken);
return value;
}
static Future _setAccessTokenExpTime(DateTime time) async {
final value = time;
await _storage.write(key: _keyAccessExpTime, value: value.toString());
}
static Future<DateTime?> getAccessTokenExpTime() async {
final value = await _storage.read(key: _keyAccessExpTime);
return value == null ? null : DateTime.parse(value);
}
static Future _setRefreshTokenExpTime(DateTime time) async {
final value = time;
await _storage.write(key: _keyRefreshExpTime, value: value.toString());
}
static Future<DateTime?> getRefreshTokenExpTime() async {
final value = await _storage.read(key: _keyRefreshExpTime);
return value == null ? null : DateTime.parse(value);
}
//////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////
static Future<void> login(
String url, String path, String username, String password) async {
Response response;
try {
response = await post(
Uri.http(
url,
path,
),
body: {'username': username, 'password': password});
if (response.statusCode >= 200 && response.statusCode < 300) {
var time = DateTime.now();
var data = jsonDecode(response.body) as Map;
var refreshParsedToken = parseJwt(data['refresh']);
var accessParsedToken = parseJwt(data['access']);
var refreshExpTime = DateTime.fromMillisecondsSinceEpoch(
refreshParsedToken['exp'] * 1000)
.subtract(Duration(minutes: 30));
var accessExpTime =
DateTime.fromMillisecondsSinceEpoch(accessParsedToken['exp'] * 1000)
.subtract(Duration(minutes: 2));
_setRefreshToken(data['refresh']);
_setAccessToken(data['access']);
_setAccessTokenExpTime(accessExpTime);
_setRefreshTokenExpTime(refreshExpTime);
setUsername(username);
_setPassword(password);
} else {
throw InvalidCredentials('invalid username or password');
}
} catch (e) {
print(e);
throw e;
}
}
static Future<bool> refreshToken(String url, String path) async {
Response response;
String? token = await getRefreshToken();
try {
response = await post(
Uri.http(
url,
path,
),
body: {'refresh': ' ${token}'});
if (response.statusCode >= 200 && response.statusCode < 300) {
var data = jsonDecode(response.body) as Map;
await _setAccessToken(data['access']);
return true;
} else {
return false;
}
} catch (e) {
print('user manager 145');
return false;
}
}
static Future<dynamic> getRequest(
String url, String path, bool require_Auth) async {
Response response;
if (require_Auth) {
if (await isLogedIn()) {
String? token = await getAccessToken();
try {
response = await get(
Uri.http(
url,
path,
),
headers: {'Authorization': 'Bearer ${token}'});
return response;
} catch (e) {
print('connection lost usermanager 112');
print(e);
return null;
}
} else {
var username = await getUsername();
var password = await _getPassword();
if (username != null && password != null) {
try {
await login(url, '/api/auth/token/', username, password);
String? token = await getAccessToken();
try {
response = await get(
Uri.http(
url,
path,
),
headers: {'Authorization': 'Bearer ${token}'});
return response;
} catch (e) {
print('connection lost usermanager 112');
print(e);
return null;
}
} catch (e) {
print('login failed user manager 140');
return null;
}
}
}
} else {
try {
response = await get(
Uri.http(
url,
path,
),
);
return response;
} catch (e) {
print('connection lost usermanager 209');
print(e);
return null;
}
}
}
static Future<bool> isLogedIn() async {
var accessExpTime = await getAccessTokenExpTime();
var refreshExpTime = await getRefreshTokenExpTime();
if (accessExpTime == null ||
refreshExpTime == null ||
(accessExpTime.isBefore(DateTime.now()) &&
refreshExpTime.isBefore(DateTime.now()))) {
return false;
} else if (accessExpTime.isAfter(DateTime.now())) {
return true;
} else if (refreshExpTime.isAfter(DateTime.now())) {
return await refreshToken(url, '/api/auth/token/refresh');
}
return false;
}
static void register(String username, String email, String password1,
String password2) async {}
static void logout(BuildContext context, String redirectPath) async {
_storage.delete(key: _keyAccessExpTime);
_storage.delete(key: _keyAccessToken);
_storage.delete(key: _keyRefreshExpTime);
_storage.delete(key: _keyRefreshToken);
Navigator.pushReplacementNamed(context, redirectPath);
}
}
parseJWT 方法在这里: How to get the claims from a JWT in my Flutter Application
我的问题是: