如何解决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;
        );
    );
);
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跨域问题也没有了。

以上是关于如何解决Vue.js里面noVNC的截图问题——蛋疼的cookies验证和node.js的websocket代理的主要内容,如果未能解决你的问题,请参考以下文章

如何使用`tsc`和`npm install`解决鸡/蛋情况?

Vue.js项目实战-打造线上商城

之前一直因为更新mac系统cocoapods问题,比较蛋疼比较隐蔽

vue.js 2.x 版本script里面的dom被过滤,从而获取不到dom字符串的解决方案

如何解决QQ热键冲突的问题

vue.js初级教程--02.环境搭建