Застрял с архитектурой аутентификации flutter

#flutter #dart

#флаттер #dart

Вопрос:

Хорошо, добрый день всем, здесь возникает вопрос:

Я начал использовать flutter для мобильного интерфейса, это довольно приятно. Но я не могу понять идею обработки токена jwt и перенаправления, если он существует.

Рассмотрим небольшое приложение, позвольте мне упростить:

 
final storage = FlutterSecureStorage(); # init storage here?

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      theme: ThemeData.dark(),
      home: LoginPage(), # want home page actually, smth like if storage is empty, then call loginpage, if storage is not empty - try to make http request, if request is okay and data received - show homepage, if request is rejected - login to obtain new jwt
    );
  }
}
  

http-запрос:

 Future<User> userLogin(String login, String password) async {
    Response response = await post('$_apiUrl'   '/'   '$_loginUrl',
        headers: <String, String>{
          'Content-Type': 'application/json; charset=UTF-8',
        },
        body: jsonEncode(<String, String>{
          'login': login,
          'password': password,
        }));
    if (response.statusCode == 200) {
      return User.fromJson(json.decode(response.body));
    } else {
      throw HttpException(
          'Error making login request. Status code: ${response.statusCode}');
    }
  }
  

Пользовательский класс:

 class User {

  final String token;
  final String uuid;
  final String login;
  final String email;
  final String firstName;
  final String lastName;

  User({this.token, this.uuid, this.login, this.email, this.firstName, this.lastName});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      token: json['token'],
      uuid: json['user']['uuid'],
      login: json['user']['login'],
      email: json['user']['email'],
      firstName: json['user']['firstName'],
      lastName: json['user']['lastName'],
    );
  }
}
  

Итак, как это работает сейчас:

В основном файле на данный момент я вызываю страницу входа с кнопкой:

 FlatButton(
                child: Text(
                  'Sign in',
                  style: TextStyle(
                    color: Colors.black,
                  ),
                ),
                color: Colors.white,
                onPressed: () {
                  Future<User> user = ApiRequestController().userLogin('alex', 'alex123');
                  Navigator.push(context, MaterialPageRoute(builder: (context) {
                    return UserProfile(userData: user,);
                  }));
                },
              ),
  

Затем я перенаправляюсь на профиль пользователя, ничего интересного:

 class UserProfile extends StatefulWidget {
  UserProfile({this.userData});
  final userData;

  @override
  _UserProfileState createState() => _UserProfileState();
}

class _UserProfileState extends State<UserProfile> {
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    futureUser = widget.userData;
    print(futureUser);
  }

  Future<User> futureUser;

  @override
  Widget build(BuildContext context) {
common stuff like scaffold etc

  

And then I build a profile:

 FutureBuilder<User>(
                future: futureUser,
                builder: (context, snapshot) {
                  if (snapshot.hasData) {
                    dynamic userProfile = snapshot.data;
                    return Column(
                      children: [
                        Container(
                          child: ListTile(
                            title: Text(
                              'login: '   '${userProfile.login}',
                              style: TextStyle(
                                color: Colors.black,
                              ),
                            ),
                          ),
                        ),
                        Container(
                          child: ListTile(
                            title: Text(
                              'uuid: '   '${userProfile.uuid}',
                              style: TextStyle(
                                color: Colors.black,
                              ),
                            ),
                          ),
                        ),
                        Container(
                          child: ListTile(
                            title: Text(
                              'First name: '   '${userProfile.firstName}',
                              style: TextStyle(
                                color: Colors.black,
                              ),
                            ),
                          ),
                        ),
                        Container(
                          child: ListTile(
                            title: Text(
                              'Last name: '   '${userProfile.lastName}',
                              style: TextStyle(
                                color: Colors.black,
                              ),
                            ),
                          ),
                        ),
                        Container(
                          child: ListTile(
                            title: Text(
                              'Auth token: '   '${userProfile.token}',
                              style: TextStyle(
                                color: Colors.black,
                              ),
                            ),
                          ),
                        ),
                      ],
                    );
                  } else if (snapshot.hasError) {
                    return Text('${snapshot.error}');
                  }
                  return CircularProgressIndicator();
                },
              ),
  

Интерфейс упрощен для облегчения понимания.

Итак, что у меня есть сейчас, у меня есть основной файл dart, где я вызываю экран входа в систему (не домашний экран, потому что я не могу понять, как использовать токен), где я вызываю асинхронный http-запрос к api, затем я перенаправляюсь в профиль пользователя.

Идея очень проста:

  1. Приложение открыто -> отправить http-запрос с существующим заголовком аутентификации, если ответ равен 200, затем показать домашнюю страницу
  2. Если ответ не был равен 200 (токен истек / поврежден / не существует, проверено на стороне сервера), затем перейдите к экрану входа в систему, чтобы получить новый токен аутентификации
  3. Как правильно хранить проклятый токен jwt -_-? Я несколько раз пробовал использовать фьючерсы, простые запросы, я не могу понять, как анализировать токен между несколькими экранами.

Что мне делать? Где инициализировать токен? Где его хранить? Как я могу вызвать его из другого виджета? Я читал о InheritedWidget, хорошо. Но, т.Е., Моя домашняя страница уже расширяет StatefulWidget, и множественного наследования нет. Расширить его до пользовательского класса или что?

Итак, я застрял с этой простой целью и не могу двигаться дальше. Просто нужно проверить, существует ли токен -> показать home, если нет — показать login. После того, как мы получим правильный токен, мы можем использовать все приложение целиком, выполняя каждый запрос с использованием токена аутентификации, полученного на первом экране входа в систему.

Где инициализировать токен? Где я должен наследовать InheritedWidget? Модель? Страница просмотра? Где? Сбой архитектуры: (

Помощь очень ценится! Заранее спасибо

Комментарии:

1. Существует пакет flutter, который называется flutter_secure_storage you, вы можете использовать его для сохранения токена

2. В вашем случае я бы использовал flutter_secure_storage and provider , поэтому загружает токен из локального хранилища только один раз, и каждый раз, когда он вам нужен, вы можете получить его в любом виджете, используя provider. вы также можете хранить информацию о пользователе, используя того же поставщика.

3. Ребята, в первом блоке кода есть защищенное хранилище flutter.

4. Вопрос в том, где его использовать? Основной файл? Специальный класс для наследования? Где?