极客手中的利器Electron

做为一个前端开发人员,你可能已经据说过Electron了,你知道VS Code是基于这个技术开发的。不但VS Code,javascript

目前一些大热的软件:飞书、Slack、WhatsApp都是基于这个技术开发的。html

即便工做不会涉及到,也应该学一下Electron,由于它是极客手里的利器,你能够经过他作不少Amazing的事情。前端

本文主要讲讲Electron是什么,以及它能作什么极客工做。java

Electron的由来

2011年左右,中国英特尔开源技术中心的王文睿(Roger Wang)但愿能用Node.js来操做WebKit,而建立了node-webkit项目,这就是NW.js的前身。当时的目的并非用这两个技术来开发桌面GUI应用。node

中国英特尔开源技术中心大力支持了这个项目,不但容许王文睿分出一部分精力来作这个开源项目,还给了他招聘名额,容许他招聘其余工程师来一块儿完成这个项目。2012年,故事的另外一个主角赵成(Cheng Zhao)加入到王文睿的小组,并对node-webkit项目作出了大量的改进。git

后来赵成离开了中国英特尔开源技术中心,帮助github团队尝试把node-webkit应用到Atom编辑器上,但因为当时node-webkit还并不稳定,且node-webkit项目的走向也不受赵成的控制,这个尝试最终以失败了结。github

但赵成和github团队并无放弃,而是着手开发另外一个相似node-webkit的项目:Atom Shell,这个项目就是Electron的前身,赵成在这个项目上倾注了大量的心血,这也是这个项目后来广受欢迎的关键因素之一,再后来github把这个项目开源出来,最终改名为Electron。web

你可能从没据说过这两个名字,但开源界就是有这么一批“英雄”,他们不为名利而来,甘作软件行业发展的铺路石,值得这个领域的全部从业者尊敬(以上内容与Electron的做者确认过)。ajax

注入脚本

Electron内部拥有一个完整得浏览器核心,你能够用程序操纵这个浏览器核心,让它加载一个第三方网页,好比:淘宝的生意参谋、网易云音乐、gitee等,但单单加载这些网页,并无什么稀奇的,毕竟在浏览器里也能加载这些网页。最有意思的是,你还能够给这些网页注入脚本,好比像下面这样,注入一个Js文件到目标网页:编程

let win = new BrowserWindow({

 webPreferences: {

   preload: path.join(appPath, 'yourPreload.js'),

   nodeIntegration: true

 }

});

win.loadURL('https://www.baidu.com/');

若是你只但愿注入一两句代码,也能够经过以下形式注入脚本:

let decryptStr = await this.win.webContents.executeJavaScript(`window.__allen_decrypt('${encryptStr}')`);

这些脚本是在目标网页的做用域下执行的,与目标网页的工程师本身写的代码没什么区别。想象一下,你若是想调用目标网页的某个服务端接口,你是否是应该考虑如何模拟token,如何跨域等等问题,如今你只须要在脚本里直接写调用接口的逻辑就能够了。

另外,若是你不喜欢目标网页的页面样式,你也能够直接注入一段样式脚本,代码以下:

let key = await win.webContents.insertCSS("html, body {   }");

//await win.webContents.removeInsertedCSS(key); //在将来的某个时刻,还能够移除掉这个样式的做用

值得一提的是,你注入的脚本还能够访问Node.js的API。也就是说,你在脚本中获取到了目标网页的资源后,能够直接写到你本地文件里。

突破同源策略的限制

注入了脚本,获取到了受限的资源,你可能但愿把这些资源提交到你本身的服务器上,或者你可能但愿在注入的脚本里,访问另外一个网站的API,以获取更多的资源,这个时候,若是没作特殊配置的话,同源策略就会起做用,限制你这么干,浏览器每每会报以下错误:

Access to XMLHttpRequest at 'https://www.domain1.com/aggsite/AggStats' from origin 'http://www.domain2.com:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

“同源策略”是浏览器的一个安全功能,“同源”是指若是两个页面的协议(http/https)、端口和主机都相同,则两个页面具备相同的源。同源策略规定不一样源的客户端脚本在没有明确受权的状况下,不能读写对方资源。只有同一个源的脚本才具有读写cookie、session、ajax等的操做的权限。

Electron中突破同源策略,就是一两个配置的事情,代码以下:

let win = new BrowserWindow({

    width: 800,height: 600,

    webPreferences: {

        nodeIntegration: true,

        webSecurity: false, //此参数禁用当前窗口的同源策略

    }

})

win.loadURL('https://www.baidu.com/');

这样设置以后,浏览器的全部同源策略限制就所有失效了。若是你只是但愿在https域下访问http的资源,那么你能够不用关掉整个同源策略,只须要把allowRunningInsecureContent这个配置设置为true便可(它一样也是webPreferences下的一个属性)。若是你设置webSecurity为false了,那么allowRunningInsecureContent会自动设置为true。

读写受限访问的Cookie

因为浏览器每次与服务端的交互,都会携带同域下的全部Cookie,因此网站开发者每每会把标记用户身份的信息(好比用户token)放到Cookie里。

通常状况下,前端开发工程师可使用document.cookie访问浏览器里存储的同域的Cookie,但也有例外,凡标记了HttpOnly的Cookie,经过这种方式都是访问不到的。网站开发者之因此这么作,主要是为了防止跨站脚本攻击(XSS)和跨站请求攻击(CSRF)。

跨站脚本攻击(XSS,是Cross Site Scripting的缩写),一旦网站容许用户提交内容,而且会在网站的某些页面上显示用户提交的内容,好比留言或者博客,那么不作防范的话,就有可能受到跨站脚本攻击。恶意用户会在提交内容时在内容中夹带一些恶意JavaScript脚本,当其余用户访问页面时,浏览器会运行这些恶意脚本,恶意脚本有可能会窃取用户的Cookie、页面上的用户隐私信息等,并发送到恶意用户的服务器。他们能够经过这些窃取来的信息模拟用户身份完成非法操做。这就是跨站脚本攻击。

跨站请求攻击(CSRF,是Cross Site Request Forgery的缩写),当用户登陆了本身信赖的网站后,用户身份信息(token)会被保存保存在用户的浏览器上,后来用户又不当心打开了一个恶意网站,这个恶意网站可能会要求浏览器请求用户信赖的网站(经过iframe等形式),若是用户信赖的网站没有作安全防范的话,可能会被恶意网站获取到用户的敏感信息(token),从而给用户带来伤害。

但这个限制在Electron面前也不值一提,咱们能够经过下面这种方式读写受限访问的Cookie:

//获取Cookie

async function(name) {

    let cookies = await remote.session.defaultSession.cookies.get({name});

    if(cookies.length>0) return cookies[0].value;

    else return '';

}

//设置Cookie

async function(cookie) {

    await remote.session.defaultSession.cookies.set(cookie);

}

经过这种方式,不管Cookie有没有设置HttpOnly属性,均可以成功读写。

转发/修改请求

有的时候你不仅仅是但愿给第三方网页附加代码逻辑,而是但愿侵入式的修改第三方网站自身的代码逻辑。但每每第三方的JavaScript代码是在一个闭包做用域内执行的,你的代码没办法注入到这个做用域内,去访问做用域内的变量或方法,碰到这样的情况该怎么办呢?

此时,你第一步要作的,就是分析清楚它的脚本是如何执行的,是哪一个脚本文件执行的。这个过程是一个很是有趣的过程,不可避免的你要用到谷歌浏览器的开发者调试工具,若是对方的代码是压缩过的,你可能还要给它“美化”一下,再逐步调试。

搞清楚逻辑以后,就把他的脚本文件下载下来,而后在这个文件中加上你的逻辑,你的逻辑可能就是粗暴的把它闭包做用域内的变量暴露到window对象上。这样你注入的脚本,就能够访问这个变量了。

修改完这个脚本文件后,把这个脚本文件host到你本身的一个服务器上,而后经过Electron把网页加载这个脚本文件的请求,转发到你本身的服务器上去,这个转发请求的代码以下:

win.webContents.session.webRequest.onBeforeRequest({ urls: ["https://*/*"] }, async (details, cb) => {

    if (details.url === 'https://g.alicdn.com/dt/op-mc/vendors.js') {

        cb({ redirectURL: 'http://domain.com/vendors.js' });

    } else {

        cb({})

    }

});

当这个网页再试图加载这个脚本时,获得的结果将是你修改过的脚本。

有些网站对一些敏感数据保护的很好,客户端请求这些数据时,获得的是服务端加密过的数据,客户端执行解密后,再使用这些数据。对于这类网站,Electron的这个能力无疑是很是有力的。

固然,你也能够考虑使用PWA技术里的service worker来办这个事儿,甚至能够本身在客户端模拟一个响应,不用再经由你的服务器转发了。若是你没有本身的服务器,也能够经过Node.js的能力,本身在软件里起一个localhost的服务。全部这些骚操做,都必须时在Electron内执行的哦。

“防盗链”

有的时候你可能只是想把目标网站的一些静态资源嵌入到你的应用程序中,好比:图片或者视频。然而若是目标网站已经作了防盗链的工做,你这个功能可能就没那么容易实现了。

防盗链的主要目的有两个:一个是版权问题,别人未经受权就使用你的资源,另外一个是流量压力的问题,盗链产生了大量的请求,这些请求对于网站运营者来讲没任何价值。

防盗链最多见的作法就是识别HTTP的Refer请求头,这个请求头表明着发起请求时前一个网页的地址,网站运维工程师会根据这个Refer请求头来推测出当前请求是否为一个盗链请求(判断这个Refer请求头的内容是否是本身域名下的一个地址)。

Electron容许开发者监听发起请求的事件,并容许开发者在发起请求前修改请求头,咱们能够在这个事件里修改这个Refer请求头,代码以下:

let session = window.webContents.session;

let requestFilter = { urls: ["http://*/*", "https://*/*"] };

session.webRequest.onBeforeSendHeaders(

    requestFilter,

    (details, callback) => {

      if (details.resourceType == "image" && details.method == "GET") {

        delete details.requestHeaders["Referer"]; //这里我直接删掉了这个请求头,也能够修改为其余内容

      }

      callback({ requestHeaders: details.requestHeaders });

    }

);

给应用植入socks5代理

Chrome自己是支持代理的,使用Electron你能够经过编程的方式把你的代理内置到你的应用程序中,这样你的用户就能够自由的访问国外的一些网站了:

常见的代理服务器有http代理、https代理和socks代理,socks代理隐蔽性更强,效率更高,速度更快。我们这里就聊聊如何在Electron应用内植入socks5代理访问网络服务。

let result = await win.webContents.session.setProxy({

    proxyRules: 'socks5://58.218.200.249:2071'

});

win.loadURL('https://www.ipip.net');

上面代码中,咱们使用session实例的setProxy方法来为当前页面设置代理,socks代理,代理设置成功后,咱们立刻使网页加载了一个IP地址查询的网址,在此页面上咱们能够看到访问该页面的实际IP地址,若是这里显示的地址是你的代理服务器的所在地的地址,那么说明代理设置成功。

以上这种方法能够给单个渲染进程设置代理,若是你须要给整个应用程序设置代理,可使用以下代码完成:

app.commandLine.appendSwitch('proxy-server', 'socks5://58.218.200.249:2071');

最后

以上内容来自于个人新书《Electron实战:入门、进阶与性能优化》,

书里还有更多有趣的内容,

你们感兴趣能够加QQ群949674481交流。

当当:http://product.dangdang.com/28547952.html
京东:https://item.jd.com/12867054.html