如何解决Vue.js里面noVNC的截图问题——蛋疼的cookies验证和node.js的websocket代理
Posted dgutfly
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何解决Vue.js里面noVNC的截图问题——蛋疼的cookies验证和node.js的websocket代理相关的知识,希望对你有一定的参考价值。
在上一篇讲noVNC截图功能的文章中,我们利用WebSocket协议的连接不检查跨域的特性解决了noVNC截图失败的问题。
但是这个方法仅限于没有cookies验证的noVNC服务,但是openstack的noVNC服务,每个虚拟机都要带token,而这个token,是写在cookies里面的。
看了openstack虚拟桌面的代码,知道里面的代码用token = WebUtil.getQueryVar(‘token‘, null);和WebUtil.createCookie(‘token‘, token, 1)这两句语句获取token再放入cookies,但是把代码放入第1章的Vue代码里面,连接失败。调出F12抓包窗口,发现由于headers关于跨域没设置好,cookies浏览器不认,没有传入服务器。
解决这个问题的思路是:找一个无视跨域限制,能传cookies的webSocket的客户端,并且对应系统也能开websocket服务端,这个系统作为openstack和主网站的webSocket代理传输数据,Node.js就可以用来解决,刚好整个Vue.js前端项目基于node.js。
var WebSocketServer = require(‘websocket‘).server; var http = require(‘http‘); var WebSocketClient = require(‘websocket‘).client; var server1 = http.createServer(function(request, response) console.log((new Date()) + ‘ Server is reseiveing on port 4949‘); response.writeHead(204); response.end(); ); server1.listen(4949, function() console.log((new Date()) + ‘ Server is listening on port 4949‘); ); var wsServer = new WebSocketServer(httpServer: server1,autoAcceptConnections: false); wsServer.on(‘request‘, function(request) var token = request[‘resourceURL‘][‘query‘][‘token‘]; var ip = request[‘resourceURL‘][‘query‘][‘ip‘]; var client = new WebSocketClient(); //模拟noVNC页面连接服务的过程 client.connect(‘ws://‘ + ip + ‘/websockify‘,‘binary‘, ‘http://‘ + ip,‘Cookie‘:‘token=‘+token); client.on(‘connectFailed‘,error=>console.log(error);); //browserConnection就是客户端的webSocket连接 var browserConnection = request.accept(‘binary‘, request.origin); client.on(‘connect‘, function(connection) //connection就是服务端的连接 connection.on(‘error‘, function(error) console.log("Connection Error: " + error.toString()); if(browserConnection != null)browserConnection.close(); browserConnection = null; ); connection.on(‘close‘, function() console.log("Connection Close"); if(browserConnection != null)browserConnection.close(); browserConnection = null; ); connection.on(‘message‘, function(message) if (message.type === ‘utf8‘) browserConnection.sendUTF(message.utf8Data); else if(message.type === ‘binary‘) browserConnection.sendBytes(message.binaryData); ); browserConnection.on(‘message‘, function(message) if (message.type === ‘utf8‘) connection.sendUTF(message.utf8Data); else if (message.type === ‘binary‘) connection.sendBytes(message.binaryData); ); browserConnection.on(‘error‘, function(error) console.log("BrowserConnection Error: " + error.toString()); if(connection != null)connection.close(); connection = null; ); browserConnection.on(‘close‘, function(reasonCode, description) console.log("BrowserConnection Close"); if(connection != null)connection.close(); connection = null; ); ); );
客户端的Vue.js代码
<template> <div id="noVNC_all"> <div id="noVNC_status_bar"> <div id="noVNC_left_dummy_elem"></div> <div id="noVNC_status">Loading</div> <div id="noVNC_buttons"> <input type=button value="Send CtrlAltDel" id="sendCtrlAltDelButton" class="noVNC_shown"> <span id="noVNC_power_buttons" class="noVNC_hidden"> <input type=button value="Shutdown" id="machineShutdownButton"> <input type=button value="Reboot" id="machineRebootButton"> <input type=button value="Reset" id="machineResetButton"> </span> </div> </div> </div> </template> <script> import * as WebUtil from ‘./webutil.js‘; import RFB from ‘@novnc/novnc/core/rfb.js‘; export default components: , data() return rfb:null, desktopName:null ; , methods: connectVNC () , updateDesktopName(e) this.desktopName = e.detail.name; , credentials(e) var html; var form = document.createElement(‘form‘); form.innerHTML = ‘<label></label>‘; form.innerHTML += ‘<input type=password size=10 id="password_input">‘; form.onsubmit = this.setPassword; // bypass status() because it sets text content document.getElementById(‘noVNC_status_bar‘).setAttribute("class", "noVNC_status_warn"); document.getElementById(‘noVNC_status‘).innerHTML = ‘‘; document.getElementById(‘noVNC_status‘).appendChild(form); document.getElementById(‘noVNC_status‘).querySelector(‘label‘).textContent = ‘Password Required: ‘; , setPassword() this.rfb.sendCredentials( password: document.getElementById(‘password_input‘).value ); return false; , sendCtrlAltDel() this.rfb.sendCtrlAltDel(); return false; , machineShutdown() this.rfb.machineShutdown(); return false; , machineReboot() this.rfb.machineReboot(); return false; , machineReset() this.rfb.machineReset(); return false; , status(text, level) switch (level) case ‘normal‘: case ‘warn‘: case ‘error‘: break; default: level = "warn"; document.getElementById(‘noVNC_status_bar‘).className = "noVNC_status_" + level; document.getElementById(‘noVNC_status‘).textContent = text; , connected(e) document.getElementById(‘sendCtrlAltDelButton‘).disabled = false; if (WebUtil.getConfigVar(‘encrypt‘, (window.location.protocol === "https:"))) this.status("Connected (encrypted) to " + this.desktopName, "normal"); else this.status("Connected (unencrypted) to " + this.desktopName, "normal"); , disconnected(e) document.getElementById(‘sendCtrlAltDelButton‘).disabled = true; this.updatePowerButtons(); if (e.detail.clean) this.status("Disconnected", "normal"); else this.status("Something went wrong, connection is closed", "error"); , updatePowerButtons() var powerbuttons; powerbuttons = document.getElementById(‘noVNC_power_buttons‘); if (this.rfb.capabilities.power) powerbuttons.className= "noVNC_shown"; else powerbuttons.className = "noVNC_hidden"; , mounted() document.getElementById(‘sendCtrlAltDelButton‘).onclick = this.sendCtrlAltDel; document.getElementById(‘machineShutdownButton‘).onclick = this.machineShutdown; document.getElementById(‘machineRebootButton‘).onclick = this.machineReboot; document.getElementById(‘machineResetButton‘).onclick = this.machineReset; WebUtil.init_logging(WebUtil.getConfigVar(‘logging‘, ‘warn‘)); document.title = WebUtil.getConfigVar(‘title‘, ‘noVNC‘); // By default, use the host and port of server that served this file var host = WebUtil.getConfigVar(‘host‘, window.location.hostname); var port = WebUtil.getConfigVar(‘port‘, window.location.port); // if port == 80 (or 443) then it won‘t be present and should be // set manually if (!port) if (window.location.protocol.substring(0,5) == ‘https‘) port = 443; else if (window.location.protocol.substring(0,4) == ‘http‘) port = 80; if(this.$route.params.ipport.indexOf(‘-‘) == -1) var password = WebUtil.getConfigVar(‘password‘, ‘123456‘); else var password = WebUtil.getConfigVar(‘password‘, ‘‘); var path = WebUtil.getConfigVar(‘path‘, ‘websockify‘); // If a token variable is passed in, set the parameter in a cookie. // This is used by nova-novncproxy. var token = WebUtil.getConfigVar(‘token‘, null); if (token) // if token is already present in the path we should use it path = WebUtil.injectParamIfMissing(path, "token", token); WebUtil.createCookie(‘token‘, token, 1) this.status("Connecting", "normal"); if ((!host) || (!port)) this.status(‘Must specify host and port in URL‘, ‘error‘); var url; if (WebUtil.getConfigVar(‘encrypt‘, (window.location.protocol === "https:"))) url = ‘wss‘; else url = ‘ws‘; if(this.$route.params.ipport == null) url += ‘://192.168.80.61:30926/websockify‘; else if(this.$route.params.ipport.indexOf(‘-‘) == -1)//对于符合非openstack链接的ip端口处理 url += ‘://‘ + this.$route.params.ipport + ‘/websockify‘; else//对于符合openstack链接的ip端口处理 url += ‘://localhost:4949/websockify/websockify?token=‘ + this.$route.params.ipport.split(‘:-‘)[1] + ‘&ip=‘ + this.$route.params.ipport.split(‘:-‘)[0]; this.rfb = new RFB(document.querySelector(‘#noVNC_all‘), url, repeaterID: WebUtil.getConfigVar(‘repeaterID‘, ‘‘), shared: WebUtil.getConfigVar(‘shared‘, true), credentials: password: password ); this.rfb.viewOnly = WebUtil.getConfigVar(‘view_only‘, false); this.rfb.addEventListener("connect", this.connected); this.rfb.addEventListener("disconnect", this.disconnected); this.rfb.addEventListener("capabilities", function () this.updatePowerButtons(); ); this.rfb.addEventListener("credentialsrequired", this.credentials); this.rfb.addEventListener("desktopname", this.updateDesktopName); this.rfb.scaleViewport = WebUtil.getConfigVar(‘scale‘, false); this.rfb.resizeSession = WebUtil.getConfigVar(‘resize‘, false); ; </script> <style lang=‘scss‘ scoped> #noVNC_status_bar width: 100%; display:flex; justify-content: space-between; #noVNC_status color: #fff; font: bold 12px Helvetica; margin: auto; .noVNC_status_normal background: linear-gradient(#b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); .noVNC_status_error background: linear-gradient(#c83737 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); .noVNC_status_warn background: linear-gradient(#b4b41e 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); .noNVC_shown display: inline; .noVNC_hidden display: none; #noVNC_left_dummy_elem flex: 1; #noVNC_buttons padding: 1px; flex: 1; display: flex; justify-content: flex-end; </style>
将node.js代理程序开起来之后,登陆对应页面。再点击截图按钮,可以看到截图成功了,并且之前的cookies跨域问题也没有了。
以上是关于如何解决Vue.js里面noVNC的截图问题——蛋疼的cookies验证和node.js的websocket代理的主要内容,如果未能解决你的问题,请参考以下文章
如何使用`tsc`和`npm install`解决鸡/蛋情况?
之前一直因为更新mac系统cocoapods问题,比较蛋疼比较隐蔽