Flutter 记录应用授权登录踩坑之旅

Flutter 记录应用授权登录踩坑之旅

最近刚刚入门Flutter,不写点代码实在记不住新知识,就用Flutter构建了一个原Objective-C项目,来实现它的第三方授权登录。

引入插件

我本次使用的是ShareSDK,因为OC项目一直用的是ShareSDK,所以就继续维持原平台。

具体使用请下载sdk,参考sdk中的example工程。

pubspec.yaml文件中加入下面依赖

yaml 复制代码
dependencies:
  # MobTech分享
  mobcommonlib: ^1.1.7
  sharesdk_plugin: ^1.3.17

然后执行:flutter packages get 导入 package

或者

bash 复制代码
flutter pub add mobcommonlib sharesdk_plugin

在你的 dart工程文件中,导入下面头文件,就可以开始使用了。

dart 复制代码
import 'package:sharesdk_plugin/sharesdk_plugin.dart';

Tips:不要着急运行项目,iOSAndroid还有配置要添加,如不需要则可以直接跳过。

安装后,iOS端这里是无法运行到模拟器中的

错误表示,微信的SDK无法链接上。ShareSDK使用的xcframework,里面包含了arm64x86_64指令集编译后的库。

解决办法,找到下图所示设置

可以看到Dart项目中只有i386指令集,而i386指令集早已不适用了,向其中添加arm64指令集。

iOS 端集成配置

iOS 端自定义需要导入的分享平台

ios目录下,使用Xcode中打开Runner.xcworkspace工程,找到Pods/Development Pods/sharesdk_plugin/Podsharesdk_plugin.podspec文件,添加或者删除你需要的dependency

ruby 复制代码
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
  s.name             = 'sharesdk_plugin'
  s.version          = '1.1.7'
  s.summary          = 'Flutter plugin for ShareSDK.'
  s.description      = <<-DESC
  ShareSDK is the most comprehensive Social SDK in the world,which share easily with 40+ platforms.
                       DESC
  s.homepage         = 'http://www.mob.com/mobService/sharesdk'
  s.license          = { :file => '../LICENSE' }
  s.author           = { 'Mob' => 'mobproduct@mob.com' }
  s.source           = { :path => '.' }
  s.source_files = 'Classes/**/*'
  s.public_header_files = 'Classes/**/*.h'
  s.dependency 'Flutter'
  s.dependency 'mob_sharesdk'
  s.dependency 'mob_sharesdk/ShareSDKExtension'
  s.dependency 'mob_sharesdk/ShareSDKUI'
  s.dependency 'mob_sharesdk/ShareSDKPlatforms/QQ'
  s.dependency 'mob_sharesdk/ShareSDKPlatforms/SinaWeibo'
  s.dependency 'mob_sharesdk/ShareSDKPlatforms/WeChat'
#  s.dependency 'mob_sharesdk/ShareSDKPlatforms/WeChatFull'#微信sdk带支付的命令
  s.dependency 'mob_sharesdk/ShareSDKPlatforms/Apple'#苹果登录


  s.static_framework = true

  s.ios.deployment_target = '8.0'
end

打开命令行工具,cdios目录下,通过pod命令来安装iOS依赖包。

bash 复制代码
cd ios
pod install

配置初始化我们 SDK 的 AppKey

在项目工程的Info.plist中如图增加MOBAppKeyMOBAppSecret两个字段

配置对应平台的 URL Scheme

打开项目的 Info 选项,然后选择 URL Types,添加对应平台的 URL Scheme 配置,如下图:

配置白名单

1.在项目的info.plist中添加LSApplicationQueriesSchemes,类型为Array

2.然后给它添加一个需要支持的项目,类型为String类型:

配置 ATS

微博平台还需要加上 ATS 配置:

  1. 在项目的info.plist中添加LSApplicationQueriesSchemes,类型为Array

  2. 给它添加一个需要支持的项目,类型为String类型:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>NSAppTransportSecurity</key>
	<dict>
		<key>NSAllowsArbitraryLoads</key>
		<true/>
	</dict>
</dict>
</plist>
网络强制走 https

如果需要强制,在Info.plist配置MOBForceHttpsYES。优先级高于ATS的配置

新浪微博,微信,QQ,line 等平台在 iOS13 上需要校验 Universal Link,之前在微信,QQ,新浪微博,line 上注册应用需要在配置上 Universal Link,另外项目里也要配置上,可以根据 MOB 后台生成的 Universal Link 去配置:

注意: Team id,Bundle id 这些必须要填写的和自己项目里使用的证书的 Team id 和 Bundle id 一致,QQ AppID 是填写 qq 初始化的 appid,如果需要 QQ 平台,那么需要填写上,把信息都填写之后保存了才可以使用我们生成的 Univesal link

在项目里配置,如下图:

点击 Capability,选择 Associated Domains,并双击添加,如下图:

填写上 Universal Link 配置,填写的格式是 applinks:xxxx

注意 :这个 Universal Link 也可以自己生成,可以参考这个 苹果官方文档,但是为了方便用户,节省用户的时间和精力,建议直接拷贝 MOB 生成的配置。

微信、QQ 通用链接最后的/一定不要移除。

微信、QQ 通用链接最后的/一定不要移除。

微信、QQ 通用链接最后的/一定不要移除。

重要的事情说三遍。

苹果登录(Sign in with Apple)配置

根据 苹果审核 指南:如果 app 专门使用第三方或社交登录服务(例如微信登录,QQ 登录,Facebook 登录,Google 登录,Twitter 登录等)来对其进行设置或验证这个 app 的用户主账户,则该 app 必须同时提供"通过 Apple 登录"作为等效选项,用户的主账户时指在 app 中建立的,用于标识身份,登录和访问功能和相关服务的账户。

与通用链接Associated Domains的添加流程一致。

添加完成后,Runner.entitlements中也会出现相关的配置信息。

Android 端集成配置

Android 端的集成配置全凭 文档demo 结合来实现的,由于没有设备调试,所以正确性暂未验证;如有错误请大家指正。

kts 构建脚本配置

配置 SDK 地址

在项目setting.gradle.kts中添加配置

kotlin 复制代码
pluginManagement {
    repositories {
        google {
            content {
                includeGroupByRegex("com\\.android.*")
                includeGroupByRegex("com\\.google.*")
                includeGroupByRegex("androidx.*")
            }
        }
        mavenCentral()
        gradlePluginPortal()
        //Maven仓地址
        maven { url = uri("https://mvn.mob.com/android")}
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
    repositories {
        google()
        mavenCentral()
        //Maven仓地址
        maven { url = uri("https://mvn.mob.com/android")}
    }
}
配置 Maven

android项目根目录下的 build.gradle 中添加以下代码:

java 复制代码
buildscript {
    repositories {
        // 配置Mob Maven库
        maven {
           url "https://mvn.mob.com/android"
        }
        // 配置HMS Core SDK的Maven仓地址。(集成华为厂商需要添加)
        // maven {
        //    url 'https://developer.huawei.com/repo/'}
        // }

    }
    dependencies {
        // 集成MobPush
        //   classpath "cn.fly.sdk:FlySDK:+"
    }
}

或(注意文件名后缀

android项目根目录下build.gradle.kts中添加配置

kotlin 复制代码
buildscript {
    dependencies {
        // 增加MobSDK插件配置
        classpath ("com.mob.sdk:MobSDK2:+")
    }
}

plugins {
    xxxxxx
}
gradle.properties 中添加代码
ini 复制代码
MobSDK.spEdition = FP

如果您的应用需要上架 Google 商店,请务必使用 Google Play 版本。 在gradle.properties中添加代码,如已添加 MobSDK.spEdition相关配置,则修改FPGPP即可

ini 复制代码
MobSDK.spEdition = GPP

第三方平台添加

android根目录下创建MobSDK.gradle文件, 你可以参考 官方 demo中的平台配置信息。

bash 复制代码
apply plugin: 'com.mob.sdk'

MobSDK {
    appKey "moba0b0c0d0"
    appSecret "5713f0d88511f9f4cf100cade0610a34"

    ShareSDK {

        //平台配置信息
        devInfo {
            SinaWeibo {
                id 1
                sortId 1
                appKey "500068354"
                appSecret "0a5f391f938dc371fb44d47a4fa0822e"
                callbackUri "http://www.sharesdk.cn"
                shareByAppClient true
                enable true
            }

            /* Wechat微信和WechatMoments微信朋友圈的appid是一样的;

            注意:开发者不能用我们这两个平台的appid,否则分享不了
            微信测试的时候,微信测试需要先签名打包出apk,
            sample测试微信,要先签名打包,keystore在sample项目中,密码123456

            BypassApproval是绕过审核的标记,设置为true后AppId将被忽略,故不经过
            审核的应用也可以执行分享,但是仅限于分享文字和图片,不能分享其他类型,
            默认值为false。此外,微信收藏不支持此字段。wx4868b35061f87885
            <!--要分享微信小应用程序时用userName,path-->*/
            Wechat {
                id 2
                sortId 2
                appId "wx4868b35061f87885"
                appSecret "64020361b8ec4c99936c0e3999a9f249"
                userName "gh_afb25ac019c9"
                path "pages/index/index.html?id=1"
                withShareTicket true
                miniprogramType 0
                bypassApproval false
                enable true
            }

            QQ {
                id 3
                sortId 3
                appId "100371282"
                appKey "aed9b0303e3ed1e27bae87c33761161d"
                shareByAppClient true
                bypassApproval false
                enable true
            }

        }
    }
}

配置引入

/android/app/build.gradle 中添加以下代码:

java 复制代码
// 导入MobSDK
apply from: '../MobSDK.gradle'

/android/app/build.gradle.kts添加以下代码:

kotlin 复制代码
// kts
apply(from = "../MobSDK.gradle")

配置 AndroidManifest

AndroidManifest 中需要加入配置,将其加到application中,避免mobsdkflutterapplication冲突。

xml 复制代码
tools:replace="android:name"

Flutter 中 API 接口调用

流程图

流程图大致如下:

graph TD Init(全局初始化) --> PlatformCheck{平台判断} PlatformCheck -->|iOS| ShowAppleButton(显示苹果登录按钮) PlatformCheck -->|Android| HideAppleButton(隐藏苹果登录按钮) ShowAppleButton --> SelectAuth(选择登录方式\n 微信/QQ/微博/Apple) HideAppleButton --> SelectAuth SelectAuth --> ClientCheck{客户端已安装?} ClientCheck -->|是| RequestAuth(发起授权请求) ClientCheck -->|否| PromptInstall(提示:请先安装客户端) RequestAuth --> AuthResult{授权结果} AuthResult -->|成功| LoginSuccess(登录成功) AuthResult -->|失败| ErrorPrompt(提示错误) AuthResult -->|未知| ErrorPrompt AuthResult -->|取消| ErrorPrompt ErrorPrompt --> SelectAuth PromptInstall --> SelectAuth

初始化 MobSDK 注册器

如果需要分享内容到三方平台的话,建议放到main.dartrunApp()之前初始化。

我这里只用到了授权登录,所以就放到了login_controller.dart控制器中onInit()初始化。

dart 复制代码
@override
  void onInit() {
    // 初始化MOBShareSDK注册器
    _initMobShareSDKRegister();
    super.onInit();
  }

/// 初始化Mob分享SDK注册器
  void _initMobShareSDKRegister() {
    ShareSDKRegister register = ShareSDKRegister();

    // 微信
    register.setupWechat(
      AppThirdPlatformKey.wechatAppId,
      AppThirdPlatformKey.wechatAppSecret,
      AppThirdPlatformKey.wechatUniversalLink,
    );

    // QQ
    register.setupQQ(AppThirdPlatformKey.qqAppId, AppThirdPlatformKey.qqAppKey);

    // 新浪微博
    register.setupSinaWeibo(
      AppThirdPlatformKey.sinaWeiBoAppKey,
      AppThirdPlatformKey.sinaWeiBoAppSecret,
      AppThirdPlatformKey.sinaWeiBoRedirectUrl,
      AppThirdPlatformKey.sinaWeiBoUniversalLink,
    );

    SharesdkPlugin.regist(register);

    //SharesdkPlugin.uploadPrivacyPermissionStatus(0, getPrivacyPolicy);
    SharesdkPlugin.addRestoreReceiver(_onMobShareSDKEvent, _onMobShareSDKError);

    // 回传用户隐私授权结果
    Mobcommonlib.submitPolicyGrantResult(true, null);
  }

监听的回调

dart 复制代码
/// Mob分享SDK监听回调
  void _onMobShareSDKEvent(dynamic event) {
    debugPrint('>>>>>>>>>>>>>>>>>>>>>>>>>>>');
    Map resMapT = event;
    Map<String, dynamic> resMap = Map<String, dynamic>.from(resMapT);

    // Map<String, dynamic> params = Map<String, dynamic>.from(resMap['params']);
    debugPrint('>>>>>>>>>>>>>>>>>>>>>>>>>>>onSuccess:$resMap');
  }

  /// Mob分享SDK监听错误回调
  void _onMobShareSDKError(dynamic event) {
    debugPrint('>>>>>>>>>>>>>>>>>>>>>>>>>>>onError:$event');
  }

向第三方请求授权

dart 复制代码
/// MOB 第三方授权登录
  void mobThidPartAuthLogin(
    /// 平台
    ShareSDKPlatform platform,

    /// 成功回调
    Function(dynamic user) onSuccess,
  ) {
    // 是否安装了客户端
    SharesdkPlugin.isClientInstalled(platform).then((dynamic hasClient) {
      bool hasInstalled = hasClient as bool;
      if (hasInstalled) {
        // 安装了客户端
        // 请求授权
        mobThirdPartApplyAuthRequest(platform, onSuccess);
      } else {
        // 没有安装客户端
        Fluttertoast.showToast(msg: '请先安装客户端');
      }
    });
  }

  /// MOB 申请三方授权登录请求
  void mobThirdPartApplyAuthRequest(
    /// 平台
    ShareSDKPlatform platform,

    /// 成功回调
    Function(dynamic user) onSuccess,
  ) {
    // 请求授权
    SharesdkPlugin.auth(platform, {}, (
      SSDKResponseState state,
      dynamic user,
      SSDKError error,
    ) {
      switch (state) {
        case SSDKResponseState.Success:
          // 登录成功
          debugPrint('授权登录成功');
          debugPrint('$user');
          onSuccess(user);
          break;
        case SSDKResponseState.Cancel:
          // 取消登录
          debugPrint('授权登录取消');
          Fluttertoast.showToast(msg: '已取消');
          break;
        default:
          // 登录失败
          debugPrint('授权登录失败');
          Fluttertoast.showToast(msg: '授权登录失败');
          debugPrint('${error.rawData}');
          break;
      }
    });
  }

获取授权信息

这里是封装了三方平台登录按钮点击,最后通过onSuccess回调的user授权信息,最后与后端沟通保存授权登录信息,一般是credential中的tokenuid字段。

dart 复制代码
/// 三方登录按钮点击事件
  void thirdPartyButtonClick(ThirdPartLoginType loginType) {
    if (!isAgreement.value) {
      Get.snackbar('提示', '请先同意协议');
      return;
    }
    ShareSDKPlatform platform;
    switch (loginType) {
      case ThirdPartLoginType.Wechat:
        platform = ShareSDKPlatforms.wechatSession;
        break;
      case ThirdPartLoginType.QQ:
        platform = ShareSDKPlatforms.qq;
        break;
      case ThirdPartLoginType.Sina:
        platform = ShareSDKPlatforms.sina;
        break;
      case ThirdPartLoginType.Apple:
        platform = ShareSDKPlatforms.apple;
        break;
    }
    // 申请授权
    mobThidPartAuthLogin(platform, (user) {
      // 授权成功
      saveMobThirdPartyAuthLoginInfo(platform, user);
    });
  }

  /// 保存三方授权信息
  ///
  /// [platform] 平台
  /// [user] 授权成功的用户相关信息
  void saveMobThirdPartyAuthLoginInfo(
    /// 平台
    ShareSDKPlatform platform,

    /// 授权成功的用户相关信息
    dynamic user,
  ) {
    Map<String, dynamic> params = {};
    if (platform == ShareSDKPlatforms.wechatSession) {
      params['accessToken'] = user['credential']['token'];
      params['wxOpenId'] = user['credential']['uid'];
      params['loginType'] = ThirdPartLoginType.Wechat.value;
    } else if (platform == ShareSDKPlatforms.qq) {
      params['accessToken'] = user['credential']['token'];
      params['loginType'] = ThirdPartLoginType.QQ.value;
    } else if (platform == ShareSDKPlatforms.apple) {
      params['accessToken'] = user['credential']['token'];
      params['loginType'] = ThirdPartLoginType.Apple.value;
    } else if (platform == ShareSDKPlatforms.sina) {
      params['accessToken'] = user['credential']['token'];
      params['loginType'] = ThirdPartLoginType.Sina.value;
    }

    // 发起请求
    LoginDioApis.thirdPartyAuthLogin(
      params: params,
      onSuccess: (loginResultModel) => _handleLoginSuccess(loginResultModel),
      onError: (apiServerException) => _handleLoginError(apiServerException),
    );
  }

  /// 登录成功回调
  void _handleLoginSuccess(LoginResultModel loginResultModel) {
    // 登录成功
    Get.snackbar('登录成功', '欢迎您,${loginResultModel.userInfo?.nickName}');
    Get.offAllNamed('/home');
  }

  /// 登录失败回调
  void _handleLoginError(ApiServerException apiServerException) {
    Fluttertoast.showToast(msg: '登录失败!');
  }

参考链接

ShareSDK-Flutter 插件 集成配置

ShareSDK-Android SDK 集成指南

ShareSDK-iOS SDK 集成指南

iOS 端 UniversalLink 生成最佳实践

Android 端 kts 构建脚本配置实现

苹果登录最佳实现

URL Scheme 全平台配置说明

白名单全平台配置说明

相关推荐
子榆.1 小时前
Flutter 与开源鸿蒙(OpenHarmony)工程化实践:CI/CD、性能监控与多端发布
flutter·开源·harmonyos
QuantumLeap丶1 小时前
《Flutter全栈开发实战指南:从零到高级》- 26 -持续集成与部署
android·flutter·ios
晚烛4 小时前
实战前瞻:构建高安全、强协同的 Flutter + OpenHarmony 智慧金融移动银行平台(支持国产密码体系、生物认证与信创全栈适配)
安全·flutter·金融
子榆.4 小时前
Flutter 与开源鸿蒙(OpenHarmony)国际化与无障碍适配指南:打造真正包容的跨平台应用
flutter·华为·开源·harmonyos
子榆.5 小时前
Flutter 与开源鸿蒙(OpenHarmony)深度集成:从原理到实战进阶
flutter·华为·开源·harmonyos
子榆.6 小时前
Flutter 与开源鸿蒙(OpenHarmony)的融合:跨平台开发新纪元
flutter·华为·开源·harmonyos
走在路上的菜鸟6 小时前
Android学Dart学习笔记第二十三节 类-扩展类型
android·笔记·学习·flutter
晚烛6 小时前
智启工厂脉搏:基于 OpenHarmony + Flutter 的信创工业边缘智能平台构建实践
前端·javascript·flutter
爱吃大芒果6 小时前
Flutter 表单开发实战:表单验证、输入格式化与提交处理
开发语言·javascript·flutter·华为·harmonyos
光影少年6 小时前
RN vs Flutter vs Expo 选型
前端·flutter·react native