enseñarle a poner en práctica una versión web de Xshell (con fuente) Java puro

prefacio

Debido a los requisitos de proyectos recientes, necesidad proyecto para implementar un bornes de conexión función WebSSH, a causa de su primera época de hacer este tipo de función, por lo que en primer lugar en el GitHub en busca de encontrar que no hay ruedas preparadas pueden ser usados ​​directa, que vio una gran cantidad de este los aspectos del proyecto, tales como: GateOne, webssh, shellinabox, estos proyectos pueden realizar la función de webssh.

Pero en última instancia no adoptó, la razón subyacente es que éstas se han escrito sobre todo en Python, necesidad de contar con una gran cantidad de archivos, puede utilizar el tiempo que utilizan este esquema, rápida respuesta, pero el tiempo para hacer un proyecto para los usuarios, puede no requiere que el usuario haga el servidor debe estar incluido en estos dependencia subyacente, lo que obviamente no es razonable, por lo que decidí escribir a sí mismo una función WebSSH, y como un proyecto de código abierto fuera independiente.

github dirección de proyecto de código abierto: https: //github.com/NoCortY/WebSSH

Selección de la tecnología

Debido webssh necesidad de intercambio de datos en tiempo real, siempre va a utilizar la conexión WebSocket, con el fin de facilitar el desarrollo del marco elegido SpringBoot, además de su conocimiento de los usuarios de Java xterm.js a ssh y la aplicación de jsch la página de conexión carcasa frontal.

Por lo tanto, la selección de la tecnología final es WebSocket + + + jsch SpringBoot xterm.js .

dependencia de las importaciones

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.7.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>
<dependencies>
    <!-- Web相关 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- jsch支持 -->
    <dependency>
        <groupId>com.jcraft</groupId>
        <artifactId>jsch</artifactId>
        <version>0.1.54</version>
    </dependency>
    <!-- WebSocket 支持 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    <!-- 文件上传解析器 -->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>1.4</version>
    </dependency>
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
    </dependency>
</dependencies>

Un caso sencillo xterm

Desde el xterm es una tecnología popular, por lo que una gran cantidad de estudiantes no apoyó este conocimiento, también estoy con el fin de conseguir esta funcionalidad de manera temporal la escuela, por lo que este le diga acerca.

xterm.js WebSocket es una basada en contenedores, que pueden ayudar a lograr el estilo de línea de comandos en la parte delantera. Y a continuación, ya que normalmente como SecureCRT o XShell cuando se conecta al servidor.

Aquí está el caso del oficial de inscripción en línea:

<!doctype html>
 <html>
  <head>
    <link rel="stylesheet" href="node_modules/xterm/css/xterm.css" />
    <script src="node_modules/xterm/lib/xterm.js"></script>
  </head>
  <body>
    <div id="terminal"></div>
    <script>
      var term = new Terminal();
      term.open(document.getElementById('terminal'));
      term.write('Hello from x1B[1;3;31mxterm.jsx1B[0m $ ')
    </script>
  </body>
 </html>

La prueba final, la página se verá así:

enseñarle a poner en práctica una versión web de Xshell (con fuente) Java puroentrada xterm

Se puede ver la página no haber sido similar al estilo concha, a continuación, sobre la base de que esto continúe en profundidad, para lograr un webssh.

aplicación de back-end

Porque mientras xterm sólo para lograr una interfaz de estilo, en realidad no interactúan con el servidor, interactúa el servidor con el se basan principalmente en nuestro back-end de Java para el control, por lo que partimos de la parte de atrás, usando jsch + WebSocket alcanzar esta parte.

Configuración WebSocket

Como el mensaje en tiempo real impulso a la necesidad frente a la utilización de WebSocket, no entienden los estudiantes WebSocket pueden ir por su cuenta para averiguar, no hay demasiada descripción, empezamos inmediatamente configuración WebSocket.

  /**
  * @Description: websocket配置
  * @Author: NoCortY
  * @Date: 2020/3/8
  */
  @Configuration
  @EnableWebSocket
  public class WebSSHWebSocketConfig implements WebSocketConfigurer{
      @Autowired
      WebSSHWebSocketHandler webSSHWebSocketHandler;
      @Override
      public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
          //socket通道
          //指定处理器和路径,并设置跨域
          webSocketHandlerRegistry.addHandler(webSSHWebSocketHandler, "/webssh")
                  .addInterceptors(new WebSocketInterceptor())
                  .setAllowedOrigins("*");
      }
  }

Procesador (Handler) y el interceptor (Interceptor) aplicación

Acabamos de completar la configuración de WebSocket, y especificar un procesador y los interceptores. Por lo que el siguiente paso es implementar el procesador y el interceptor.

interceptor :

  public class WebSocketInterceptor implements HandshakeInterceptor {
      /**
       * @Description: Handler处理前调用
       * @Param: [serverHttpRequest, serverHttpResponse, webSocketHandler, map]
       * @return: boolean
       * @Author: NoCortY
       * @Date: 2020/3/1
       */
      @Override
      public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
          if (serverHttpRequest instanceof ServletServerHttpRequest) {
              ServletServerHttpRequest request = (ServletServerHttpRequest) serverHttpRequest;
              //生成一个UUID,这里由于是独立的项目,没有用户模块,所以可以用随机的UUID
              //但是如果要集成到自己的项目中,需要将其改为自己识别用户的标识
              String uuid = UUID.randomUUID().toString().replace("-","");
              //将uuid放到websocketsession中
              map.put(ConstantPool.USER_UUID_KEY, uuid);
              return true;
          } else {
              return false;
          }
      }

      @Override
      public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {

      }
  }

procesador :

  /**
  * @Description: WebSSH的WebSocket处理器
  * @Author: NoCortY
  * @Date: 2020/3/8
  */
  @Component
  public class WebSSHWebSocketHandler implements WebSocketHandler{
      @Autowired
      private WebSSHService webSSHService;
      private Logger logger = LoggerFactory.getLogger(WebSSHWebSocketHandler.class);

      /**
       * @Description: 用户连接上WebSocket的回调
       * @Param: [webSocketSession]
       * @return: void
       * @Author: Object
       * @Date: 2020/3/8
       */
      @Override
      public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
          logger.info("用户:{},连接WebSSH", webSocketSession.getAttributes().get(ConstantPool.USER_UUID_KEY));
          //调用初始化连接
          webSSHService.initConnection(webSocketSession);
      }

      /**
       * @Description: 收到消息的回调
       * @Param: [webSocketSession, webSocketMessage]
       * @return: void
       * @Author: NoCortY
       * @Date: 2020/3/8
       */
      @Override
      public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception {
          if (webSocketMessage instanceof TextMessage) {
              logger.info("用户:{},发送命令:{}", webSocketSession.getAttributes().get(ConstantPool.USER_UUID_KEY), webSocketMessage.toString());
              //调用service接收消息
              webSSHService.recvHandle(((TextMessage) webSocketMessage).getPayload(), webSocketSession);
          } else if (webSocketMessage instanceof BinaryMessage) {

          } else if (webSocketMessage instanceof PongMessage) {

          } else {
              System.out.println("Unexpected WebSocket message type: " + webSocketMessage);
          }
      }

      /**
       * @Description: 出现错误的回调
       * @Param: [webSocketSession, throwable]
       * @return: void
       * @Author: Object
       * @Date: 2020/3/8
       */
      @Override
      public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
          logger.error("数据传输错误");
      }

      /**
       * @Description: 连接关闭的回调
       * @Param: [webSocketSession, closeStatus]
       * @return: void
       * @Author: NoCortY
       * @Date: 2020/3/8
       */
      @Override
      public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
          logger.info("用户:{}断开webssh连接", String.valueOf(webSocketSession.getAttributes().get(ConstantPool.USER_UUID_KEY)));
          //调用service关闭连接
          webSSHService.close(webSocketSession);
      }

      @Override
      public boolean supportsPartialMessages() {
          return false;
      }
  }

Tenga en cuenta que, me uní al interceptor en el usuario utiliza un UUID al azar , ya que como un proyecto independiente WebSocket, ningún módulo de usuario, si necesita este proyecto en su proyecto, es necesario modificarlo parte del código, que identifica al usuario que identifique a sí mismo en un programa utilizado por el usuario.

WebSSH aplicación lógica de negocio (core)

simplemente nos hemos dado cuenta de configuración WebSocket, son algunos de código muerto, e implementa la interfaz entonces se puede realizar de acuerdo a sus necesidades, y ahora vamos a lograr mayor lógica de negocio de servicios de fondo, antes de implementar esta lógica, vamos a pensar en ello, WebSSH que sobre todo queremos mostrar un poco efecto.

Estoy aquí para hacer un resumen:

1. En primer lugar tenemos que conectar el (conexión de inicialización) Terminal

2. En segundo lugar, tenemos que manejar el servidor de mensajes desde el extremo frontal (la parte delantera para recibir y procesar mensajes)

3. Tenemos que ser escrita de nuevo al mensaje devuelto desde el frente-extremo terminal (datos write-back front-end)

4. Cerrar la conexión

De acuerdo con estos cuatro requisitos, en primer lugar definir una interfaz, por lo que puede hacer que los requisitos desaparecen.

  /**
   * @Description: WebSSH的业务逻辑
   * @Author: NoCortY
   * @Date: 2020/3/7
   */
  public interface WebSSHService {
      /**
       * @Description: 初始化ssh连接
       * @Param:
       * @return:
       * @Author: NoCortY
       * @Date: 2020/3/7
       */
      public void initConnection(WebSocketSession session);

      /**
       * @Description: 处理客户段发的数据
       * @Param:
       * @return:
       * @Author: NoCortY
       * @Date: 2020/3/7
       */
      public void recvHandle(String buffer, WebSocketSession session);

      /**
       * @Description: 数据写回前端 for websocket
       * @Param:
       * @return:
       * @Author: NoCortY
       * @Date: 2020/3/7
       */
      public void sendMessage(WebSocketSession session, byte[] buffer) throws IOException;

      /**
       * @Description: 关闭连接
       * @Param:
       * @return:
       * @Author: NoCortY
       * @Date: 2020/3/7
       */
      public void close(WebSocketSession session);
  }

Ahora podemos ir a darse cuenta de la función que hemos definido de acuerdo con esta interfaz.

1. Conexión de inicialización

Dado que somos dependientes de los subyacentes jsch lograr, así que aquí es necesario para utilizar jsch para establecer una conexión. La llamada de inicialización de la conexión, de hecho, es conectar la información que necesitamos, salvo en un mapa, no hay ninguna operación de conexión real. Por qué no hay conexión directa? Debido a que no es sólo la conexión de punta en el WebSocket, pero también necesitamos que nos envíe a la terminal de Linux nombre de usuario y contraseña frontal, sin esta información, no somos capaces de conectar.

     public void initConnection(WebSocketSession session) {
             JSch jSch = new JSch();
             SSHConnectInfo sshConnectInfo = new SSHConnectInfo();
             sshConnectInfo.setjSch(jSch);
             sshConnectInfo.setWebSocketSession(session);
             String uuid = String.valueOf(session.getAttributes().get(ConstantPool.USER_UUID_KEY));
             //将这个ssh连接信息放入map中
             sshMap.put(uuid, sshConnectInfo);
     }

2. El tratamiento de los datos enviados por el cliente

En este paso, se dividirá en dos ramas.

  • La primera rama: Si el cliente se envía al nombre de usuario y la contraseña de información terminal, a continuación, conectamos el terminal.

  • La segunda rama: Si el cliente se envía a las operaciones de comando de terminal, entonces vamos a ser enviada directamente a la terminal y obtener los resultados de la terminal.

la implementación del código específico:

     public void recvHandle(String buffer, WebSocketSession session) {
             ObjectMapper objectMapper = new ObjectMapper();
             WebSSHData webSSHData = null;
             try {
                 //转换前端发送的JSON
                 webSSHData = objectMapper.readValue(buffer, WebSSHData.class);
             } catch (IOException e) {
                 logger.error("Json转换异常");
                 logger.error("异常信息:{}", e.getMessage());
                 return;
             }
         //获取刚才设置的随机的uuid
             String userId = String.valueOf(session.getAttributes().get(ConstantPool.USER_UUID_KEY));
             if (ConstantPool.WEBSSH_OPERATE_CONNECT.equals(webSSHData.getOperate())) {
                 //如果是连接请求
                 //找到刚才存储的ssh连接对象
                 SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(userId);
                 //启动线程异步处理
                 WebSSHData finalWebSSHData = webSSHData;
                 executorService.execute(new Runnable() {
                     @Override
                     public void run() {
                         try {
                             //连接到终端
                             connectToSSH(sshConnectInfo, finalWebSSHData, session);
                         } catch (JSchException | IOException e) {
                             logger.error("webssh连接异常");
                             logger.error("异常信息:{}", e.getMessage());
                             close(session);
                         }
                     }
                 });
             } else if (ConstantPool.WEBSSH_OPERATE_COMMAND.equals(webSSHData.getOperate())) {
                 //如果是发送命令的请求
                 String command = webSSHData.getCommand();
                 SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(userId);
                 if (sshConnectInfo != null) {
                     try {
                         //发送命令到终端
                         transToSSH(sshConnectInfo.getChannel(), command);
                     } catch (IOException e) {
                         logger.error("webssh连接异常");
                         logger.error("异常信息:{}", e.getMessage());
                         close(session);
                     }
                 }
             } else {
                 logger.error("不支持的操作");
                 close(session);
             }
     }

3. Los datos se envían al extremo frontal por WebSocket

     public void sendMessage(WebSocketSession session, byte[] buffer) throws IOException {
             session.sendMessage(new TextMessage(buffer));
     }

4. Cerrar la conexión

     public void close(WebSocketSession session) {
         //获取随机生成的uuid
             String userId = String.valueOf(session.getAttributes().get(ConstantPool.USER_UUID_KEY));
             SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(userId);
             if (sshConnectInfo != null) {
                 //断开连接
                 if (sshConnectInfo.getChannel() != null) sshConnectInfo.getChannel().disconnect();
                 //map中移除该ssh连接信息
                 sshMap.remove(userId);
             }
     }

En este punto, toda nuestra implementación de servicios de fondo es más, debido a las limitaciones de espacio, algunas operaciones que aquí se convertirá en el método de envasado, no hacer demasiadas espectáculos, destacar algunas pensamiento lógico para lograrlo. A continuación vamos a alcanzar la parte delantera.

aplicación front-end

trabajo front-end se divide en tan pocos pasos:

  1. Realización de la página

  2. conexión WebSocket y completar la recepción de los datos y la posterior escritura

  3. El envío de datos

Por lo tanto, damos un paso a paso para lograrlo.

implementación página

Implementar página es muy simple, sólo tenemos que mostrar la clase de gran pantalla del terminal negro en una pantalla completa, por lo que hacemos todo lo que no escribir con estilo, basta con crear un div, después de lo cual el terminal xterm en este caso por la div, se puede lograr.

  <!doctype html>
  <html>
  <head>
      <title>WebSSH</title>
      <link rel="stylesheet" href="../css/xterm.css" />
  </head>
  <body>
  <div id="terminal" style="width: 100%;height: 100%"></div>

  <script src="../lib/jquery-3.4.1/jquery-3.4.1.min.js"></script>
  <script src="../js/xterm.js" charset="utf-8"></script>
  <script src="../js/webssh.js" charset="utf-8"></script>
  <script src="../js/base64.js" charset="utf-8"></script>
  </body>
  </html>

conexión WebSocket y completa la transmisión de datos, recibir, reescritura

  openTerminal( {
      //这里的内容可以写死,但是要整合到项目中时,需要通过参数的方式传入,可以动态连接某个终端。
          operate:'connect',
          host: 'ip地址',
          port: '端口号',
          username: '用户名',
          password: '密码'
      });
      function openTerminal(options){
          var client = new WSSHClient();
          var term = new Terminal({
              cols: 97,
              rows: 37,
              cursorBlink: true, // 光标闪烁
              cursorStyle: "block", // 光标样式  null | 'block' | 'underline' | 'bar'
              scrollback: 800, //回滚
              tabStopWidth: 8, //制表宽度
              screenKeys: true
          });

          term.on('data', function (data) {
              //键盘输入时的回调函数
              client.sendClientData(data);
          });
          term.open(document.getElementById('terminal'));
          //在页面上显示连接中...
          term.write('Connecting...');
          //执行连接操作
          client.connect({
              onError: function (error) {
                  //连接失败回调
                  term.write('Error: ' + error + 'rn');
              },
              onConnect: function () {
                  //连接成功回调
                  client.sendInitData(options);
              },
              onClose: function () {
                  //连接关闭回调
                  term.write("rconnection closed");
              },
              onData: function (data) {
                  //收到数据时回调
                  term.write(data);
              }
          });
      }

Los resultados muestran

conexión

enseñarle a poner en práctica una versión web de Xshell (con fuente) Java puroconexión

La conexión se realiza correctamente

enseñarle a poner en práctica una versión web de Xshell (con fuente) Java puroLa conexión se realiza correctamente

Operación de comandos

comando ls:

enseñarle a poner en práctica una versión web de Xshell (con fuente) Java purocomando ls

editor vim:

enseñarle a poner en práctica una versión web de Xshell (con fuente) Java puroeditor vim

la parte superior de comandos:

enseñarle a poner en práctica una versión web de Xshell (con fuente) Java purocomando top

epílogo

Luego completamos la puesta en práctica de un proyecto webssh, sin depender de cualquiera de los otros componentes, el extremo posterior se implementa completamente en Java, debido al uso de SpringBoot, muy fácil de implementar.

Pero también puede extenderse al proyecto, tales como archivos nuevos de carga o descarga, como Xftp desea, usted puede fácilmente arrastrar los archivos de subida y caída y descarga.

Después de este proyecto en el que se actualizará continuamente, estas funciones se darán cuenta de forma gradual, Github: https: //github.com/NoCortY/WebSSH

Al igual que puedo dar Estrella oh ~

Supongo que te gusta

Origin www.cnblogs.com/javazhiyin/p/12468477.html
Recomendado
Clasificación