在没有firebase的情况下处理flutter中的登录和身份验证

时间:2021-07-07 14:38:11

标签: android flutter dart authentication jwt

我有一个带有两个端点的 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

我的问题是:

  • 如何让用户始终保持连接(唯一的方法是在刷新令牌过期时存储凭据并发送主题?如果是这样是否安全?以及在这种情况下如何撤销会话)
  • 如果我发送请求并发现刷新令牌已过期,我该怎么办(使用保存的凭据重新登录?注销用户?)。
  • 我的暗示好吗?如果不是要改变它。

0 个答案:

没有答案
相关问题