252 lines
9.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Text;
using System.Text.Json;
namespace AmtScanner.Api.Services;
public interface IGuacamoleService
{
Task<string?> GetAuthTokenAsync();
Task<string?> CreateOrGetConnectionAsync(string token, string connectionName, string hostname, string username, string password);
Task<string?> GetConnectionUrlAsync(string token, string connectionId);
}
public class GuacamoleService : IGuacamoleService
{
private readonly HttpClient _httpClient;
private readonly ILogger<GuacamoleService> _logger;
private readonly IConfiguration _configuration;
private string GuacamoleBaseUrl => _configuration["Guacamole:BaseUrl"] ?? "http://localhost:8080/guacamole";
private string GuacamoleUsername => _configuration["Guacamole:Username"] ?? "guacadmin";
private string GuacamolePassword => _configuration["Guacamole:Password"] ?? "guacadmin";
public GuacamoleService(HttpClient httpClient, ILogger<GuacamoleService> logger, IConfiguration configuration)
{
_httpClient = httpClient;
_logger = logger;
_configuration = configuration;
}
/// <summary>
/// 获取 Guacamole 认证 Token
/// </summary>
public async Task<string?> GetAuthTokenAsync()
{
try
{
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("username", GuacamoleUsername),
new KeyValuePair<string, string>("password", GuacamolePassword)
});
var response = await _httpClient.PostAsync($"{GuacamoleBaseUrl}/api/tokens", content);
if (!response.IsSuccessStatusCode)
{
_logger.LogError("Failed to get Guacamole token: {Status}", response.StatusCode);
return null;
}
var json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
if (doc.RootElement.TryGetProperty("authToken", out var tokenElement))
{
return tokenElement.GetString();
}
return null;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting Guacamole token");
return null;
}
}
/// <summary>
/// 创建或获取已存在的连接
/// </summary>
public async Task<string?> CreateOrGetConnectionAsync(string token, string connectionName, string hostname, string username, string password)
{
try
{
// 先查找是否已存在同名连接
var existingId = await FindConnectionByNameAsync(token, connectionName);
if (existingId != null)
{
// 更新连接参数
await UpdateConnectionAsync(token, existingId, connectionName, hostname, username, password);
return existingId;
}
// 创建新连接
var connectionData = new Dictionary<string, object>
{
["name"] = connectionName,
["parentIdentifier"] = "ROOT",
["protocol"] = "rdp",
["parameters"] = new Dictionary<string, string>
{
["hostname"] = hostname,
["port"] = "3389",
["username"] = username,
["password"] = password,
["security"] = "any",
["ignore-cert"] = "true",
["resize-method"] = "display-update",
["enable-wallpaper"] = "true",
["enable-theming"] = "true",
["enable-font-smoothing"] = "true",
["enable-full-window-drag"] = "true",
["enable-desktop-composition"] = "true",
["enable-menu-animations"] = "true",
["disable-bitmap-caching"] = "false",
["disable-offscreen-caching"] = "false",
["disable-glyph-caching"] = "false",
// 启用驱动器重定向(文件传输)
["enable-drive"] = "true",
["drive-name"] = "共享文件夹",
["drive-path"] = "/drive",
["create-drive-path"] = "true"
},
["attributes"] = new Dictionary<string, string>()
};
var json = JsonSerializer.Serialize(connectionData);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(
$"{GuacamoleBaseUrl}/api/session/data/mysql/connections?token={token}",
content);
if (!response.IsSuccessStatusCode)
{
var error = await response.Content.ReadAsStringAsync();
_logger.LogError("Failed to create connection: {Status} - {Error}", response.StatusCode, error);
return null;
}
var responseJson = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(responseJson);
if (doc.RootElement.TryGetProperty("identifier", out var idElement))
{
return idElement.GetString();
}
return null;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating Guacamole connection");
return null;
}
}
/// <summary>
/// 查找已存在的连接
/// </summary>
private async Task<string?> FindConnectionByNameAsync(string token, string connectionName)
{
try
{
var response = await _httpClient.GetAsync(
$"{GuacamoleBaseUrl}/api/session/data/mysql/connections?token={token}");
if (!response.IsSuccessStatusCode)
{
return null;
}
var json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
foreach (var prop in doc.RootElement.EnumerateObject())
{
if (prop.Value.TryGetProperty("name", out var nameElement) &&
nameElement.GetString() == connectionName)
{
return prop.Name;
}
}
return null;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error finding connection by name");
return null;
}
}
/// <summary>
/// 更新连接参数
/// </summary>
private async Task UpdateConnectionAsync(string token, string connectionId, string connectionName, string hostname, string username, string password)
{
try
{
var connectionData = new Dictionary<string, object>
{
["name"] = connectionName,
["parentIdentifier"] = "ROOT",
["protocol"] = "rdp",
["identifier"] = connectionId,
["parameters"] = new Dictionary<string, string>
{
["hostname"] = hostname,
["port"] = "3389",
["username"] = username,
["password"] = password,
["security"] = "any",
["ignore-cert"] = "true",
["resize-method"] = "display-update",
["enable-wallpaper"] = "true",
["enable-theming"] = "true",
["enable-font-smoothing"] = "true",
["enable-full-window-drag"] = "true",
["enable-desktop-composition"] = "true",
["enable-menu-animations"] = "true",
// 启用驱动器重定向(文件传输)
["enable-drive"] = "true",
["drive-name"] = "共享文件夹",
["drive-path"] = "/drive",
["create-drive-path"] = "true"
},
["attributes"] = new Dictionary<string, string>()
};
var json = JsonSerializer.Serialize(connectionData);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var request = new HttpRequestMessage(HttpMethod.Put,
$"{GuacamoleBaseUrl}/api/session/data/mysql/connections/{connectionId}?token={token}")
{
Content = content
};
await _httpClient.SendAsync(request);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error updating connection");
}
}
/// <summary>
/// 获取连接 URL用于 iframe 嵌入)
/// </summary>
public async Task<string?> GetConnectionUrlAsync(string token, string connectionId)
{
// Guacamole 客户端 URL 格式
// 连接标识符需要 Base64 编码: connectionId + "\0" + "c" + "\0" + "mysql"
var connectionIdentifier = $"{connectionId}\0c\0mysql";
var encodedId = Convert.ToBase64String(Encoding.UTF8.GetBytes(connectionIdentifier));
return $"{GuacamoleBaseUrl}/#/client/{encodedId}?token={token}";
}
}