오늘하루도 우힣ㅎ

Flutter) FlutterSecureStorage를 이용한 자동 로그인 구현 본문

flutter/Etc

Flutter) FlutterSecureStorage를 이용한 자동 로그인 구현

우힣 2020. 10. 19. 11:57

  어플을 사용할때 한번 로그인을 하게 되면 계속해서 로그인이 유지 되는것을 볼 수 있다. 사용할때는 당영하다고 생각을하고 편리함을 잊고 살고는 한다. 하지만 자동 로그인이 없다면 어플을 실행할때마다 다시 로그인을 해야하는 귀찮은 과정을 겪어야 한다. 이는 사용자들의 이탈을 불러일으키게 될 것이다.

 

 이에 자동 로그인 기능을 만들어 내기 위해서 flutter_secure_storage를 사용하려 한다. flutter secure storage는 플랫폼별로 다른 형식으로 작동을 하는데 ios의 경우 KeyChain방식을 이용하고, android의 경우 KeyStore방식이 사용이 된다.

 

KeyChain과 KeySotre에 대한 정확한 정보를 확인하고 싶으면 아래의 사이트에서 확인을 할 수 있습니다.

- KeyChain : developer.apple.com/documentation/security/keychain_services#//apple_ref/doc/uid/TP30000897-CH203-TP1

- KeyStore : developer.android.com/training/articles/keystore.html

 

flutter_secure_storage의 패키지는 pub.dev/packages/flutter_secure_storage 에서 얻어 사용이 가능하다.

 

flutter_secure_storage | Flutter Package

Flutter Secure Storage provides API to store data in secure storage. Keychain is used in iOS, KeyStore based solution is used in Android.

pub.dev

 

1. pubspec.yaml 파일에 패키지 추가하기.

2. 이번에는 로그인 화면과 로그아웃 화면을 만들어 해당 flutter storage secure가 잘 작동하는지 확인을 하려 합니다. 간단한 모습은 아래의 그림으로 볼 수 있습니다.

  • 로그인이 되게 되면 로그인 정보를 기기상에 저장을 합니다.

  • 앱 종료후 다시 실행시켜도 로그인 정보가 기기에 남아있다면 바로 로그아웃 페이지로 넘어가도록 합니다.

  • 로그아웃을 하게 되면 기기에 저장되에 있는 로그인 정보를 지웁니다.

  • 앱을 재실행 시키게 되면 남아 기기에 로그인과 관련된 정보가 존재하지 않으므로 로그인 페이지로 실행이 됩니다.

3-1. main.dart(LoginPage)

해당 페이지에서는 기기에 저장되어 있는 정보를 불러오는 작업, 정보가 있을때 바로 로그아웃 페이지로 가는 작업, 그렇지 않으면 로그인시 기기에 로그인 정보를 저장하는 작업이 필요하다. 코드는 아래와 같이 이루어지게 되는데 주의 깊게 봐야할 부분은 WidetBinding.instance.addPostFrameCallback()부분이다. 이부분은 inistState에서 데이터를 비동기로 불러오기 위해 사용이 되어지는 부분입니다. initState자체에 async를 적용할수 없기 때문에 다음과 같은 방식으로 사용을 합니다.  그 외의 부분은 코드내 주석을 통하여 설명을 하겠습니다.

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app_secure_storage/logout.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Secure Storage',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyLoginPage(title: 'Secure Storage Login'),
    );
  }
}

class MyLoginPage extends StatefulWidget {
  MyLoginPage({Key key, this.title}) : super(key: key);
  final String title;

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

class _MyLoginPage extends State<MyLoginPage> {
  TextEditingController idController;
  TextEditingController passController;
  String userInfo = ""; //user의 정보를 저장하기 위한 변수

  static final storage =
      new FlutterSecureStorage(); //flutter_secure_storage 사용을 위한 초기화 작업
  @override
  void initState() {
    super.initState();
    idController = TextEditingController();
    passController = TextEditingController();

    //비동기로 flutter secure storage 정보를 불러오는 작업.
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _asyncMethod();
    });
  }

  _asyncMethod() async {
    //read 함수를 통하여 key값에 맞는 정보를 불러오게 됩니다. 이때 불러오는 결과의 타입은 String 타입임을 기억해야 합니다.
    //(데이터가 없을때는 null을 반환을 합니다.)
    userInfo = await storage.read(key: "login");
    print(userInfo);

    //user의 정보가 있다면 바로 로그아웃 페이지로 넝어가게 합니다.
    if (userInfo != null) {
      Navigator.pushReplacement(
          context,
          CupertinoPageRoute(
              builder: (context) => LogOutPage(
                    id: userInfo.split(" ")[1],
                    pass: userInfo.split(" ")[3],
                  )));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Padding(
        padding: const EdgeInsets.all(10.0),
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              TextField(
                controller: idController,
                decoration: InputDecoration(labelText: "id"),
              ),
              TextField(
                controller: passController,
                decoration: InputDecoration(labelText: "password"),
              ),
              RaisedButton(
                onPressed: () async {
                  // write 함수를 통하여 key에 맞는 정보를 적게 됩니다.
                  //{"login" : "id id_value password password_value"}
                  //와 같은 형식으로 저장이 된다고 생각을 하면 됩니다.
                  await storage.write(
                      key: "login",
                      value: "id " +
                          idController.text.toString() +
                          " " +
                          "password " +
                          passController.text.toString());

                  Navigator.pushReplacement(
                    context,
                    CupertinoPageRoute(
                        builder: (context) => LogOutPage(
                              id: idController.text,
                              pass: passController.text,
                            )),
                  );
                },
                child: Text("로그인"),
              )
            ],
          ),
        ),
      ),
    );
  }
}

3-2. logout.dart(LogoutPage)

이 부분에서는 로그인 정보를 받아와 id와 password 정보를 보여주도록 하고 있다. 이를 통하여 데이터가 제대로 저장되어 사용이 되어 지는지를 확인 할 수 있다. 코드에 대한 설명은 위와 동일하게 주석을 통하여 설명을 하겠습니다. 여기서는 delete를 통하여 flutter_secure_storage에 저장된 정보를 지우도록 합니다.

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app_secure_storage/main.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

class LogOutPage extends StatefulWidget {
  //로그인 정보를 이전 페이지에서 전달 받기 위한 변수
  final String id;
  final String pass;
  LogOutPage({this.id, this.pass});
  @override
  _LogOutPage createState() => _LogOutPage();
}

class _LogOutPage extends State<LogOutPage> {
  static final storage = FlutterSecureStorage();
  //데이터를 이전 페이지에서 전달 받은 정보를 저장하기 위한 변수
  String id;
  String pass;
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    id = widget.id; //widget.id는 LogOutPage에서 전달받은 id를 의미한다.
    pass = widget.pass; //widget.pass LogOutPage에서 전달받은 pass 의미한다.
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Logout Page"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("id : " + id),
            Text("password : " + pass),
            RaisedButton(
              onPressed: () {
                //delete 함수를 통하여 key 이름이 login인것을 완전히 폐기 시켜 버린다.
                //이를 통하여 다음 로그인시에는 로그인 정보가 없어 정보를 불러 올 수가 없게 된다.
                storage.delete(key: "login");
                Navigator.pushReplacement(
                  context,
                  CupertinoPageRoute(
                      builder: (context) => MyLoginPage(
                            title: "Login Page",
                          )),
                );
              },
              child: Text("Logout"),
            ),
          ],
        ),
      ),
    );
  }
}

 

 이를 통하여 자동 로그인을 할 수 있는 코드는 완성이 돼었습니다. 사실 쉽게 만들기 위해서 띄어쓰기를 통하여 id, password를 구분 하도록 하였지만 저장해 두어야할 정보가 많아지게 된다면 json 형식을 string형태로 바꾸어 정보를 저장하고 그를 다시 decoding 하여 사용 하는 것이 적절한 방식이라 생각 됩니다.

 주로 저의 경우에는 User와 관련된 정보를 기기에 flutter_secure_storage를 이용해 저장시켜둔뒤 계속 사용하는 방식을 취하고 있습니다. (이 글을 읽으신 분들은 혼자서 json 형식으로 데이터를 저장하고 불러오게 하는 방식을 해보는 건 어떨까 생각을 합니다!!)

로그인 정보가 계속 유지됨을 보여주는 것입니다.

Comments