javaweb集成guacamole在html页面中展示 末蓝、 2023-02-13 11:09 332阅读 0赞 上一篇幅是介绍guacamole的安装,接下来说说使用 项目需求,需要在页面中集成远程桌面,要去掉基础认证。整体的方案有两种,都在这里说一下吧。 ***一、不需要guacamole客户端,在自己项目中实现socket通道。与页面进行连接(建议使用第二种)*** 1、环境准备 启动guacd服务 service guacd start 2、在自己的java项目中引入guacamole-common的包,版本与自己的guacd版本一致。 <dependency> <groupId>org.apache.guacamole</groupId> <artifactId>guacamole-common</artifactId> <version>1.0.0</version> </dependency> 3、自定义servlet实现GuacamoleHTTPTunnelServlet,官方截图如下 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FzZDM3MjUwNjU4OQ_size_16_color_FFFFFF_t_70][] 引入jar,之后创建MyGuacamoleHTTPTunnelServlet package com.xx.guacalome; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.InetGuacamoleSocket; import org.apache.guacamole.net.SimpleGuacamoleTunnel; import org.apache.guacamole.protocol.ConfiguredGuacamoleSocket; import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.servlet.GuacamoleHTTPTunnelServlet; import org.springframework.beans.factory.annotation.Value; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; /** * @description: * @Author: huangsan * @Date: 2020/5/27 11:01 上午 */ @WebServlet(urlPatterns = "/tunnel") public class MyGuacamoleHTTPTunnelServlet extends GuacamoleHTTPTunnelServlet { @Value("${guacamole.guacd.host}") private String guacdHost; @Value("${guacamole.guacd.port}") private String guacdPort; @Value("${guacamole.target.protocol}") private String targetProtocol; @Value("${guacamole.target.host}") private String targetHost; @Value("${guacamole.target.port}") private String targetPort; @Value("${guacamole.target.username}") private String targetUsername; @Value("${guacamole.target.password}") private String targetPassword; @Override protected GuacamoleTunnel doConnect(HttpServletRequest httpServletRequest) throws GuacamoleException { System.out.println("-----------远程桌面调用成功"); GuacamoleConfiguration config = new GuacamoleConfiguration(); config.setProtocol(targetProtocol); config.setParameter("hostname", targetHost); config.setParameter("port", targetPort); config.setParameter("username", targetUsername); config.setParameter("password", targetPassword); return new SimpleGuacamoleTunnel( new ConfiguredGuacamoleSocket(new InetGuacamoleSocket(this.guacdHost, Integer.parseInt(this.guacdPort)), config)); } } 在yml配置文件中配置我们需要的value guacamole: guacd: host: 10.0.30.50 port: 4822 target: protocol: rdp host: 10.0.30.224 port: 3389 username: admin password: 123456 启动类上配置@ServletComponentScan注解扫描servlet 启动项目即可,我们后台的通道就搭建完成 4、配置guacamole-common-js 创建前端html页面,在页面中定义展示区域的div。<div id="display"></div> all.min.js获取的方法,在maven中依赖 <dependency> <groupId>org.apache.guacamole</groupId> <artifactId>guacamole-common-js</artifactId> <version>1.0.0</version> </dependency> 然后去maven仓库中找到这个包,解压之后就可以找到相关jar。 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>Guacamole show</title> </head> <body> <script type="text/javascript" src="${pageContext.request.contextPath }/static/js/bigData/deviceMonitor/all.min.js"></script> <div id="display"></div> <script type="text/javascript"> let display = document.getElementById("display"); let guac = new Guacamole.Client( new Guacamole.HTTPTunnel("http://localhost/tunnel") // new Guacamole.WebSocketTunnel('ws://localhost/websocket-tunnel'), ); display.appendChild(guac.getDisplay().getElement()); guac.connect(); window.onunload = function () { guac.disconnect(); } //下面是鼠标键盘是否支持 let mouse = new Guacamole.Mouse(guac.getDisplay().getElement()); mouse.onmousedown = mouse.onmouseup = mouse.onmousemove = function (mouseState) { guac.sendMouseState(mouseState); }; let keyboard = new Guacamole.Keyboard(document); keyboard.onkeydown = function (keysym) { guac.sendKeyEvent(1, keysym); }; keyboard.onkeyup = function (keysym) { guac.sendKeyEvent(0, keysym); }; </script> </body> </html> guacamole client支持httptunnel或者websockettunnel 这里我们就配置一个httptunnel。 通道地址指向我们项目中暴露的servlet地址即可。 以上内容在guacamole的API中说的很详细,在页面中还支持展示更多内容,具体请参考html与js如下: [http://guacamole.apache.org/doc/gug/guacamole-common-js.html][http_guacamole.apache.org_doc_gug_guacamole-common-js.html] [http://guacamole.apache.org/doc/1.0.0/guacamole-common-js/][http_guacamole.apache.org_doc_1.0.0_guacamole-common-js] 二、依赖于guacamole的客户端和服务端来搞事情(以上的方法,需要在自己的项目中去引入其他的依赖,还不如利用其客户端当为服务一样使用) 1、环境准备 需要启动guacd的客户端和服务端,一个tomcat一个是service guacd start,就不赘述了。 2、与上面前端服务一样,创建html,指定展示区域div 3、编写js,如下(到这里就直接可以使用了,参数按照我这个来拼接就可以了,456节就是对这些参数的分析,如果是直接用的话就不用看下去了,当然想知道是什么的可以继续看下) guac.onerror = function (error) { alert(error); }; $.ajax({ type: "POST", async: false, url: "http://10.0.30.50:8888/g/api/tokens", data: "username=hhb&password=hhb123", }).done(function (result) { guac.connect("token=" + result.authToken + '&GUAC_DATA_SOURCE=default&GUAC_ID=test1&GUAC_TYPE=c&GUAC_WIDTH=900'); }).fail(function (result) { console.error("token error: ", result.status, result.statusText) }); window.onunload = function () { guac.disconnect(); } // Mouse 鼠标事件 let mouse = new Guacamole.Mouse(guac.getDisplay().getElement()); mouse.onmousedown = mouse.onmouseup = mouse.onmousemove = function(mouseState) { guac.sendMouseState(mouseState); }; // Keyboard 键盘事件 let keyboard = new Guacamole.Keyboard(document); keyboard.onkeydown = function (keysym) { guac.sendKeyEvent(1, keysym); }; keyboard.onkeyup = function (keysym) { guac.sendKeyEvent(0, keysym); }; 4、讲解一下js内容,首先要解决认证的问题 官网的描述是可以在connect中传递任意数据,那么我们将获取的的token等信息就可以在connect的时候进行传递。 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FzZDM3MjUwNjU4OQ_size_16_color_FFFFFF_t_70 1][] 那就先来获取token,我们看客户端登陆是如何获取token的,之后用ajax来模拟一个 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FzZDM3MjUwNjU4OQ_size_16_color_FFFFFF_t_70 2][] ajax代码如上所示。 token拿到之后我们要封装到connect中去,这里我在api中是没有看到任何说明指出connect怎么来拼接,没办法了反正是开源的,down一下代码来读一读吧 下载github上代码 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FzZDM3MjUwNjU4OQ_size_16_color_FFFFFF_t_70 3][] 拿到代码就好办了我们来看一下。 5、在源码中找到我们想要的 \---我们如果自己创建的话是创建一个tunnel的servlet,所以我们全局搜索“/tunnel”。 \---在TunnelModule类中我么看到了configureServlets方法,从这里我们看出有两种方式一种http一种websocket @Override protected void configureServlets() { bind(TunnelRequestService.class); // Set up HTTP tunnel serve("/tunnel").with(RestrictedGuacamoleHTTPTunnelServlet.class); // Try to load each WebSocket tunnel in sequence for (String classname : WEBSOCKET_MODULES) { if (loadWebSocketModule(classname)) { logger.debug("WebSocket module loaded: {}", classname); return; } } // Warn of lack of WebSocket logger.info("WebSocket support NOT present. Only HTTP will be used."); } \---进入http的创建中我们看到 @Singleton public class RestrictedGuacamoleHTTPTunnelServlet extends GuacamoleHTTPTunnelServlet { /** * Service for handling tunnel requests. */ @Inject private TunnelRequestService tunnelRequestService; /** * Logger for this class. */ private static final Logger logger = LoggerFactory.getLogger(RestrictedGuacamoleHTTPTunnelServlet.class); @Override protected GuacamoleTunnel doConnect(HttpServletRequest request) throws GuacamoleException { // Attempt to create HTTP tunnel GuacamoleTunnel tunnel = tunnelRequestService.createTunnel(new HTTPTunnelRequest(request)); // If successful, warn of lack of WebSocket logger.info("Using HTTP tunnel (not WebSocket). Performance may be sub-optimal."); return tunnel; } } 发现了一个和我们之前的实现思路一样的继承GuacamoleHTTPTunnelServlet \---我们看到对HTTPTunnelRequest进行了封装参数是我么的HttpServletRequest \---点进去发现其实就是把我们的httpservlet的参数拿出来而已 public HTTPTunnelRequest(HttpServletRequest request) { // For each parameter for (Map.Entry<String, String[]> mapEntry : ((Map<String, String[]>) request.getParameterMap()).entrySet()) { // Get parameter name and corresponding values String parameterName = mapEntry.getKey(); List<String> parameterValues = Arrays.asList(mapEntry.getValue()); // Store copy of all values in our own map parameterMap.put( parameterName, new ArrayList<String>(parameterValues) ); } } \---看来我们只要把参数给http请求就可以了,那我们再来看看都需要哪些参数点到tunnelRequestService.createTunnel方法中 public GuacamoleTunnel createTunnel(TunnelRequest request) throws GuacamoleException { // Parse request parameters String authToken = request.getAuthenticationToken(); String id = request.getIdentifier(); TunnelRequest.Type type = request.getType(); String authProviderIdentifier = request.getAuthenticationProviderIdentifier(); GuacamoleClientInformation info = getClientInformation(request); GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); UserContext userContext = session.getUserContext(authProviderIdentifier); try { // Create connected tunnel using provided connection ID and client information GuacamoleTunnel tunnel = createConnectedTunnel(userContext, type, id, info); // Notify listeners to allow connection to be vetoed fireTunnelConnectEvent(session.getAuthenticatedUser(), session.getAuthenticatedUser().getCredentials(), tunnel); // Associate tunnel with session return createAssociatedTunnel(tunnel, authToken, session, userContext, type, id); } // Ensure any associated session is invalidated if unauthorized catch (GuacamoleUnauthorizedException e) { // If there is an associated auth token, invalidate it if (authenticationService.destroyGuacamoleSession(authToken)) logger.debug("Implicitly invalidated session for token \"{}\".", authToken); // Continue with exception processing throw e; } } \---重点来看 String authToken = request.getAuthenticationToken(); String id = request.getIdentifier(); TunnelRequest.Type type = request.getType(); String authProviderIdentifier = request.getAuthenticationProviderIdentifier(); GuacamoleClientInformation info = getClientInformation(request); 首先前四个request的方法我们可以点进去看看,无非就是获取参数,但是id\\type\\authProviderIdentifier这三个参数都是必须的 public String getAuthenticationProviderIdentifier() throws GuacamoleException { return getRequiredParameter(AUTH_PROVIDER_IDENTIFIER_PARAMETER); } \---既然知道了参数的key,那我们就可以在前端来模拟了,具体都有哪些参数可以参考 /* * 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. */ package org.apache.guacamole.tunnel; import java.util.List; import org.apache.guacamole.GuacamoleClientException; import org.apache.guacamole.GuacamoleClientException; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException; /** * A request object which provides only the functions absolutely required to * retrieve and connect to a tunnel. */ public abstract class TunnelRequest { /** * The name of the request parameter containing the user's authentication * token. */ public static final String AUTH_TOKEN_PARAMETER = "token"; /** * The name of the parameter containing the identifier of the * AuthenticationProvider associated with the UserContext containing the * object to which a tunnel is being requested. */ public static final String AUTH_PROVIDER_IDENTIFIER_PARAMETER = "GUAC_DATA_SOURCE"; /** * The name of the parameter specifying the type of object to which a * tunnel is being requested. Currently, this may be "c" for a Guacamole * connection, or "g" for a Guacamole connection group. */ public static final String TYPE_PARAMETER = "GUAC_TYPE"; /** * The name of the parameter containing the unique identifier of the object * to which a tunnel is being requested. */ public static final String IDENTIFIER_PARAMETER = "GUAC_ID"; /** * The name of the parameter containing the desired display width, in * pixels. */ public static final String WIDTH_PARAMETER = "GUAC_WIDTH"; /** * The name of the parameter containing the desired display height, in * pixels. */ public static final String HEIGHT_PARAMETER = "GUAC_HEIGHT"; /** * The name of the parameter containing the desired display resolution, in * DPI. */ public static final String DPI_PARAMETER = "GUAC_DPI"; /** * The name of the parameter specifying one supported audio mimetype. This * will normally appear multiple times within a single tunnel request - * once for each mimetype. */ public static final String AUDIO_PARAMETER = "GUAC_AUDIO"; /** * The name of the parameter specifying one supported video mimetype. This * will normally appear multiple times within a single tunnel request - * once for each mimetype. */ public static final String VIDEO_PARAMETER = "GUAC_VIDEO"; /** * The name of the parameter specifying one supported image mimetype. This * will normally appear multiple times within a single tunnel request - * once for each mimetype. */ public static final String IMAGE_PARAMETER = "GUAC_IMAGE"; /** * All supported object types that can be used as the destination of a * tunnel. */ public static enum Type { /** * A Guacamole connection. */ CONNECTION("c"), /** * A Guacamole connection group. */ CONNECTION_GROUP("g"); /** * The parameter value which denotes a destination object of this type. */ final String PARAMETER_VALUE; /** * Defines a Type having the given corresponding parameter value. * * @param value * The parameter value which denotes a destination object of this * type. */ Type(String value) { PARAMETER_VALUE = value; } }; //下面没用的都删掉了 } 那我们来分析一下我们需要传的参数 "token=" + result.authToken + '&GUAC\_DATA\_SOURCE=default&GUAC\_ID=test1&GUAC\_TYPE=c&GUAC\_WIDTH=900' \---token:不用说,为什么不是必须的参数,参考api的快速连接部分。 \---GUAC\_DATA\_SOURCE:在获取token的时候就传递回来了,我这里是default {"authToken":"x","username":"hhb","dataSource":"default","availableDataSources":["quickconnect","default"]} \---GUAC\_ID:就是我们user-mapping中的connect的name \---GUAC\_TYPE:看源码发现是个枚举类型,这里我们没引入组的概念(1.1.0版本有),这里直接传c public static enum Type { /** * A Guacamole connection. */ CONNECTION("c"), /** * A Guacamole connection group. */ CONNECTION_GROUP("g"); /** * The parameter value which denotes a destination object of this type. */ final String PARAMETER_VALUE; /** * Defines a Type having the given corresponding parameter value. * * @param value * The parameter value which denotes a destination object of this * type. */ Type(String value) { PARAMETER_VALUE = value; } }; \---GUAC\_WIDTH:这属于随便的参数了,看个人需要我就指定了个宽度,你们可以自由继续拼装。 5、我们知道了connect中需要传递的一些参数,那就直接拼接出来即可,直接调用创建隧道。 6、总结:这种方法好处就是不用自己在项目中引入guacamole的内容了,不好的地方是我们需要解决跨域问题,虽然guacamole是个tomcat,但是我无论怎么配置跨域都没解决,没办法最后使用nginx来解决了。 以上就是web集成guacamole的两种方式了,其实思路就是一种,创建隧道与guacamole-common-js连接就可以了,有什么问题欢迎大家指出。 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FzZDM3MjUwNjU4OQ_size_16_color_FFFFFF_t_70]: https://img-blog.csdnimg.cn/20200528151615716.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FzZDM3MjUwNjU4OQ==,size_16,color_FFFFFF,t_70 [http_guacamole.apache.org_doc_gug_guacamole-common-js.html]: http://guacamole.apache.org/doc/gug/guacamole-common-js.html [http_guacamole.apache.org_doc_1.0.0_guacamole-common-js]: http://guacamole.apache.org/doc/1.0.0/guacamole-common-js/ [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FzZDM3MjUwNjU4OQ_size_16_color_FFFFFF_t_70 1]: https://img-blog.csdnimg.cn/20200528160251523.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FzZDM3MjUwNjU4OQ==,size_16,color_FFFFFF,t_70 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FzZDM3MjUwNjU4OQ_size_16_color_FFFFFF_t_70 2]: https://img-blog.csdnimg.cn/20200528160542337.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FzZDM3MjUwNjU4OQ==,size_16,color_FFFFFF,t_70 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FzZDM3MjUwNjU4OQ_size_16_color_FFFFFF_t_70 3]: https://img-blog.csdnimg.cn/2020052816082450.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2FzZDM3MjUwNjU4OQ==,size_16,color_FFFFFF,t_70
还没有评论,来说两句吧...