Flutter三方库适配OpenHarmony【flutter_web_auth】— URI 解析与 Scheme 匹配算法

前言

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net

onNewWant 收到一个 Want 对象,里面有一个 uri 字符串。插件需要从这个字符串里提取出 Scheme,然后在 callbacks Map 中找到对应的 MethodResult。听起来简单,但字符串解析总有各种边界情况——空值、格式错误、编码问题。这篇把 URI 解析的每个细节都过一遍。

一、onNewWant 中 want.uri 的获取

1.1 代码

staticonNewWant(want: Want):void{const uri = want.uri;if(!uri){console.info(TAG,'onNewWant: no uri in want');return;}// ...}

1.2 want.uri 的来源

浏览器重定向到 myapp://callback?code=abc123 ↓ 系统创建 Want 对象 ↓ want = { action: 'ohos.want.action.viewData', uri: 'myapp://callback?code=abc123', // ... } ↓ EntryAbility.onNewWant(want) ↓ FlutterWebAuthPlugin.onNewWant(want) ↓ want.uri → "myapp://callback?code=abc123" 

1.3 uri 可能为空的场景

场景want.uri处理
正常深度链接"myapp://callback?code=abc"继续处理
非深度链接的 Wantundefined静默返回
空字符串""!uri 捕获
系统内部 Wantundefined静默返回

1.4 空值判断

if(!uri){console.info(TAG,'onNewWant: no uri in want');return;}

!uri 同时处理了 undefinednull 和空字符串三种情况。这是 JavaScript/ArkTS 的 falsy 值特性。

二、indexOf(‘😕/’) 手动解析 Scheme

2.1 代码

const schemeEnd = uri.indexOf('://');if(schemeEnd <0){return;}const scheme = uri.substring(0, schemeEnd);

2.2 解析过程

uri = "myapp://callback?code=abc123" │ │ 0 5 ← indexOf('://') = 5 scheme = uri.substring(0, 5) = "myapp" 

2.3 为什么手动解析而不用 URL 类

// 方案1:手动解析(当前实现)const schemeEnd = uri.indexOf('://');const scheme = uri.substring(0, schemeEnd);// 方案2:使用 URL 类const url =newURL(uri);const scheme = url.protocol.replace(':','');// "myapp:" → "myapp"
方案优点缺点
手动解析简单、无依赖、不会抛异常不处理复杂 URI
URL 类标准、处理各种格式自定义 Scheme 可能抛异常
💡 手动解析更安全new URL("myapp://callback") 在某些环境下可能抛异常,因为 myapp 不是标准的 URL 协议。手动用 indexOf 不会有这个问题。

2.4 indexOf 返回 -1 的情况

if(schemeEnd <0){return;// URI 中没有 "://",不是有效的深度链接}
URIindexOf(‘😕/’)处理
myapp://callback5继续
https://example.com5继续(但不会匹配 callbacks)
myapp:callback-1返回
just-a-string-1返回
://no-scheme0scheme = “”,不会匹配

三、Scheme 匹配逻辑

3.1 代码

if(FlutterWebAuthPlugin.callbacks.has(scheme)){const pendingResult: MethodResult = FlutterWebAuthPlugin.callbacks.get(scheme)as MethodResult; FlutterWebAuthPlugin.callbacks.delete(scheme); pendingResult.success(uri);console.info(TAG,`Resolved callback for scheme: ${scheme}`);}else{console.info(TAG,`No pending callback for scheme: ${scheme}`);}

3.2 匹配流程

scheme = "myapp" │ ├── callbacks.has("myapp")? │ │ │ ├── YES │ │ ├── get("myapp") → pendingResult │ │ ├── delete("myapp") → 从 Map 中移除 │ │ └── pendingResult.success(uri) → 返回完整 URI │ │ │ └── NO │ └── 日志记录,静默忽略 │ └── 完成 

3.3 返回完整 URI 而不是只返回参数

pendingResult.success(uri);// 返回 "myapp://callback?code=abc123&state=xyz"// 而不是只返回 "code=abc123&state=xyz"

Dart 层负责解析返回的 URI:

final result =awaitFlutterWebAuth.authenticate(...);// result = "myapp://callback?code=abc123&state=xyz"final code =Uri.parse(result).queryParameters['code'];final state =Uri.parse(result).queryParameters['state'];
📌 返回完整 URI 是更好的设计——Dart 层可以用 Uri.parse() 方便地提取任何参数,不需要原生层预先知道有哪些参数。

四、多 Scheme 并发场景

4.1 正常场景

// 两个不同 Scheme 的认证final google =FlutterWebAuth.authenticate( url: googleAuthUrl, callbackUrlScheme:"com.google.app",);final github =FlutterWebAuth.authenticate( url: githubAuthUrl, callbackUrlScheme:"com.github.app",);
callbacks Map: { "com.google.app" → result1, "com.github.app" → result2, } 

4.2 回调匹配

onNewWant: uri = "com.google.app://callback?code=google123" → scheme = "com.google.app" → callbacks.has("com.google.app") → YES → result1.success("com.google.app://callback?code=google123") onNewWant: uri = "com.github.app://callback?code=github456" → scheme = "com.github.app" → callbacks.has("com.github.app") → YES → result2.success("com.github.app://callback?code=github456") 

每个 Scheme 独立匹配,互不干扰。

4.3 同 Scheme 并发(冲突)

// 两次使用相同 Scheme(不推荐)final first =FlutterWebAuth.authenticate(url: url1, callbackUrlScheme:"myapp");final second =FlutterWebAuth.authenticate(url: url2, callbackUrlScheme:"myapp");
callbacks.set("myapp", result1); // 第一次 callbacks.set("myapp", result2); // 第二次,覆盖了 result1! // result1 丢失,first Future 永远不会 complete // 直到 cleanUpDanglingCalls 清理 
结果说明
first永远不会 complete(直到 cleanUpDanglingCalls)
second正常工作

五、URL 编码与特殊字符

5.1 常见的编码问题

原始 URI: myapp://callback?name=张三&token=abc+def 编码后: myapp://callback?name=%E5%BC%A0%E4%B8%89&token=abc%2Bdef 

5.2 对 Scheme 提取的影响

const uri ="myapp://callback?name=%E5%BC%A0%E4%B8%89";const schemeEnd = uri.indexOf('://');// 5const scheme = uri.substring(0, schemeEnd);// "myapp"

URL 编码不影响 Scheme 部分——Scheme 只包含 ASCII 字母、数字和少数特殊字符,不需要编码。

5.3 对参数提取的影响

// Dart 层final result ="myapp://callback?name=%E5%BC%A0%E4%B8%89";final uri =Uri.parse(result);final name = uri.queryParameters['name'];// "张三"(自动解码)

Uri.parse() 会自动处理 URL 编码,开发者不需要手动解码。

5.4 Fragment(#)的处理

隐式模式的回调可能使用 fragment: myapp://callback#access_token=abc123&token_type=bearer 
// Dart 层处理 fragmentfinal result ="myapp://callback#access_token=abc123";final uri =Uri.parse(result);final token = uri.fragment.split('&').map((e)=> e.split('=')).firstWhere((e)=> e[0]=='access_token')[1];// token = "abc123"
参数位置格式Dart 提取方式
Query?key=valueuri.queryParameters['key']
Fragment#key=value手动解析 uri.fragment

六、安全性考虑

6.1 Scheme 劫持

攻击场景: 1. 恶意 App 也注册了 myapp:// Scheme 2. 用户完成认证后,系统可能把回调发给恶意 App 3. 恶意 App 获取了 authorization code 

6.2 防御措施

措施说明实现
使用反向域名 Scheme降低冲突概率com.example.myapp
PKCE即使 code 被截获也无法使用需要 code_verifier
State 参数验证回调的合法性随机字符串比对
// 使用 PKCE + State 的安全认证import'dart:math';import'dart:convert';import'package:crypto/crypto.dart';final codeVerifier =_generateCodeVerifier();final codeChallenge =_generateCodeChallenge(codeVerifier);final state =_generateRandomString(32);final url =Uri.https('auth.example.com','/authorize',{'response_type':'code','client_id': clientId,'redirect_uri':'$callbackUrlScheme:/','code_challenge': codeChallenge,'code_challenge_method':'S256','state': state,});final result =awaitFlutterWebAuth.authenticate( url: url.toString(), callbackUrlScheme: callbackUrlScheme,);// 验证 statefinal returnedState =Uri.parse(result).queryParameters['state'];if(returnedState != state){throwException('State mismatch! Possible CSRF attack.');}
📌 PKCE 和 State 参数是 flutter_web_auth 使用者应该实现的安全措施,不是插件本身的功能。插件只负责传递 URL 和接收回调。

七、与其他解析方案的对比

7.1 三种解析方案

// 方案1:indexOf(当前实现)const scheme = uri.substring(0, uri.indexOf('://'));// 方案2:正则表达式const match = uri.match(/^([a-z][a-z0-9+.-]*):/);const scheme = match ? match[1]:null;// 方案3:URL 类const scheme =newURL(uri).protocol.slice(0,-1);

7.2 对比

方案性能健壮性复杂度
indexOf最快最低
正则
URL 类最慢最高

当前的 indexOf 方案在性能和简洁性之间取得了好的平衡。对于 flutter_web_auth 的场景(URI 格式可预测),这个方案足够了。

总结

本文详细分析了 flutter_web_auth 的 URI 解析与 Scheme 匹配:

  1. want.uri 获取:空值判断覆盖 undefined/null/空字符串
  2. indexOf 解析:简单高效,不依赖 URL 类
  3. Scheme 匹配:从 callbacks Map 中查找对应的 MethodResult
  4. URL 编码:不影响 Scheme 提取,Dart 层自动解码参数
  5. 安全性:PKCE + State 防御 Scheme 劫持

下一篇我们讲错误处理——NO_CONTEXT、LAUNCH_FAILED、CANCELED 三种错误的完整梳理。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!

相关资源:

在这里插入图片描述

URI 语法结构图(来自 Wikipedia)

Read more

实测可用!发那科机器人与西门子PLC通讯全方案(网关+Modbus TCP双版本,避坑指南附代码)

实测可用!发那科机器人与西门子PLC通讯全方案(网关+Modbus TCP双版本,避坑指南附代码) 在工业自动化现场,发那科(FANUC)机器人与西门子PLC的组合十分常见,但两者“协议壁垒”常常让工程师头疼——发那科机器人原生支持EtherNet/IP,而西门子PLC(S7-1200/1500)主打Profinet,直接通讯往往“语言不通”。 本文结合3个实际产线项目经验,整理两种经过现场验证、100%可用的通讯方案(网关跨协议版 + Modbus TCP低成本版),步骤拆解到每一步按键操作,标注新手常踩的坑,附PLC测试代码和故障排查方法,适合工控工程师直接照搬落地,再也不用为通讯调试熬夜! 核心前提(避免做无用功) * 发那科机器人:支持EtherNet/IP或Modbus TCP功能(需确认系统选件,无选件需联系厂家授权,如Modbus TCP需R602选件),本文以R-30iB系列为例。 * 西门子PLC:S7-1200/S7-1500(本文分型号适配步骤),安装**TIA

若依(RuoYi)低代码框架全面分析

若依(RuoYi)低代码框架全面分析

文章目录 * 一、框架概述与技术背景 * 技术架构全景 * 二、核心特长分析 * 1. 完备的权限管理体系 * 2. 高度模块化的系统设计 * 3. 强大的代码生成器 * 4. 丰富的功能组件 * 三、显著短板与局限性 * 1. 技术栈相对保守 * 2. 代码生成器的局限性 * 3. 性能瓶颈与扩展性挑战 * 4. 学习曲线与定制成本 * 四、实际应用场景分析 * 适合场景 * 不适用场景 * 五、与其他框架对比 * 六、总结与展望 一、框架概述与技术背景 若依(RuoYi)是基于Spring Boot的权限管理系统,是中国Java低代码领域的代表性开源框架。其名称"若依"取自"若你"的谐音,体现了"

B站直播神器:神奇弹幕机器人完整使用教程

B站直播神器:神奇弹幕机器人完整使用教程 【免费下载链接】Bilibili-MagicalDanmaku【神奇弹幕】哔哩哔哩直播万能场控机器人,弹幕姬+答谢姬+回复姬+点歌姬+各种小骚操作,目前唯一可编程机器人 项目地址: https://gitcode.com/gh_mirrors/bi/Bilibili-MagicalDanmaku 想要打造一个高互动、自动化的B站直播间吗?神奇弹幕作为目前唯一可编程的B站直播机器人,能够帮你实现弹幕互动、礼物答谢、智能点歌等多种功能,让你的直播变得更加高效和专业。无论你是新手主播还是经验丰富的UP主,这个工具都能为你节省大量时间精力。 🎯 核心功能深度解析 智能弹幕管理系统 神奇弹幕机器人提供了完整的弹幕管理解决方案。通过主控制台界面,你可以轻松管理直播间的各项设置,包括修改标题、封面、公告等基础信息。 在弹幕姬功能模块中,你可以配置显示时长、弹幕发送字数限制,开启自动重试功能,甚至设置弹幕翻译和新人提示等智能互动选项。 自动化答谢与互动系统 根据观众不同身份,机器人能够智能设置专属欢迎语: 观众类型欢迎语示例特色功

无人机遥感航拍巡检数据集 无人机遥感图像识别 无人机视角山区泥石流和滑坡图像识别数据集-数据集第10067期

无人机遥感航拍巡检数据集 无人机遥感图像识别 无人机视角山区泥石流和滑坡图像识别数据集-数据集第10067期

滑坡检测数据集核心信息介绍 ** 这个滑坡检测数据集主要用于目标检测任务,整体数据规模和细节都比较明确。从数量上看,数据集总共包含 1660 张图像, 往期热门主题 主题搜两字"关键词"直达 代码数据获取: 获取方式:***文章底部卡片扫码获取*** 覆盖了YOLO相关项目、OpenCV项目、CNN项目等所有类别, 覆盖各类项目场景(包括但不限于以下----欢迎咨询定制): 项目名称项目名称基于YOLO+deepseek 智慧农业作物长势监测系统基于YOLO+deepseek 人脸识别与管理系统基于YOLO+deepseek 无人机巡检电力线路系统基于YOLO+deepseek PCB板缺陷检测基于YOLO+deepseek 智慧铁路轨道异物检测系统基于YOLO+deepseek 102种犬类检测系统基于YOLO+deepseek 人脸面部活体检测基于YOLO+deepseek 无人机农田病虫害巡检系统基于YOLO+deepseek 水稻害虫检测识别基于YOLO+deepseek 安全帽检测系统基于YOLO+deepseek 智慧铁路接触网状态检测系统基于YOLO+