与Web小部件通信 - Meteor,React,Node

时间:2016-12-05 05:40:46

标签: javascript node.js reactjs meteor webpack

我正在构建一个聊天仪表板和小部件,客户应该可以将小部件放入他们的页面。一些类似的例子是IntercomDrift

目前,“main”应用程序是用Meteor.js编写的(它的前端是React)。我编写了一个<Widget />组件并将其抛入/widget目录。在这个目录中,我还有一个index.jsx文件,它只包含以下内容:

import React from 'react';

import ......

ReactDOM.render(
  <Widget/>,
  document.getElementById('widget-target')
);

然后我在index.jsx设置一个入口点的webpack配置,当运行webpack时,在公共目录中吐出bundle.js

只需添加scriptdiv即可将其包含在其他网页上:

<script src="http://localhost:3000/bundle.js" type="text/javascript"></script>
<div id="widget-target"></div>

几个问题:

  1. 这个实现有什么问题?他们的安全问题是否需要注意?之前链接的两个示例似乎都以一种或另一种形式使用iframe。
  2. 与主流星应用程序通信的最佳方式是什么? REST API?用Socket.io发出事件?小部件是一个聊天小部件,所以我需要来回发送消息。
  3. 如何为用户和窗口小部件实现某种唯一标识符/用户身份验证?目前,小部件已预编译。

2 个答案:

答案 0 :(得分:7)

1这个实现有什么问题?他们的安全问题是否需要注意?之前链接的两个示例似乎都以一种或另一种形式使用iframe。

正如@JeremyK所说,你在iFrame中更安全。话虽如此,许多第三方(Facebook,GA,......)正在使用中间路线,包括对讲机:

  • 要求用户在您的网页中集成您的捆绑代码。然后由您来确保您不会在其网站上引入安全漏洞。这段代码将做两件事:
  • 负责设置iframe,其中服务的主要部分将会发生。您可以对其进行定位,设置样式等。这样可以确保iframe中发生的所有逻辑都是安全的,并且您不会暴露。
  • 使用窗口消息在您的客户网页和您的iframe之间公开一些API。
  • 主代码(iframe代码)然后由第一个脚本异步加载,不包含在其中。

例如,对讲机要求客户在其网页上添加一些脚本:https://developers.intercom.com/docs/single-page-app#section-step-1-include-intercom-js-library非常小(https://js.intercomcdn.com/shim.d97a38b5.js)。这会加载额外的代码来设置iFrame并公开their API,这样可以轻松与iFrame进行交互,例如关闭它,设置用户属性等。

2与主流星应用程序通信的最佳方式是什么? REST API?用Socket.io发出事件?小部件是一个聊天小部件,所以我需要来回发送消息。

您有三个选择:

  • 将您的小部件构建为整个Meteor应用。这将增加需要加载的代码的大小。作为额外代码的交换,您可以通过Meteor API与后端进行通信,如Meteor.call,获取所有数据的反应性(例如,如果您通过主Meteor应用程序向用户发送响应,则响应只要它们位于同一个数据库(不需要在同一台服务器上),以及乐观的UI,就会弹出客户端而无需工作。简而言之,这就是Meteor在这里提供的所有内容,并且它可能更容易与现有的后端集成,我认为这是Meteor。
  • 不要包括Meteor。由于您正在构建聊天应用,因此您可能需要socket.io而不是传统的REST API。当然,你可以混合使用
  • 使用Meteor DDP。 (它有点像socket.io,但对于Meteor。流星应用程序使用它来处理服务器的所有请求)这将包括更少的东西,完整的Meteor并且可能更容易集成到您的Meteor后端而不是REST API / socket.io,将在整个流星上做一些额外的工作。

3如何为用户和小部件实现某种唯一标识符/用户身份验证?

此部分可能应该在客户网站上进行一些工作(在您的iframe中),以便您可以在其页面上设置Cookie,并将该数据发送到您将与您的服务器通信的iframe并识别用户。您使用artwells:accounts-guest(基于meteor:accounts-base的那个)将取决于您决定将Iteor包含在您的iframe中。

如果您的iframe中没有Meteor,您可以执行以下操作:

  • 只需在服务器上执行即可自行处理用户创建

const token = createToken();
Users.insert({ tokens: [token] });
// send the token back to your iframe
// and set is as a cookie on your customer website
  • 然后每次打电话给您的服务器,在您的iframe上:

let token;
const makeRequest = async (request) => {
    token = token || getCookieFromCustomerWebsite();
    // pass the token to your HTTP / socket.io / ... request.
    // in the header of whatever
    return await callServer(token, request);
};
    服务器中的
  • 有一个设置用户的中间件。我看起来像:

const loginAs = (userId, cb) => {
  DDP._CurrentInvocation.withValue(new DDPCommon.MethodInvocation({
    isSimulation: false,
    userId,
  }), cb);
};

// my middleware that run on all API requests for a non Meteor client
export const identifyUserIfPossible = (req, res, next) => {
  const token = req.headers.authorization;
  if (!token) {
    return next();
  }
  const user = Users.findOne({ tokens: token });
  if (!user) {
    return next();
  }

  loginAs(user._id, () => {
    next();
    // Now Meteor.userId() === user._id from all calls made on that request
    // So you can do Meteor.call('someMethod') as you'd do on a full Meteor stack
  });
};

答案 1 :(得分:2)

要求您的客户嵌入这样的代码并不遵循Security by Design的原则。

从他们的角度来看,您要求他们将您的预先绑定的代码嵌入到他们的网站中,使他们的网站暴露于您的代码中存在的任何隐藏的安全风险(无意或故意恶意),这些风险将无限制地访问其网站DOM,localstorage等。

这就是为什么使用iframe是在网站中嵌入第三方内容的首选方法,因为该内容是从其余主机站点沙箱化的。

此外,遵循“最低权限”的安全原则,他们(使用您的指导/示例)可以在iframe上设置sandbox属性,并通过白名单明确锁定小部件将拥有的权限。

iframe中加载小部件还可以让您更灵活地与服务器进行通信。这可能是一个普通的流星客户端,使用meteor的ddp与您的服务器进行通信。您的其他建议也是可能的。

用户身份验证/识别取决于系统的详细信息。这可能包括使用Meteor Accounts,它会为您提供密码或社交身份验证解决方案。或者您可以尝试匿名帐户解决方案,例如artwells:accounts-guest

html5rocks article on sandboxed-iframes