前言
本文主要介紹的是SpringBoot如何整合Guacamole在瀏覽器是遠程桌面的訪問。
Guacamole 介紹
Apache Guacamole 是一個無客戶端遠程桌面網關。它支持標准協議,如 VNC、RDP 和 SSH。我們稱之為無客戶端,因為不需要插件或客戶端軟件。
用戶使用他們的網絡瀏覽器連接到 Guacamole 服務器。用 JavaScript 編寫的 Guacamole 客戶端由 Guacamole 服務器內的網絡服務器提供給用戶。加載后,此客戶端使用 Guacamole 協議通過 HTTP 連接回服務器。部署到 Guacamole 服務器的 Web 應用程序讀取 Guacamole 協議並將其轉發到 guacd,即原生的 Guacamole 代理。這個代理實際上解釋了 Guacamole 協議的內容,代表用戶連接到任意數量的遠程桌面服務器。Guacamole 協議與 guacd 結合提供了協議不可知性:Guacamole 客戶端和 Web 應用程序都不需要知道實際使用的是什么遠程桌面協議。
架構圖如下:

協議
Web 應用程序根本不了解任何遠程桌面協議。它不包含對 VNC 或 RDP 或由 Guacamole堆棧支持的任何其他協議的支持。它其實只懂Guacamole協議,這是一個用於遠程顯示渲染和事件傳輸的協議。雖然具有這些屬性的協議自然具有與遠程桌面協議相同的能力,但遠程桌面協議和 Guacamole 協議背后的設計原則是不同的:Guacamole 協議並非旨在實現特定桌面環境的功能。
作為遠程顯示和交互協議,Guacamole 實現了現有遠程桌面協議的超集。因此,向 Guacamole 添加對特定遠程桌面協議(如RDP)的支持涉及編寫一個中間層,在遠程桌面協議和 Guacamole 協議之間進行“轉換”。實現這樣的轉換與實現任何本地客戶端沒有什么不同,只是這個特定的實現呈現到遠程顯示器而不是本地顯示器。
處理這種翻譯的中間層是 guacd。
guacd
guacd 是 Guacamole 的核心,它動態加載對遠程桌面協議(稱為“客戶端插件”)的支持,並根據從 Web 應用程序收到的指令將它們連接到遠程桌面。 guacd 是一個守護進程,它與 Guacamole 一起安裝並在后台運行,監聽來自 Web 應用程序的 TCP 連接。guacd 也不理解任何特定的遠程桌面協議,而是實現了足夠的 Guacamole 協議來確定需要加載哪些協議支持以及必須將哪些參數傳遞給它。加載客戶端插件后,它會獨立於 guacd 運行,並完全控制自身與 Web 應用程序之間的通信,直到客戶端插件終止。 guacd 和所有客戶端插件都依賴於一個公共庫 libguac,這使得通過 Guacamole
協議進行的通信更容易也更抽象一些。
網絡應用程序
用戶實際與之交互的 Guacamole 部分是 Web 應用程序。
如前所述,Web 應用程序沒有實現任何遠程桌面協議。它依賴於 guacd,並且僅實現了一個漂亮的 Web 界面和身份驗證層。
我們選擇用 Java 實現 Web 應用程序的服務器端,但沒有理由不能用不同的語言編寫。事實上,因為 Guacamole 旨在成為一個 API,我們鼓勵這樣做。
RealMint
Guacamole 現在是一個通用的遠程桌面網關,但情況並非總是如此。Guacamole最初是用 JavaScript 編寫的純文本 Telnet 客戶端,稱為 RealMint(“RealMint”是“終端”的字謎)。它主要是為了演示而編寫的,雖然目的是有用,但它的主要名聲只是因為它是純 JavaScript。
RealMint 使用的隧道是用 PHP 編寫的。與 Guacamole 的 HTTP 隧道相比,RealMint 的隧道僅使用簡單的長輪詢,效率低下。RealMint 有一個不錯的鍵盤實現,現在在 Guacamole 的鍵盤代碼的一部分中存在,但這實際上是 RealMint 的功能和可用性的范圍。鑒於它只是一個遺留協議的實現,並且存在其他幾個 JavaScript 終端模擬器,其中大多數已經建立和穩定,該項目被放棄了。
VNC客戶端
一旦開發人員了解了 HTML5 canvas 標簽,並看到它已經在 Firefox 和 Chrome 中實現,就開始在概念驗證JavaScript VNC 客戶端上開展工作。
這個客戶端是帶有 Java 服務器組件的純 JavaScript,通過將 VNC 轉換為相同的基於 XML 的版本來工作。它的發展自然是由VNC 的特性驅動的,它的范圍僅限於將單個連接轉發給一組用戶。盡管相對較慢,但概念驗證運行良好,以至於該項目需要一個在線住所,並在SourceForge 上注冊為“Guacamole”——一個 HTML5 VNC 客戶端。
隨着 Guacamole 的發展並不僅僅是一個概念驗證,對速度的需求也增加了,舊的 RealMint 風格的長輪詢被放棄了,XML 的使用也是如此。
由於當時無法信任 WebSocket 得到支持,並且 Java 沒有用於 servlet 的 WebSocket 標准,因此開發了一個等效的基於 HTTP 的隧道。如果 WebSocket 因任何原因無法使用,此隧道今天仍在使用。
遠程桌面網關
開發了一種更快的基於文本的協議,它可以呈現多個遠程桌面協議的功能,而不僅僅是 VNC。整個系統被重新構建為一個標准守護進程 guacd 和一個公共庫 libguac,后者驅動守護進程和協議支持,並變得可擴展。 該項目的范圍從一個足夠的 VNC 客戶端擴展到一個高性能的 HTML5 遠程桌面網關和通用 API。在目前的狀態下,Guacamole可以用作中央網關來訪問運行不同遠程桌面服務器的任意數量的機器。它提供可擴展的身份驗證,如果您需要更專業的東西,則可以使用用於基於 HTML5 的遠程訪問的通用 API。
官網:http://guacamole.apache.org/
Guacamole 安裝
參考官網安裝: https://guacamole.apache.org/doc/gug/users-guide.html
建議使用docker安裝,可以快速安裝,docker安裝教程:https://www.cnblogs.com/xuwujing/p/15073174.html
Apache Guacamole分為服務端和客戶端,均在同一服務器上部署。
首先是下載鏡像,Guacamole數據庫可以選擇mysql或 PostgreSQL
docker pull guacamole/guacamole
docker pull guacamole/guacd
docker pull mysql/mysql-server
然后初始化mysql以及對mysql進行相關配置。
docker run --rm guacamole/guacamole /opt/guacamole/bin/initdb.sh --mysql > initdb.sql
docker run --name mysql --restart=always -e MYSQL_ROOT_PASSWORD=123456 -d mysql/mysql-server:5.7
docker cp initdb.sql mysql:/initdb.sql
sudo docker exec -it mysql mysql -uroot -p123456
CREATE DATABASE guacamole;
CREATE USER 'guacamole'@'%' IDENTIFIED BY 'guacamole';
GRANT SELECT,INSERT,UPDATE,DELETE ON guacamole.* TO 'guacamole'@'%';
FLUSH PRIVILEGES;
quit
docker exec -it mysql bashmysql –uroot –p123456 -Dguacamole<initdb.sql
依次啟動:
docker run --name guacd --restart=always -d guacamole/guacd
docker run --name guacamole --restart=always --link guacd:guacd --link mysql:mysql -e MYSQL_DATABASE='guacamole' -e MYSQL_USER='guacamole' -e MYSQL_PASSWORD='guacamole' -d -p 8080:8080 guacamole/guacamole

可以用 docker ps 命令查看是否啟動成功!
啟動成功之后,可以在瀏覽器上面輸入:http://ip:8080/guacamole 進行訪問,默認的賬號密碼都是guacadmin;

SpringBoot整合Guacamole
在整合之前,需要Guacamole已經啟動,且准備一台windows的遠程機。
首先是核心pom文件:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!-- Main Guacamole library -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common</artifactId>
<version>1.1.0</version>
</dependency>
<!-- Guacamole JavaScript library -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common-js</artifactId>
<version>1.1.0</version>
<type>zip</type>
<scope>runtime</scope>
</dependency>
示例代碼可以從這里下載:http://guacamole.apache.org/api-documentation/
這里我選擇的是java和js,這里必須要下載js文件,本篇文章示例在末尾,可以直接下載。

准備好了,編寫如下代碼,在示例代碼有,這里使用的是webSocket,也可以選擇http方式建立連接。
Java代碼如下:
@ServerEndpoint(value = "/webSocket", subprotocols = "guacamole")
@Component
public class WebSocketTunnel extends GuacamoleWebSocketTunnelEndpoint {
/**
* Returns a new tunnel for the given session. How this tunnel is created
* or retrieved is implementation-dependent.
*
* @param session The session associated with the active WebSocket
* connection.
* @param endpointConfig information associated with the instance of
* the endpoint created for handling this single connection.
* @return A connected tunnel, or null if no such tunnel exists.
* @throws GuacamoleException If an error occurs while retrieving the
* tunnel, or if access to the tunnel is denied.
*/
@Override
protected GuacamoleTunnel createTunnel(Session session, EndpointConfig endpointConfig) throws GuacamoleException {
//guacamole server地址 端口
String hostname = "192.168.0.1";
int port = 4822;
GuacamoleConfiguration configuration = new GuacamoleConfiguration();
configuration.setProtocol("rdp");
// 遠程windows服務的地址
configuration.setParameter("hostname", "192.168.0.2");
configuration.setParameter("port", "3389");
configuration.setParameter("username", "administrator");
configuration.setParameter("password", "123456");
configuration.setParameter("ignore-cert", "true");
GuacamoleSocket socket = new ConfiguredGuacamoleSocket(
new InetGuacamoleSocket(hostname, port),
configuration
);
GuacamoleTunnel tunnel = new SimpleGuacamoleTunnel(socket);
return tunnel;
}
JavaScript代碼主要是引用下載的js,使用http的話,核心使用代碼就是:
var guac = new Guacamole.Client(
new Guacamole.HTTPTunnel("tunnel")
);
WebSocket 代碼如下:
var guac = new Guacamole.Client(
new Guacamole.WebSocketTunnel("webSocket")
);
完整代碼如下:
<!DOCTYPE HTML>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html>
<head>
<link rel="stylesheet" type="text/css" href="guacamole.css"/>
<title>Guacamole (EXAMPLE)</title>
</head>
<body>
<h1>pancm Guacamole</h1>
<!-- Display -->
<div id="display"></div>
<!-- Guacamole JavaScript API -->
<script type="text/javascript"
src="guacamole-common-js/all.min.js"></script>
<!-- Init -->
<script type="text/javascript"> /* <![CDATA[ */
// Get display div from document
var display = document.getElementById("display");
// Instantiate client, using an HTTP tunnel for communications.
// httl方式
var guac = new Guacamole.Client(
new Guacamole.HTTPTunnel("tunnel")
);
// WebSocket 方式
var guac = new Guacamole.Client(
new Guacamole.WebSocketTunnel("webSocket")
);
// Add client to display div
display.appendChild(guac.getDisplay().getElement());
// Error handler
guac.onerror = function(error) {
alert(error);
};
// Connect
guac.connect();
// Disconnect on close
window.onunload = function() {
guac.disconnect();
}
// Mouse
var mouse = new Guacamole.Mouse(guac.getDisplay().getElement());
mouse.onmousedown =
mouse.onmouseup =
mouse.onmousemove = function(mouseState) {
guac.sendMouseState(mouseState);
};
// Keyboard
var keyboard = new Guacamole.Keyboard(document);
keyboard.onkeydown = function (keysym) {
guac.sendKeyEvent(1, keysym);
};
keyboard.onkeyup = function (keysym) {
guac.sendKeyEvent(0, keysym);
};
/* ]]> */ </script>
</body>
</html>
編寫完畢之后,我們啟動程序,在瀏覽器上面輸入localhost:9632/api 即可進行遠程桌面。
示例圖:

如何進行前后端分離
前后端在示例代碼中是未分離的,如果進行分離的話, 在http或WebSocket請求的前面加上ip和端口即可。
如下:
var guac = new Guacamole.Client(
new Guacamole.WebSocketTunnel("ws://192.168.0.1:9632/api/webSocket")
);
WebSocket方式如何傳遞參數以及取值
前端js在請求的url后面加上?key=value&key2=value2& 即可.
示例如下:
var guac = new Guacamole.Client(
new Guacamole.WebSocketTunnel("ws://192.168.0.1:9632/api/webSocket?ip=192.168.0.1&")
);
后端取值代碼示例:
String ip = session.getRequestParameterMap().get("ip").get(0);
如何設置屏幕寬高
在后端代碼中,添加如下配置:
GuacamoleClientInformation information = new GuacamoleClientInformation();
information.setOptimalScreenHeight(height);
information.setOptimalScreenWidth(width);
GuacamoleSocket socket = new ConfiguredGuacamoleSocket(
new InetGuacamoleSocket(hostname, port),
configuration,
information
);
如何設置錄屏
在后端代碼中,添加如下配置:
String fileName = getNowTime() + ".guac";//文件名
String outputFilePath = "/home/guacamole";
//添加會話錄制--錄屏
configuration.setParameter("recording-path", outputFilePath);
configuration.setParameter("create-recording-path", "true");
configuration.setParameter("recording-name", fileName);
錄屏的文件是.guac,我們需要對其進行解碼,命令如下:
guacenc 文件名稱
當然也可以對其設置參數配置,示例圖如下:

如果對生成的文件格式不滿意,想轉碼的話,可以使用ffmpeg進行轉碼。
如何關閉遠程桌面
關閉后端的session即可。代碼如下:
private void optClose(Session session) {
// 判斷當前連接是否還在線
if (session.isOpen()) {
try {
// 關閉連接
CloseReason closeReason = new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "進行關閉!");
session.close(closeReason);
} catch (IOException e) {
e.printStackTrace();
}
}
}
其他開源遠程桌面工具介紹
Teleport
Teleport是一款簡單易用的堡壘機系統,具有小巧、易用的特點,支持 RDP/SSH/SFTP/Telnet 協議的遠程連接和審計管理。
Teleport由兩大部分構成:
跳板核心服務 WEB操作界面
安裝方式
下載地址:
https://www.tp4a.com/download
Teleport 也是分為服務端和客戶端,服務端安裝如下,下載好文件之后,上傳在linux服務中進行安裝,命令如下:
tar -zxvf teleport-linux-3.0.2.9.tar.gz
cd teleport-linux-3.0.2.9
sudo ./setup.sh
相關命令:
啟動: /etc/init.d/teleport start
停止: /etc/init.d/teleport stop
重啟: /etc/init.d/teleport restart
查看運行狀態: /etc/init.d/teleport status
客戶端主要是在進行遠程桌面操作和錄像回放的時候使用, 安裝直接下載到本地運行即可。
啟動之后在瀏覽器輸入:http://teleport服務器IP:7190/ 進行配置即可。

更多請查閱官網!
官網地址: https://docs.tp4a.com/
JumpServer
JumpServer 使用 Python / Django 為主進行開發,遵循 Web 2.0 規范,配備了業界領先的 Web
Terminal 方案,交互界面美觀、用戶體驗好。JumpServer 采納分布式架構,支持多機房跨區域部署,支持橫向擴展,無資產數量及並發限制。
更多詳細了解請查閱官網。
官網地址:https://www.jumpserver.org/

其他
項目地址
項目工程地址:
https://github.com/xuwujing/springBoot-study/tree/master/springboot-guacamole
SpringBoot整個集合的地址:
https://github.com/xuwujing/springBoot-study
SpringBoot整合系列的文章
音樂推薦
原創不易,如果感覺不錯,希望給個推薦!您的支持是我寫作的最大動力!
版權聲明:
作者:虛無境
博客園出處:http://www.cnblogs.com/xuwujing
CSDN出處:http://blog.csdn.net/qazwsxpcm
個人博客出處:http://www.panchengming.com
