【问题标题】:How do Browser Extensions such as "memonic" associate data with a specific user?诸如“记忆”之类的浏览器扩展如何将数据与特定用户相关联?
【发布时间】:2013-12-16 18:52:39
【问题描述】:

假设您有一个“主页”网站,提供某种“保存我最喜欢的剪报”服务,用户可以在其中注册帐户,然后将他们最喜欢的报价或其他文本的 sn-ps 保存到个人收藏中(一个示例这种网站是“Memonic”:http://www.memonic.com/,其中我敢肯定)。

第一个用例:用户访问他们最喜欢的新闻站点,选择并复制一些文本,切换到加载了“主页”(剪辑)站点的选项卡,粘贴到表单中,然后将选定的文本保存到他们的帐户。

第二个用例: 用户访问新闻站点,选择文本,右键单击以选择将所选文本保存到其帐户的菜单项。他们不需要访问“家”网站。这几乎就是 Memonic 的 Firefox 扩展所做的。

所以在第一个用例(浏览器)中,假设是基于 PHP 的架构,服务器从请求中浏览器传递的 cookie 中识别用户。 cookie 包含 session_id,对于该用户是唯一的,服务器用户可以在其中找到包含 user_id 的会话数据。然后使用 user_id 将记录插入数据库。

我的问题:这在浏览器扩展程序中是如何工作的?我的理解是扩展不使用“会话”或“cookies”,尽管我猜有一种方法可以存储本地数据。但是,您如何将唯一的 id(用于识别用户)从服务器传递到浏览器扩展程序?这个唯一的 id 应该源自服务器(如 php 的会话 id),还是应该由客户端(浏览器插件)生成并发送到服务器?

稍微详细说明第二个用例:

  1. 用户下载并安装浏览器扩展,但未注册

  2. 用户在注册帐户之前可以免费获得 10 次“剪报”

  3. 用户访问新闻站点,开始“剪辑”,达到 10,然后单击扩展工具栏中的“注册”。网站上会弹出一个叠加层,其中包含一个 iframe,其中包含注册表单。用户创建用户名和密码,点击提交。

现在,登录凭据(用户名和密码)已发送到“家庭”(剪辑)服务器,“家庭”服务器创建了一个带有 user_id 的新用户帐户,并存储在数据库中。此时,浏览器扩展应该知道如何识别用户(user_id 或等效的 session_id)...这是如何实现的?

ps -- 我真的只对 Firefox 和 Chrome 感兴趣

【问题讨论】:

    标签: javascript session cookies google-chrome-extension firefox-addon


    【解决方案1】:

    我的问题,这是如何在浏览器扩展中工作的?我的理解是扩展程序不使用“会话”或“cookies”,虽然我猜有一种方法可以存储本地数据

    (我为 addons.mozilla.org 进行代码和功能审查,因此对 Firefox 方面的知识更加了解,但在 Chrome 中应该差不多)

    嗯,一些插件会使用 cookie 和其他网络技术,但大多数情况下,当涉及到此类扩展时,作者会选择类似的东西

    • 创建一个可以通过 XMLHttpRequest 样式的 AJAX 代码轻松使用的服务器端点,通常是某种 REST API 端点。
    • 扩展通常可以访问 XMLHttpRequest 本身或类似它的某些 API(例如 Firefox Add-on SDKChrome])
    • 在用户交互时,扩展程序将发出 XHR/Ajax 调用以执行所需的任何操作、查询信息、注册帐户、登录用户、提交新数据等。

    扩展 API 通常提供多种方式来存储和检索数据,从纯文本文件、键值存储到关系数据库 (WebSQL/IndexedDB),例如Firefox Add-on SDK simple-storageChrome chrome.storage。 此外,可能有 API 可以安全地存储登录凭据(例如 passwords module

    您可以自行决定需要在浏览器本地存储什么样的数据,以及最适合的存储类型。

    一般来说,扩展程序可以做网站可以做的事情,而且还可以做更多事情。 (在 Chrome 中通过 chrome.* 扩展 API,在 Firefox 中您基本上可以做 Firefox 可以做的所有事情,这就是普通程序可以做的所有事情。

    但是如何将唯一的 id(用于识别用户)从服务器传递到浏览器扩展程序?

    API 如何以及是否维护(登录)状态完全取决于您,并且取决于您的(现有)设计

    • 向每个请求发送用户:密码
    • 拥有一个登录 API,该 API 将设置适当的 cookie 或返回将用于后续请求的某种会话令牌
    • 使用 OAuth2 等协议

    这个唯一的 id 应该源自服务器(如 php 的会话 id),还是应该由客户端(浏览器插件)生成并发送到服务器?

    这取决于你,但对我来说,服务器生成真正的唯一 ID 是有意义的,因为它通常知道哪些 ID 仍然可用,而不是浏览器扩展生成仍然有很小概率的伪唯一 ID与其他浏览器实例(其他用户)可能并行生成的 id 发生冲突。

    第二个用例:用户访问新闻站点,选择文本,右键单击以选择将所选文本保存到其帐户的菜单项。他们不需要访问“家”网站。这几乎就是 Memonic 的 Firefox 扩展所做的。

    插件将提供一个菜单项,在点击时获取选定的文本,并通过后台 API Ajax 调用将其发送到服务器(在请求中提供身份验证信息或之前已通过身份验证)。

    2) 用户在注册帐户之前可以免费获得 10 次“剪报”

    • 该扩展程序可以要求服务器(进行 API 调用)发出某种临时用户 ID,并随每个“剪辑”请求一起发送。
    • 在 10 个这样的“剪辑”请求后,服务器将返回一个错误,告知扩展程序“免费剪辑”已用完。
    • 此时扩展程序会询问用户“不再提供免费剪报。注册一个帐户以获得无限剪报”,例如通过在新选项卡(或其他,辅助用户界面)。
    • 当打开页面时,扩展可能已经将临时用户 id 传递给页面(例如通过 GET 参数,或通过设置 cookie 或请求数据或请求标头),以便在成功注册时已经生成“剪报”将与新的完整帐户相关联。
    • 该扩展程序还可以监控注册进度以自动获取登录详细信息。

    【监控注册】这是如何实现的?

    最简单的方法可能是附加 a content-script 并直接从 DOM 中抓取信息,或者让网站发出一个常规的 DOM 事件,然后内容脚本将处理。

    有些人还选择不通过常规网站+表单完成整个注册工作,而是使用 HTML 来创建一些由扩展程序控制的 UI。一旦用户单击注册按钮,扩展程序就会读出注册字段并进行另一个 API 调用以执行实际注册,服务器将回复成功或错误代码。成功后,扩展程序将记住登录信息并随后使用它。

    PS: 这是一个使用 Firefox Add-on SDK 的完整示例扩展,带有上下文菜单项,单击该菜单项时将查询 Web 服务(API 端点,此处不需要身份验证),以通知用户当前选项卡的 TLD 注册人。它不能完全转化为您的特定用例,但它是一个很好、简单的演示,可以展示您以后可能需要的一些东西,并让您一睹这种扩展的样子。

    var cm = require("sdk/context-menu");
    var notifications = require("sdk/notifications");
    var {Request} = require("sdk/request");
    var {Cc, Ci, Cu} = require("chrome");
    Cu.import("resource://gre/modules/Services.jsm");
    
    
    cm.Item({
      label: "Whois Registrant",
      contentScript: 'self.on("click", function (node, data) {' +
                     '  console.log("clicked", location.host);' +
                     '  self.postMessage(location.host);' +
                     '});',
      onMessage: function (domain) {
        domain = Services.eTLD.getBaseDomainFromHost(domain);
        Request({
          url: "http://www.restfulwhois.com/v1/" + encodeURIComponent(domain),
          headers: {"Accept": "application/json"},
          onComplete: function (response) {
            var msg;
            try {
                msg = "Registered via:\n" + JSON.stringify(response.json.registrant, null, 2);
            }
            catch (ex) {
                msg = "Failed - " + ex;
            }
            notifications.notify({
              title: domain,
              text: msg
            });
          }
        }).get();
      }
    });
    

    【讨论】:

      猜你喜欢
      • 2020-12-13
      • 2014-11-20
      • 1970-01-01
      • 2011-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-27
      相关资源
      最近更新 更多