标签:show 问题 点击 ica mac padding 页面 submit isa
在上一篇讲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的截图问题(2)——蛋疼的cookies验证和node.js的websocket代理
标签:show 问题 点击 ica mac padding 页面 submit isa
原文地址:https://www.cnblogs.com/dgutfly/p/11359434.html