为 Flutter 实现 Facebook 身份验证

身份验证是一个安全过程,用户必须提供他们的身份才能访问特定的应用程序、系统或一段特定的信息或资源。在计算机安全方面,身份验证是验证用户身份的过程或动作。如果没有身份验证,计算机网络就容易受到攻击,黑客可能想要获得对机密信息或资源的访问权限。

Flutter为我们提供了一个名为 的包flutter_facebook_auth,我们可以在其中轻松地将 Facebook 登录添加到我们的 Flutter 应用程序中。它为我们提供了很多功能,包括:

  • 在 IOS、Android 和 Web 上登录
  • 在 Android 上快速登录
  • 提供访问令牌以向 Graph API 发出请求等等。

如需完整文档,请访问此页面

在本文中,我们将设置一个应用程序来展示如何在我们的 Flutter 应用程序中使用这个包。

创建我们的 Flutter 应用程序

要开始,请前往GitHub并克隆为此项目创建的启动文件。打开pubspec.yaml文件并在终端flutter_facebook_auth下添加cupertino_icons或运行flutter pub add flutter_facebook_auth

name: facebook_auth
description: A new Flutter project.

publish_to: `none 

version: 1.0.0+1

environment:
  sdk: ">=2.17.6 <3.0.0"

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.2
  flutter_facebook_auth: ^4.4.0+1 # Add This Line

dev_dependencies:
  flutter_test:
    sdk: flutter

  flutter_lints: ^2.0.0

flutter:
  uses-material-design: true

向我们的应用程序添加依赖项

在向我们的应用程序添加功能之前,我们需要配置项目根文件以授予我们访问 Facebook 登录的权限。在您的浏览器上,前往Facebook 开发者创建一个 Facebook 开发者帐户并开始创建我们的应用程序。

单击登录按钮。这将提示您使用您的 Facebook 详细信息登录。

输入您的登录详细信息,然后点击登录按钮继续。

点击My Apps,我们就可以开始在 Facebook 上创建我们的应用了。

在这里,我们没有创建应用程序。点击 Create App 按钮,这样我们就可以开始创建我们的应用程序了

在这里,我们可以选择任何应用类型。我将为本教程选择业务并点击下一步。

添加您选择的显示名称,然后点击创建应用程序以继续。系统可能会提示您输入密码,输入密码,然后单击提交。

在这里您可以看到我们的应用程序已经创建。我们在左上角有我们的应用程序名称和我们的应用程序 ID。单击Facebook Login以便我们可以在我们的应用程序上设置 Facebook 登录功能。

我将在本教程的 android 平台上工作。单击 Android 徽标继续。

在您的project-level > build.gradle文件中将代码添加到buildscript{repositories{ }}

buildscript {
    ext.kotlin_version = '1.6.10'
    repositories {
        google()
        mavenCentral() // Add Here
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:7.1.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

allprojects {
    repositories {
        google()
        mavenCentral() // Add Here
    }
}

rootProject.buildDir = '../build'
subprojects {
    project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
    project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

在您的app-level>build.gradle文件中,将代码添加到依赖项部分。

def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
    compileSdkVersion flutter.compileSdkVersion
    ndkVersion flutter.ndkVersion

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = '1.8'
    }

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.example.facebook_auth"
        // You can update the following values to match your application needs.
        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
        minSdkVersion flutter.minSdkVersion
        targetSdkVersion flutter.targetSdkVersion
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }

    buildTypes {
        release {
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.debug
        }
    }
}

flutter {
    source '../..'
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.facebook.android:facebook-android-sdk:latest.release' // Add This Line Here.
}

如果您已成功完成,请单击下一步。

在第 3 步中,输入Package Name和 ,Default Activity Class Name如上所示。您可以通过转到android>app>build.gradleFlutter 项目中的文件来获取包名称。

点击保存以继续设置应用程序。

在第 4 步中,我们需要生成一个开发密钥哈希,以确保我们的应用程序与 Facebook 之间交互的真实性。我在 Windows 机器上,所以我将单击openssl-for-windows并下载并解压缩特定于我的 PC(32 位或 64 位)的 zip 文件。复制下面的代码并将其粘贴到您的终端上。

keytool -exportcert -alias androiddebugkey -keystore "C:\Users\USERNAME\.android\debug.keystore" | "PATH_TO_OPENSSL_LIBRARY\bin\openssl" sha1 -binary | "PATH_TO_OPENSSL_LIBRARY\bin\openssl" base64

将 USERNAME 更改为您自己的并将路径添加到openss\bin文件夹。就我而言,我有这个:

在这里,我将用户名更改为我的系统用户并添加了openssl\bin文件夹的路径。当提示输入密码时,使用android. 这将生成一个密钥供您复制并粘贴到您网页的“密钥哈希”部分。

点击保存并继续。

第五步,启用单点登录,点击保存并下一步。

在第 6 步中,我们将编辑资源和清单文件。在您的 上app>src>main>res>values,创建一个string.xml文件并添加以下代码:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">App Name</string>
    <string name="facebook_app_id">App ID</string>
    <string name="fb_login_protocol_scheme">Login Protocol Scheme</string>
    <string name="facebook_client_token">Client Token</string>
</resources>

您facebook_app_id可以在浏览器的上方找到,并且您fb_login_protocol_scheme是带有前缀的应用程序 ID fb(例如,如果您的应用程序 ID 为1234,则您的协议方案将为fb1234)。要获取您的facebook_client_token,在您的仪表板上,导航到Settings>Advanced>Security>Client Token。就我而言:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Auth Tutorial</string>
    <string name="facebook_app_id">1924520271082903</string>
    <string name="fb_login_protocol_scheme">fb1924520271082903</string>
    <string name="facebook_client_token">ab808c0a71a13009bcf8792433ff6b94</string>
</resources>

最后,转到您的AndroidManifest.xml文件并复制以下代码:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.facebook_auth">
    
    <!-- FACEBOOK CONFIGURATIONS -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <!-- FACEBOOK CONFIGURATIONS ENDS HERE -->  


    <application
        android:label="facebook_auth"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
        
            <!-- FACEBOOK CONFIGURATIONS -->
        <meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id"/>
        <meta-data android:name="com.facebook.sdk.ClientToken" android:value="@string/facebook_client_token"/>

        <activity android:name="com.facebook.FacebookActivity"
            android:configChanges=
                "keyboard|keyboardHidden|screenLayout|screenSize|orientation"
            android:label="@string/app_name" />
        <activity
            android:name="com.facebook.CustomTabActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="@string/fb_login_protocol_scheme" />
            </intent-filter>
        </activity>
        <!--FACEBOOK CONFIGURATION ENDS HERE -->
    </application>
</manifest>

注意:我评论了您要复制和粘贴的行。您已完成与 Facebook 相关的所有任务。接下来,我们将开始向我们的应用程序添加功能。

开源会话重播

OpenReplay是一个开源的会话重播套件,可让您查看用户在您的 Web 应用程序上所做的事情,从而帮助您更快地解决问题。OpenReplay 是自托管的,可以完全控制您的数据。

开始享受您的调试体验 -开始免费使用 OpenReplay

添加功能

平台配置完成后,让我们设置我们的应用程序。在我们的 main.dart 文件中,我们将向按钮添加功能。在onPressed参数上,添加以下代码。

Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => const UserScreen(),
                ),
              );

UserScreen()上面的代码允许我们在单击按钮时移动到下一个屏幕( )。user_screen.dart在 lib 文件夹中创建一个名为的新 dart 文件。该文件将用于显示从 Facebook 获取的用户数据。

// user.dart

import 'package:flutter/material.dart';
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';

class UserScreen extends StatefulWidget {
  const UserScreen({Key? key}) : super(key: key);

  @override
  State<UserScreen> createState() => _UserScreenState();
}

class _UserScreenState extends State<UserScreen> {
  Map<String, dynamic>? _userData;
  AccessToken? _accessToken;
  bool? _checking = true;

  _ifUserIsLoggedIn() async {
    final accessToken = await FacebookAuth.instance.accessToken;

    setState(() {
      _checking = false;
    });

    if (accessToken != null) {
      final userData = await FacebookAuth.instance.getUserData();
      _accessToken = accessToken;
      setState(() {
        _userData = userData;
      });
    } else {
      _login();
    }
  }

  _login() async {
    final LoginResult loginResult = await FacebookAuth.instance.login();

    if (loginResult.status == LoginStatus.success) {
      _accessToken = loginResult.accessToken;
      final userInfo = await FacebookAuth.instance.getUserData();
      _userData = userInfo;
    } else {
      print('ResultStatus: ${loginResult.status}');
      print('Message: ${loginResult.message}');
    }
  }

  _logOut() async {
    await FacebookAuth.instance.logOut();
    _accessToken = null;
    _userData = null;
  }

  @override
  void initState() {
    super.initState();
    _ifUserIsLoggedIn();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: _checking!
            ? const Center(
                child: CircularProgressIndicator(),
              )
            : Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    const Text('Welcome'),
                    _userData != null
                        ? Text(
                            '${_userData!['name']}',
                            style: TextStyle(
                                fontWeight: FontWeight.bold, fontSize: 28),
                          )
                        : Container(),
                    _userData != null
                        ? Container(
                            child: Image.network(
                                _userData!\['picture'\]['data']['url']),
                          )
                        : Container(),
                    const SizedBox(
                      height: 20,
                    ),
                    ElevatedButton(
                      onPressed: () {
                        _logOut();
                        Navigator.pop(context);
                      },
                      child: const Text('Log Out'),
                    ),
                  ],
                ),
              ));
  }
}

让我们分解上面的代码。

Map<String, dynamic>? _userData;
  AccessToken? _accessToken;
  bool? _checking = true;

在上面的代码中,我们有三个可为空的变量:

  • _userData: 登录时保存用户信息。
  • _accessToken:登录时会自动生成访问令牌;此变量将为我们保存该访问令牌。
  • _checking:这将检查用户之前是否登录过。
_ifUserIsLoggedIn() async {
    final accessToken = await FacebookAuth.instance.accessToken;

    setState(() {
      _checking = false;
    });

    if (accessToken != null) {
      final userData = await FacebookAuth.instance.getUserData();
      _accessToken = accessToken;
      setState(() {
        _userData = userData;
      });
    } else {
      _login();
    }
  }

  _login() async {
    final LoginResult loginResult = await FacebookAuth.instance.login();

    if (loginResult.status == LoginStatus.success) {
      _accessToken = loginResult.accessToken;
      final userInfo = await FacebookAuth.instance.getUserData();
      _userData = userInfo;
    } else {
      print('ResultStatus: ${loginResult.status}');
      print('Message: ${loginResult.message}');
    }
  }

  _logOut() async {
    await FacebookAuth.instance.logOut();
    _accessToken = null;
    _userData = null;
  }

查看上面的代码,我们有三个异步函数:

  • _ifUserIsLoggedIn():这将检查用户是否已登录并获取访问令牌和用户数据。
  • _login():登录函数让我们登录,如果登录状态为成功,它应该获取用户数据和生成的访问令牌。
  • _logOut():这会将用户数据和访问令牌设置为 null,然后将我们带回第一页。
Scaffold(
        body: _checking!
            ? const Center(
                child: CircularProgressIndicator(),
              )
            : Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    const Text('Welcome'),
                    _userData != null
                        ? Text(
                            '${_userData!['name']}',
                            style: TextStyle(
                                fontWeight: FontWeight.bold, fontSize: 28),
                          )
                        : Container(),
                    _userData != null
                        ? Container(
                            child: Image.network(
                                _userData!\['picture'\]['data']['url']),
                          )
                        : Container(),
                    const SizedBox(
                      height: 20,
                    ),
                    ElevatedButton(
                      onPressed: () {
                        _logOut();
                        Navigator.pop(context);
                      },
                      child: const Text('Log Out'),
                    ),
                  ],
                ),
              ));

上面的代码在屏幕上显示了我们的值。我们使用三元运算符,它是 dart 中简化的 if 和 else 语句。它检查变量的值_checking是否为真,然后显示进度指示器;否则,显示用户数据。我们还有一个注销按钮,可以使用该_logOut功能并将我们带回主屏幕。

测试应用程序

完成所有配置和设置后,让我们测试我们的应用程序。使用终端中的命令在模拟器或实际设备上运行应用程序。该命令flutter run将构建应用程序并将其安装在您的设备上。

https://blog.openreplay.com/7105fbcdb9ac741da805612f9533d7d2/video.mp4

结论

我们的应用程序需要身份验证。没有它,您会使数据容易受到破坏和未经授权的访问。

这是GitHub 源代码的链接

来源:https://blog.openreplay.com/implementing-facebook-authentication-for-flutter

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容