如何解决Vue.js里面noVNC的截图问题(2)——蛋疼的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;
        });
    });
});
Node.js websocket代理

  客户端的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>
客户端的Vue.js代码

  将node.js代理程序开起来之后,登陆对应页面。再点击截图按钮,可以看到截图成功了,并且之前的cookies跨域问题也没有了。

转载于:https://www.cnblogs.com/dgutfly/p/11359434.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值