浏览器扩展程序(例如“memonic”)如何将数据与特定用户关联?

4

假设您有一个“主页”网站,用于某种“保存我的收藏夹”的服务,用户可以注册帐户,然后将他们最喜欢的引语或其他文本片段保存到个人收藏中(这种网站的一个例子是“Memonic”:http://www.memonic.com/,我相信还有其他的)。

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

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

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

我的问题是:浏览器扩展程序如何工作?我的理解是,扩展程序不使用“会话”或“cookie”,但我猜测有一种方法可以存储本地数据。但是,您如何将唯一标识符(用于识别用户)从服务器传递到浏览器扩展程序?这个唯一标识符应该起源于服务器(像php的会话ID一样),还是客户端(浏览器插件)生成并发送给服务器?
稍微详细说明第二种用例:
1. 用户下载并安装浏览器扩展程序,但未注册 2. 用户允许使用10次免费“剪报”功能,然后再注册账号 3. 用户访问新闻网站,开始“剪报”,达到10次,然后在扩展程序的工具栏中单击“注册”。一个覆盖层弹出在网站上方,其中包含一个iframe,其中包含注册表单。用户创建用户名和密码,然后点击提交。
现在,登录凭据(用户名和密码)已发送到“主页”(剪贴板)服务器,并且“主页”服务器已经创建了一个包含用户ID的新用户帐户,并将其存储在数据库中。此时,浏览器扩展应该知道如何识别用户(使用用户ID或等效的会话ID),这是如何实现的?
附注:我只对Firefox和Chrome感兴趣。
1个回答

5
我的问题是,这在浏览器扩展中如何工作?我理解扩展程序不使用"会话"或"cookie",尽管我猜测有一种方式可以存储本地数据。
(我为addons.mozilla.org编写代码和功能评论,因此对Firefox方面的事情更加了解,但在Chrome中应该基本相同)
好吧,有些附加组件利用cookie和其他web技术,但大多数情况下,对于这种类型的扩展,作者会选择像这样的东西:
1. 创建一个易于通过XMLHttpRequest-style AJAX消耗的服务器端点,通常是某种REST API端点。 2. 扩展通常可以访问某些API,它要么是XMLHttpRequest本身,要么类似于它(例如Firefox Add-on SDKChrome)。 3. 在用户交互时,扩展将发出XHR/Ajax调用以执行所需的任何操作,查询信息,注册帐户,登录用户,提交新数据等。

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

由您决定需要在浏览器本地存储哪种类型的数据以及哪种类型的存储最适合该数据。

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

但是如何将唯一ID(用于标识用户)从服务器传递到浏览器扩展程序中呢?

API如何维护(登录)状态完全取决于您和您(现有的)设计。

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

这个唯一的ID应该是在服务器上生成(如php的会话ID),还是客户端(浏览器插件)生成并将其发送到服务器?

这取决于您,但对我来说,服务器生成真正的唯一ID是有意义的,因为它通常知道哪些ID仍然可用,而不是浏览器扩展程序生成准唯一ID,这些ID仍然有与其他浏览器实例(其他用户)可能并行生成的ID发生碰撞的轻微概率。

第二个用例:用户访问新闻网站,选择文本,右键单击选择菜单项,将所选文本保存到其帐户中。他们不需要访问“主页”网站。这基本上就是Memonic的Firefox扩展程序所做的事情。

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

2) 用户在注册账户之前可以免费剪辑10次。

  • 扩展程序可以向服务器发送API请求以获取某种临时用户ID,并在每个“剪辑”请求中发送该ID。
  • 进行了10次这样的“剪辑”请求后,服务器将返回错误信息,告诉扩展程序“免费剪辑”已用完。
  • 此时,扩展程序会询问用户是否要注册账户以获得无限制的剪辑,例如通过在新标签页(或其他辅助UI)上打开你的服务器上的注册页面来实现。
  • 在打开页面时,扩展程序可以通过GET参数、设置cookie或请求数据或请求头来传递临时用户ID,以便在成功注册后,已经进行的“剪辑”将与新的完整账户关联。
  • 扩展程序还可以监视注册过程,以自动获取登录详细信息。

[监视注册] 如何完成?

最简单的方法可能是附加内容脚本,然后直接从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();
  }
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接