大家好,我是 17。

Flutter WebView 总共写了四篇文章

  1. 在 Flutter 中运用 webview_flutter 4.0 | js 交互
  2. Flutter WebView 性能优化,让 h5 像原生页面相同优异,已当选 一周 2023.02.22 期
  3. Flutter WebView 怎么与 h5 同步登录状况
  4. 在 Flutter 中运用 webview_flutter 4.0 | 根底用法与事件处理

本篇是第 3 篇,讲下 Flutter WebView 与 h5 怎么同步状况。

缘起

本来是没有这一篇的,第 2 篇 发出来后,小伙伴 枉费 发评论说:敢问下篇是不是该轮到cookie了!

Flutter WebView 如何与 h5 同步登录状态

所以就有了这篇文章。cookie 的内容也不少,或许是前端做的久了,感觉 cookie 是理所当然的工作,所以才有不知道写什么的感觉。 尽管比上星期轻松多了,但也又花了一个周末!我仍是想极力写好,尽量点到每个重要的知识点。17 想写的有些颜色,不能仅满意于会同步设置 token,还需求了解怎么保障 token 的安全,这样会显现比较专业。

cookie 简介

为什么要介绍 cookie

cookie 关于前端小伙伴来说 cookie 再熟悉不过了,但其它端假如没有用过类似的东西不知道也很正常。尽管网上的资料许多,可是关于新手来说,仍是无从分辨的,不知道应该看哪些内容,我就一起都说了吧。

cookie 定义

HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据。浏览器会存储 cookie 并在下次向同一服务器再发起恳求时带着并发送到服务器上。一般,它用于告知服务端两个恳求是否来自同一浏览器——如坚持用户的登录状况。Cookie 使基于无状况的 HTTP 协议记录稳定的状况信息成为了或许。

HTTP 是无状况的:在同一个连接中,两个履行成功的恳求之间是没有关系的。这就带来了一个问题,用户没有办法在同一个网站中进行连续的交互,比方在一个电商网站里,用户把某个产品加入到购物车,切换一个页面后再次添加了产品,这两次添加产品的恳求之间没有相关,浏览器无法知道用户最终挑选了哪些产品。而运用 HTTP 的标头扩展,HTTP Cookie 就能够处理这个问题。把 Cookie 添加到标头中,创建一个会话让每次恳求都能同享相同的上下文信息,达成相同的状况。

留意,HTTP 实质是无状况的,运用 Cookie 能够创建有状况的会话。

以上内容节选自 MDN

保存在本地的意思是保存在本地的磁盘上,当你关闭浏览器甚至关机都不要紧,下次翻开浏览器的时分它还在。由于每次都会随域名主动发送,cookie 不宜太大,一般不超越 4KB。

token 的效果

token 的效果我举个例子,便是我去你家串门,第一次到你家,你不认识我,问:你是谁?我回答:我是 17。然后你让我进门了。第二次去你家,你仍是不认识我,由于每次串门都是完全独立的,能够当作从没来过你家。你仍旧问:你是谁?我回答:我是 17,你又让我进门了。假如我经常去你家串门每次都问仍是很费事的。所以你就给我了一个能证明我身份的令牌(token),我再次到你家串门的时分,直接拿出令牌,令牌上或许是这样写的:我是 17,咱们见过的。你一看,啊,原来是 17 啊,快请进。

token 就适当所以这样的令牌。一个用户在网站的一个页面中登录了,他的登录信息会保存在 cookie 里,在拜访下一个页面的时分,浏览器会带上 cookie 的信息向服务器恳求页面,服务器根据 cookie 的信息就知道你是否登录了。 token 适当于一把钥匙,能开锁。经过 token 无法反解出用户名和暗码。暗码不要直接放在 cookie 中。

cookie 能够有许多 key value,token 仅仅这众多的 key value 中的一个

Flutter WebView 中 cookie 的基本操作。

在 webview_flutter 4 中有 cookieManager 专门用来办理 WebView 中的 cookie,咱们先来学习下用法。

用 WebViewCookie 设置 cookie

 cookieManager.setCookie(
      const WebViewCookie(
        name: 'IAM17',
        value: 'FE',
        domain: '',
      ),
  );

这样就行了,很简略吧。还有一个参数 path,默许是 ‘/’。 setCookie 返回的是 Future<void>

用 cookieManager 铲除 cookie

cookieManager.clear() 这个就太简略了。直接铲除就好了。需求留意的是,这个办法杀伤力有点大,是无差别进犯,所有 WebView 实例中的 cookie 都会被铲除,慎用!。

cookieManager 的才能也就到此为止了,即不能读 cookie,也不能删去单条 cookie,剩下的还盼望全能的 javascript。

用 js 设置 cookie

尽管 cookieManager.setCookie 的办法很好用,但有的时分,你或许需求用 js 来设置 cookie。

最简略的只需一个key,一个value,这样设置的 cookie 是一个会话 cookie,浏览器关闭后 cookie 失效。

var key='name',value='IAM17';
document.cookie = `${key}=${encodeURIComponent(value)}`;

要想在一段时间内有用,加上 expires 参数。

var key='name',value='IAM17';
var now = new Date();
// 设置一天有用期
now.setTime(now.getTime() + 1000*60*60*24);
var expires=now.toGMTString();
document.cookie = `${key}=${encodeURIComponent(value)};expires=${expires}`;

设置 cookie 在整个网站有用

var key='name',value='IAM17';
document.cookie = `${key}=${encodeURIComponent(value)};path=/`;

还有三个与安全相关的参数,了解下意义,后边会详细讲。

  • httponly 制止 js 读取
  • secure 只能经过 https 传输
  • SameSite 现代浏览器支撑的安全相关的特点

准备好 js 后,这样履行

 final controller = WebViewController();
 controller.runJavaScript('你的 js ');

用 js 读取 cookie

   final cookies = await controller
           .runJavaScriptReturningResult('document.cookie') as String;

这样读出来的是一个字符串,用起来并不便当, 17 准备了一办法,把字符串解析成 map,贴心吧!

Future<Map<String,String>> getCookie(WebViewController controller) async {
    final controller = WebViewController();
    var cookies = await controller
        .runJavaScriptReturningResult('document.cookie') as String;
    var list = cookies.split('; ');
    var map = <String, String>{};
    for (var item in list) {
      var cookieItem = item.split('=');
      map[cookieItem[0]] = cookieItem[1];
    }
    return map;
}

;(分号)后边是一个英文空格

用 js 删去单条 cookie

比方你想删去一个 key 为 token 的 cookie

controller.runJavaScript(
          'document.cookie = "token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"');

能够不指定 value,指定了也不要紧。

js 铲除本域名下所有 cookie

js 能够删去本域名下的 cookie。

function clearAllCookie() {
    var keys = document.cookie.match(/[^ =;]+(?=\=)/g);
    if(keys) {
	for(var i = keys.length; i--;)
	   document.cookie = keys[i] + '=;expires=' + new Date(0).toUTCString()
        }
    }
}

^ 后边是一个英文空格

或许有的同学对正则不是很熟悉,我把正则表达式解释一下。现已掌握的同学自行跳过。

[^\s=;]+ 意义为匹配 除空格等号分号外的至少一个连续字符。比方 a11a,都能够,可是空字符串不可,由于至少要一个字符;a b 不可,由于有空格;a=b 不可;由于有等号,;c 不可,由于有分号。

(?=\=) 这是一个零宽必定先行断语,听术语有点模糊,仍是举例阐明。name=17 用这个零宽必定先行断语匹配的成果便是 e = 之前的那个方位。零宽的意思是不匹配任何字符,只匹配方位。

零宽断语正如它的名字相同,是一种零宽度的匹配,它匹配到的内容不会保存到匹配成果中去,最终匹配成果仅仅一个方位罢了。给指定方位添加一个约束条件,用来规则此方位之前或许之后的字符必须满意约束条件才能使正则中的字表达式匹配成功。

现在把它们合在一起 [^\s=;]+(?=\=),能够匹配 除空格等号分号外的至少一个连续字符并且后边紧跟着等号。

\ 是转义符,这里不加转义其实也能够,由于前面有 ?=,加一个 \ 会显得清晰一些。

g 表示全局匹配,不然只匹配第一个。

最后把这个函数用 runJavaScript 履行一下就好了。

根底用法现已讲完了,下面咱们实战一下。

登录状况同步

用到 cookie 的最经典的应用当属登录状况坚持了。 h5 能够独自登录,app 也能够独自登录,理想状况下,两头的登录状况应该坚持一致,就需求登录状况的同步了。

app 同步登录状况到 h5

WebView 刚翻开的时分,需求把 app 的登录状况同步到 h5。

先写入准备好的 token,再翻开对应的 h5 就行了。

setCookie() async {
    final manager = WebViewCookieManager();
    await cookieManager.setCookie(
      const WebViewCookie(
        name: 'token',
        value: 'IAM17',
        domain: '',
      ),
    );
    await controller.loadRequest(Uri.parse(
        'https://'));
  }

假如没有域名,用 IP 也是能够的,内网外网的 IP 都行。

当然了,调用 js 写入 cookie 也是能够的,但已然有 cookieManager.setCookie,用这个办法更便当,还不必等页面加载完结。

h5 同步登录状况到 app

app 的状况是未登录,翻开 h5 后, h5 自己在网页中登录了。怎么同步到 app 呢?

二个办法

  1. 假如是独自的登录页面,app 阻拦登录页面的恳求后,在 app 中登录,再把状况同步到 h5。
  2. js 调用 JavaScriptChannel 设置的对象办法。

第 1 种办法能够阻拦真正的页面地址,也能够阻拦和 app 约定好的地址。

第 2 种办法 js 调用登录接口,登录后,再调用 JavaScriptChannel 办法把 token 同步给 app。 当然了,js 调用 JavaScriptChannel 办法告诉 app 登录,app 再同步回 h5 也是能够的。

怎么阻拦和交互请拜见 在 Flutter 中运用 webview_flutter 4.0 | js 交互

这 2 种计划在前文都现已讲过,不再赘述。

尽管各种办法都能够实现,17 不引荐阻拦真正的页面地址,由于这样页面地址就被定死了,不便当修改了。假如哪天域名改变,或途径改变,那就很费事了。

登录 token 安全保障

已然 token 是钥匙,那么钥匙被他人拿去了,可就费事了,所以要确保 token 的安全。要想知道怎么确保安全,就得先知道怎么获取 token。

怎么获取 token

  1. HTTP 绑架 一般 HTTP 绑架是绑架 http 恳求,篡改HTTP响应体,运用户收到绑架者篡改后的内容。当然 HTTP 绑架也能拿到 cookie 的信息。
  2. 跨站脚本进犯(Cross-site scripting,XSS),CSRF(跨站恳求假造)来盗取 cookie。

XSS 是指进犯者能够利用缝隙在网站上注入恶意的客户端代码。若受害者运转这些恶意代码,进犯者就能够突破网站的拜访约束并假充受害者。

Cookie 往往用来存储用户的身份信息,恶意网站能够设法假造带有正确 Cookie 的 HTTP 恳求,这便是 CSRF 进犯。

防备措施

要防范这两种进犯也很简略。

网站启用 https 就能够防备 HTTP 绑架。假如网页运用 https 传输,cookie 也会运用 https 传输。假如你的网站只支撑 Https,cookie 不需求独自设置。假如你的网站还支撑 http, 为了安全,能够阻挠 cookie 在 http 协议下传输。

Set-Cookie: token=IAM17; Secure

Secure 代表本条 cookie 只应经过被 HTTPS 协议加密过的恳求发送给服务端,它永久不会运用不安全的 HTTP 发送(本地主机在外),这意味着中间人进犯者无法轻松拜访它。无法轻松这样的表述就显现很谨慎了。

XSS,CSRF 有多种进犯手段,但无论怎样,不论你几路来,我只一路去。防备进犯把 cookie 中的 HttpOnly,Secure,SameSite 一股脑加上就行了。这样就把用 js,传输,恳求链接几种或许的取得 token 的路都堵死了。进犯很难再有机会取得 token。

Set-Cookie: token=IAM17; Secure; HttpOnly; SameSite=Strict;

Cookie 的 SameSite 特点用来约束第三方 Cookie,从而削减安全风险。 SameSite 有三个值能够选

  • Strict 最为严格,完全制止第三方 Cookie。
  • Lax 是默许值,除导航到目标网址的 Get 恳求在外,也是制止第三方Cookie
  • None 陏便发第三方 cookie,但必须同时设置 Secure

第三方Cookie 是指由非当时网站设置的Cookie

比方有两个网站 A,B,A 恳求 B 的页面,带上 A 的 cookie 发送给 B ,关于 B 的页面来说,接收到的 cookie 便是 第三方 cookie。SameSite=Strict 制止 cookie 发送到第三方。也便是说 A 网站的 cookie 这个时分不或许会主动发到 B 网站。

假如 WebView 不支撑 SameSite 怎么办。其实不必忧虑,就算 cookie 没有这些安全设置,h5 也应该有才能确保 token 的安全,避免 XSS,CSRF 的进犯。

js 无法设置 HttpOnly,只能用设置 header 的方式来设置。

怎么验证 cookie 有没有设置成功?

用 chrome 翻开网页,F12 翻开 控制面板。找到应用菜单下的存储下面的 cookie,假如 Secure 处打 ✓ 就阐明设置成功了。相同的,假如 httpOnly 设置成功了,HttpOnly处也会打 ✓。Samesite 会显现相应的值。

Flutter WebView 如何与 h5 同步登录状态

知识扩展, LocalStorage

每次都随着恳求主动发送,尽管十分便当,但也带来了费事,增加了网络传输的负担。有些数据是没有必要每次都传输的。在前期,程序员们想出了分域的办法。比方 js,css,图片用不同的域名。可是 cookie 还有一个问题,容量太小。为了处理这两个问题,LocalStorage 盛大登场了。一起的还有一个 SessionStorage,数据只在一个会话期间内有用。了解下即可,Flutter 这边一般不会用到,假如真的需求存很多数据,流程应该是这样的:js 经过 javascriptChannel 的办法向 flutter 恳求数据(或 flutter 向 js 发送数据),由 js 把数据写入 LocalStorage。这样咱们能够坚持一定的灵活性,比方修改为把数据写入数据库也是很简单的。

cookie 另外一个主要用途是盯梢分析用户。P3P 能够跨域设置 cookie,了解下即可。

番外

周五接到告诉,周六日要开会。我想这下写文的计划是不成了。可是无巧不成书,周五下午就开始不舒服,晚上没吃饭,头痛。周六起来后更严重了,我只好请假。在家里随时能够躺下来,状况好的时分起来写文,所以这篇 《Flutter WebView 怎么与 h5 同步登录状况》仍是如约而至了。