自定义脚本的跨浏览器Web扩展第3部分

在本文中,我将继续介绍一系列出版物,在这些出版物中,我将谈谈我为浏览器编写Web扩展的经验。 我已经有创建Web扩展的经验,该Web扩展由大约100,000个Chrome用户安装,它们可以自动工作,但是在本系列文章中,我决定通过将Web扩展与服务器端紧密集成来研究Web扩展的开发过程。

图片图片图片图片图片

第1部分第2 部分第4部分

实现Web扩展与服务器端交互的陷阱


如前所述,服务器端使用Meteor.js。 为了实现RESTful API,使用了github.com/kahmali/meteor-restivus软件包。 它已经包含一些实现部分,用于涵盖与授权相关的用户机制。

例如,只需指定authRequired:true即可 ,如下面的示例所示,以便API点仅对授权用户有效。

Api.addRoute('clientScript/:id_script', {authRequired: true}, {get: { action: function() { //method for GET on htts://example.com/api/v1/clientScript/:id_script } }); 

因此,添加了三个API点用于注册,接收配置文件数据并对其进行更新,用于密码重置。

在Web扩展本身中,当调用需要授权的方法时,使用以下代码:

 var details = { url: API_URL + '/api/v1/clientDataRowDownload/' + dataRowId + '/download', method: 'GET', contentType: 'json', headers: {'X-Auth-Token': kango.storage.getItem("authToken"), 'X-User-Id': kango.storage.getItem("userId")} }; kango.xhr.send(details, function(data) { //code for response handler }) 

此处清晰可见授权请求的示例。 标头包括X-Auth-Token和X-User-Id,它们是通过注册或授权过程获得的。 此数据存储在Web扩展的本地存储中,并且始终在content.js脚本中可用。

通过在浏览器端读取文件并通过XHR发送来完成Web扩展中的文件下载:

 $("form#uploadFile").on("submit", function(event, template) { event.preventDefault(); var reader = new FileReader(); reader.onload = function(evt) { var details = { url: API_URL + '/api/v1/clientFileAdd/' + kango.storage.getItem("userId"), method: 'POST', contentType: 'json', params: {"content": encodeURIComponent(evt.target.result.replace(/^data:[^;]*;base64,/, "")), "name": encodeURIComponent(event.currentTarget.fileInput.files[0].name), "size": event.currentTarget.fileInput.files[0].size, "type": event.currentTarget.fileInput.files[0].type, "lastModified": event.currentTarget.fileInput.files[0].lastModified }, headers: {'X-Auth-Token': kango.storage.getItem("authToken"), 'X-User-Id': kango.storage.getItem("userId")} }; kango.xhr.send(details, function(data) { if (data.status == 200 && data.response != null) { if(data.response.status == "success") { //ok } else { //error } } else { if(data.status == 401) { //notAuth } else { //error } } }); }; if (event.currentTarget.fileInput.files.length != 0) { reader.readAsDataURL(event.currentTarget.fileInput.files[0]); } return false; }); 

请注意event.target.result.replace(/ ^ data:[^;] *; base64,/,“”)行,这一点很重要。 浏览器端的文件是使用base64编码的,但是为了在服务器端使用Buffer.from行中的这种编码(新字符串(this.bodyParams.content),“ base64”),我们必须切断编码前缀并仅读取文件的“主体”,以实现服务器端的兼容性。 。 还必须注意encodeURIComponent中的换行,因为在base64和文件名中通常会找到相同的+

编辑脚本时,在传输内容时需要考虑脚本主体中的字符编码。 在某些情况下,在服务器端使用encodeURIComponent进行解码时,base64编码无法产生正确的结果。 因此,utf8中的强制编码预先与utf8.encode(str)一起使用。 来自@mathias的mths.be/utf8js v3.0.0

使用完善的FileSaver库实现文件下载。 通过XHR接收到的数据仅传输到File构造函数的输入,然后初始化文件下载:

 var file = new File([data.response.data.join("\n")], "data_rows" + date.getFullYear() + "_" + (date.getMonth() + 1) + "_" + date.getDate() + ".csv", {type: "application/vnd.ms-excel"}); saveAs(file); 

自定义脚本的内部库


为了使脚本,Web扩展和服务器部分进行交互,必须有一个中间链接,该链接可以使您快速从下载的文件中接收数据,在执行脚本后保存数据,等等。

为此目的,编写了一个内部库,该库在将任何脚本添加到页面代码之前开始初始化。 在这里,您需要添加有关源保护策略的信息以加载资源,即content-security-policy。

许多站点使用CSP标头来防止在其Web服务页面上执行任意javascript代码,从而保护自己免受Web浏览器端的XSS攻击。

由于用户自己安装了Web扩展,因此可以更改下载资源的标题和内容。 由于Mozilla Firefox中的错误,某些网站存在此问题。 也就是说,在Firefox的Web扩展中,将无法修改标头或添加元标记来取消使用它们的网站的CSP策略。 尽管该标准明确规定了Web扩展的规定,但该错误已经好几年没有解决了,该标准指出,与从用户自己安装的Web扩展有关的,关于从应用程序服务器一侧下载资源的策略并不占优势。

可以使用kango框架通过以下方式实现CSP策略限制:

 var browserObject; if(kango.browser.getName() == 'chrome') { browserObject = chrome; } else { browserObject = browser; } var filter = { urls: ["*://*/*"], types: ["main_frame", "sub_frame"] }; var onHeadersReceived = function(details) { var newHeaders = []; for (var i = 0; i < details.responseHeaders.length; i++) { if ('content-security-policy' !== details.responseHeaders[i].name.toLowerCase() && 'x-xss-protection' !== details.responseHeaders[i].name.toLowerCase() ) { newHeaders.push(details.responseHeaders[i]); } } return { responseHeaders: newHeaders }; }; browserObject.webRequest.onHeadersReceived.addListener(onHeadersReceived, filter, ["blocking", "responseHeaders"]); 

在这种情况下,您一定不要忘记在Web扩展清单中添加允许在阻塞模式下使用webRequest对象的行:

 "permissions": { ... "webRequest": true, "webRequestBlocking": true, ... } 

解决了CSP施加的限制问题后,用户可以使用他在Internet上任何页面上编写的脚本。

可通过全局Gc对象使用内部库中的调用函数。
当前实现的功能:

  • GC.saveRow(名称,内容,[重写= 0,异步=假]); ,其中name是要写入集合的行的名称,content是要写入的数据线本身,rewrite是用于覆盖整个集合的标志,用于调用Gc.saveRow(name,'clear',1); 删除字符串集合中的所有条目,async是用于异步模式的标志。
  • GC.getRows(名称,数字,[count = 1,async = false]); 其中name是集合中各行的名称,number是要接收数据的行的序列号,count是从数字开始的接收数据数,async是用于异步模式的标志。
  • GC.console(字符串); ,其中string是要输出到执行脚本页面上的GC控制台的字符串。 例如,演示任务的进度。
  • GC.clearConsole(); ,该功能会清除执行脚本的页面上的GC控制台。
  • GC.stopScript(); ,用于停止脚本执行的功能。
  • GC.loadFile(名称,[parseAs = text]); ,其中name是要获取其扩展名的文件的扩展名,parseAs是数据预处理器格式,当前支持json和text。

在下一篇文章中,我将讨论“ 计划任务 ”。

Source: https://habr.com/ru/post/zh-CN416585/


All Articles