4

浅谈 electron 中的 session 管理(隔离)

 2 years ago
source link: https://blog.guowenfh.com/2017/10/21/2017/electron-multiple-session/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

浅谈 electron 中的 session 管理(隔离)

发表于2017-10-21更新于2020-03-12字数统计2.6k阅读时长19分

已经有很长一段时间没有产出博客了。
一. 是因为花了很多时间去专研业务,能够做到目前的基本业务流程理清,大致了然于胸(导致了一个问题:有人找我解决问题,我可能会先问一句,你的需求是什么?)
二. 确实是自己这一段时间确实懈怠了,每天上班回去就不想敲代码了,看看剧,看看小说,刷刷微博。虽然在组内有过一些分享,整理过一些东西,但是却没有将其在博客产出了。
这样的情况,让我明显的感觉到自己的成长速度相对于第一年成长的速度,慢了几个等级。这让我有一种危机感,于是克服这种懈怠,跳出舒适区,继续强健自己。重新回归吧。
最后。还是引用这个博客的描述:”兴趣遍地都是,专注和坚持才是真正稀缺的。”

不多说了,开始吧。

由于公司的项目内部调整,有幸接触 2 个星期的 electron 开发。(然后我又被拥抱变化了。。)实现了一个多账号的切换,并且同时对于多账号的聊天窗口做一个浏览器 tab 的集成的需求,这里对于接触到的知识点,做一个总结。以免完全忘记(忽略代码规范,我自己都看不下去)

electron-中文文档

在我加入项目之前,壳就已经搭好了,我只是在之上去开发。然而我接触时间太短,然后就撤离了,只能说一些我看到的和用到的部分。( 其它存在的问题, 比如:安全,目前没有更多的精力去解决)

了解之后,最开始的项目搭建是使用的 electron-quick-start 来快速的构建出 一个 electron 客户端的项目。

由于项目需要快速迭代和试错。也没有使用大多数客户端项目将所有资源存在本地,然后再去更新本地资源的形式,而是在客户端暴露 sdk 的情况下 直接 load 了一个 远程地址

const electron = require('electron')
const {BrowserWindow} = electron
const path = require('path')
let mainWindow = new BrowserWindow({
width: 1280,
height: 768,
icon: path.resolve(__dirname ,'./build/icon.png'),
title:'客户端',
webPreferences: {
webSecurity: false,
allowRunningInsecureContent: true,
preload: path.resolve(path.join(__dirname, './common/sdk.js'))
}
})
mainWindow.loadURL('https://baidu.com',{extraHeaders: 'pragma: no-cache\n'})

SDK 部分,实际上也没有做太多的封装,直接就暴露出来了。
大概是下面这些

{
const storage = require('electron-json-storage'); // 缓存
const notifier = require('node-notifier') // 通知功能
const charset = require('superagent-charset');
const request = charset(require('superagent')); // HTTP
}

关于此项目 electron 暴露出来的内容,我能聊的大致就是这些了。至于图标,打包成可安装文件,客户端快捷键的设置,并没有太认真的去看,不过应该在网上多搜索,是能找到答案的。 electron-github-issues 实际上能解决绝大多数的问题。

session 模块

关于 electron session 模块,就和文档中的描述一致,session 模块可以用来创建一个新的 Session 对象,然后 有 session.fromPartition(partition) 进行自定义的设置。
你也可以通过使用 webContents 的属性 session 来使用一个已有页面的 session (webContents 是 BrowserWindow 的属性.)

在经过实际的测试发现,在主进程之外无法直接使用electron.session 来获取到 session 对象:{ defaultSession: [Getter], fromPartition: [Function] }

所以在最后,我只能是通过 webContents 中的 session 来处理。

当然就算是这样,也有很多解决方案,但是我目前使用了我认为最简单的一个。直接修改 本地所有的 cookies。

在 BrowserWindow 中

在文档中发现 可以直接在用 BrowserWindow 是可以直接通过 webPreferences 参数来对于 session 进行最初的设置的。

  • webPreferences 参数是个对象,它的属性:
    • session Session - 设置界面 session. 而不是直接忽略 session 对象 , 也可用 partition 来代替, 它接受一个 partition 字符串. 当同时使用 session 和 partition , session 优先级更高. 默认使用默认 session .
    • partition String - 通过 session 的 partition 字符串来设置界面 session. 如果 partition 以 persist: 开头, 这个界面将会为所有界面使用相同的 partition. 如果没有 persist: 前缀, 界面使用历史 session. 通过分享同一个 partition, 所有界面使用相同的 session. 默认使用默认 session.

在 webview 中

<webview src="https://github.com" partition="persist:github"></webview>
<webview src="http://electron.atom.io" partition="electron"></webview>

在 webview 中同样支持 partition 的设置。规则同上。

但是除此之外 webview 也同样提供了一个方法 <webview>.getWebContents()去获取 到 webview 所属的 webContents。 这样的话,我们也可以直接使用它 session 的属性进行处理

首先我们需要去创建出一个登陆窗口去让用户把账号给添加到目前的登陆流程中来。然后通过回调函数,将一个必要信息传到主窗口做登陆完成的处理(或者是使用 ipcMain EventEmitter 形式,最终只是需要拿到值。)

{
/**
* 创建一个 登录 的窗口。
* 用于 session 隔离
* Promise 中有 {partition,userinfo,cookies}
* @returns Promise
*/
function createLoginWin(partition){
partition = partition || `persist:${Math.random()}`;
const charset = require('superagent-charset');
const request = charset(require('superagent')); // HTTP
let BrowserWindow = new require('electron').remote.BrowserWindow;
let presWindow = new BrowserWindow({
width: 1280,
height: 768,
title:'用户登陆',
webPreferences: {
webSecurity: false,
allowRunningInsecureContent: true,
partition
}
});
let webContents = presWindow.webContents;
return new Promise(function(resove,reject){
// webContents.openDevTools();
presWindow.loadURL('http://taobao.com/#/login');
webContents.on("did-navigate-in-page", function() {
// 这里可以看情况进行参数的传递,获取制定的 cookies
webContents.session.cookies.get({},function(err,cookies){
if(err){
presWindow.close(); // 关闭登陆窗口
return reject(err);
}
// 这一步并不是必需的。
request
.get('http://taobao.com/userinfo')
.query({ _: Date.now() }) // query string
.set("Cookie", cookies.map(item=>`${item.name}=${item.value};`).join(' '))
.end(function(err,res){
presWindow.close();
if(err) {return reject(err);}
if(!res || !res.body || !res.body.result !== 1){
return reject(res.body)
}
let obj = { partition,cookies,userinfo:res.body.data}
resove(obj);
})
})
});
})
}
}

至于信息的存储的话,是使用了 electron-json-storage 将用户的值存储到本地。这里可以随意。

上面只是创建了新用户登录的窗口。那么对于旧有的(目前登录)用户信息,做一个初始化同步存储下来的操作。(保持结构一致,(除了 partition 不存在之外))为了后续的 使用方便,可以封装几个对于当前窗口 cookies 操作的函数

const cookies =  {
getCurrCookies(params={}){
let currWin = require('electron').remote.getCurrentWindow();
let currSession = currWin.webContents.session;
return new Promise((resove,reject)=>{
currSession.cookies.get(Object.assign({},params),function(err,cookies){
if(err){return reject(err);}
resove(cookies);
})
})
},
removeCurrCookies(cookies = []){
let currWin = require('electron').remote.getCurrentWindow();
let currSession = currWin.webContents.session;
let err = [];
let apiCount = 0;
return new Promise((resove,reject)=>{
cookies.forEach(item=>{
currSession.cookies.remove(`http://${item.domain}`,item.name ,function(err){
if(err){return err.push(err);}
apiCount = apiCount + 1;
})
if(err.length === apiCount){
resove({message:'cookie 清除成功'});
}else{
reject(err);
}
})
})
},
setCurrCookies(cookies = []){
let currWin = require('electron').remote.getCurrentWindow();
let currSession = currWin.webContents.session;
let err = [];
let apiCount = 0;
return new Promise((resove,reject)=>{
cookies.forEach(item=>{
currSession.cookies.set(Object.assign({},item,{
url:`http://${item.domain}`,
name:item.name
}),function(err){
if(err){
return err.push(err)
}
apiCount = apiCount + 1;
})
if(err.length === apiCount){
resove({message:'cookie 设置成功!'});
}else{
reject(err);
}
})
})
}
}

有了这几个函数。结合我们上面,将用户登录信息保存下来的部分,切换店铺就变得异常简单了。

流程如下:

获取当前 –> 清除当前 –> 获取目标 –> 设置当前 –> 重新载入

多 webview 聊天窗口

先来上一个截图。

006tKfTcgy1fkq9cead1tj30l20aw75e.jpg

在我的使用中,直接将聊天窗口创建出来,一个新的 BrowserWindow ,html 中会创建多个 webview

function openChatTool(data=[]){
// 需要打开的聊天窗口集合,里面会有我们在上面存下来的信息
let random = Math.random();
let BrowserWindow = new require('electron').remote.BrowserWindow;
let presWindow = new BrowserWindow({
width: 1280,
height: 768,
title:'聊天窗口',
webPreferences: {
webSecurity: false,
allowRunningInsecureContent: true,
}
});
// presWindow.webContents.openDevTools();
presWindow.loadURL(`http://${location.host}/chat.html?v=${Math.random()}`);
presWindow.webContents.on('did-finish-load', function() {
// 使用了 send 方法在线程中进行信息传递,在 chat.html 中 可以使用 ipcRenderer接受 如:electron.ipcRenderer.on('chatList',()=>{})
presWindow.webContents.send('chatList', data);
});
}

chat.html 中 tab 切换的部分在此直接略过

const electron = require('electron');
electron.ipcRenderer.on('chatList', function(event, data) {
console.log(data);
const webview = document.createElement('webview');
webview.allowpopups = true;
webview.disablewebsecurity = true;
webview.className = index == 0 ? 'active' : '';
// 直接使用 之前存下来的 partition,是最简单的形式
// 当然也可以不使用这个,在下面的事件中 session.cookies.set 将 cookie 设置进去
webview.partition = item.partition || `persist:${Math.random()}`;
webview.src="http://chat.com";
document.body.appendChild(webview);
webview.addEventListener('did-finish-load',function(event){
let webviewContents = webview.getWebContents();
if(webview.getURL()=='http://chat.com/index'){
webviewContents.webContents.session.cookies.get({},function(err,cookies){
// 处理登录失效。重新登录的逻辑。还需要结合别的事件来处理
// 这里可以直接拿到 webview 内的 session 信息
// 代码略
// 可以在外部插入代码
webview.executeJavaScript(`console.log(11)`,()=>{ console.log('insert dom success')})
})
}
})
})

这样需求就搞定了,但是实际上我用到的只是非常少的一部分,并且完成的也不算好。 单单是一个 session 模块中的东西我也还有很多没有去详细的尝试和理解的。不过这个需求整体下来,感觉 electron 还是非常有趣的。只不过接触的时间还太短,没有挖掘出更多有好玩的东西,要是之后有了时间,可以考虑用他写一个自己的应用吧。


Recommend

  • 39

    通常为了弄清楚一个概念,我们需要掌握十个概念。在判断 JWT (Json Web Token) 是否能代替 session 管理之前,我们要了解什么是 token,以及 access token 和 refresh token 的区别;了解什么是 OAuth,

  • 44

    浅谈Spring的事务隔离级别与传播性 这篇文章以一个问题开始,如果你知道答案的话就可以跳过不看啦@(o・ェ・)@ Q:在一个批量任务执行的过程中,调用多个子任务时,如果有一些子任务发生异常,只是回滚那些出现异常的任务,而...

  • 7

    AWS Session Manager 管理 EC2 实例 2021-02-16 — Yanbin 管理一个远程机器最常规的做法是 SSH(Unix/Linux, Mac) 或 PowerShell/RDP(Windows),这就要求远端机器要开通相应的访问端口及打开防火墙,配置好登陆用的用户名密码或 SSH Key。当选...

  • 1
    • blog.csdn.net 3 years ago
    • Cache

    第04课:Electron 应用代码管理

    第04课:Electron 应用代码管理 本课程使用 VS Co...

  • 10

    浅谈 SESSION_UPLOAD_PROGRESS 的利用 浅谈 SESSION_UPLOAD_PROGRESS 的利用 [toc] 今天来说一个老生常谈的东西吧,虽然已经很老了,但是在 CTF...

  • 4
    • my.oschina.net 3 years ago
    • Cache

    浅谈利用session绕过getshell

    在前些时间,国赛上再一次遇到了服务器本地文件包含session的漏洞,这是个老生常谈的东西了,但还是常常可以碰到,而我们想利用session来getshell往往还需要一些特殊的方法,借此机会,研究一番。 本文涉及相关...

  • 7

    基于vue3.0.11+electron13仿制macOS桌面UI管理系统ElectronVue3MacUI。 前段时间有分享一个vue3结合electron12开发后台管理系统项目。今天要分享的是最新研发的跨平台仿

  • 4
    • blog.51cto.com 2 years ago
    • Cache

    SpringSecurity-10-Session会话管理

    SpringSecurity-10-Session会话管理​理解Session ​Http协议是一种无状态协议所以当服务端需要记录用户的状态时,需要某种机制用于识别用户,这个机制就是​Session​。服务器通过和用户约定每一个请求携带一个id信息,用于统...

  • 8

    从会话管理到故障异常处理:Session机制在IoT设备中的全方位应用 产品@Devin 2023-03-15 0 评论...

  • 3
    • blog.ops-coffee.cn 9 months ago
    • Cache

    权限管理之多租户隔离授权

    权限管理之多租户隔离授权 想要做好权限管理,并不是一件容易的事情,既要考虑授权的粒度保证安全,也要考虑授权的方式足够便捷。之前有篇文章权...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK