using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
namespace AmtScanner.Api.Services;
///
/// Guacamole 远程桌面服务
///
public class GuacamoleService
{
private readonly ILogger _logger;
private readonly HttpClient _httpClient;
private readonly IConfiguration _config;
private string? _authToken;
private DateTime _tokenExpiry = DateTime.MinValue;
public GuacamoleService(ILogger logger, IHttpClientFactory httpClientFactory, IConfiguration config)
{
_logger = logger;
_httpClient = httpClientFactory.CreateClient("Guacamole");
_config = config;
}
///
/// 获取 Guacamole 认证 Token
///
public async Task GetAuthTokenAsync()
{
// 如果 token 还有效,直接返回
if (!string.IsNullOrEmpty(_authToken) && DateTime.Now < _tokenExpiry)
{
return _authToken;
}
try
{
var baseUrl = _config["Guacamole:BaseUrl"] ?? "http://localhost:8080/guacamole";
var username = _config["Guacamole:Username"] ?? "guacadmin";
var password = _config["Guacamole:Password"] ?? "guacadmin";
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair("username", username),
new KeyValuePair("password", password)
});
var response = await _httpClient.PostAsync($"{baseUrl}/api/tokens", content);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize(json);
_authToken = result.GetProperty("authToken").GetString();
_tokenExpiry = DateTime.Now.AddMinutes(30); // Token 有效期约 30 分钟
_logger.LogInformation("Guacamole 认证成功");
return _authToken;
}
else
{
_logger.LogError("Guacamole 认证失败: {Status}", response.StatusCode);
return null;
}
}
catch (Exception ex)
{
_logger.LogError(ex, "获取 Guacamole Token 失败");
return null;
}
}
///
/// 创建 RDP 连接
///
public async Task CreateRdpConnectionAsync(string name, string hostname, string username, string password, int port = 3389)
{
var token = await GetAuthTokenAsync();
if (string.IsNullOrEmpty(token)) return null;
try
{
var baseUrl = _config["Guacamole:BaseUrl"] ?? "http://localhost:8080/guacamole";
var dataSource = _config["Guacamole:DataSource"] ?? "mysql";
var connection = new
{
parentIdentifier = "ROOT",
name = name,
protocol = "rdp",
parameters = new
{
hostname = hostname,
port = port.ToString(),
username = username,
password = password,
security = "any",
ignoreCert = "true",
enableDrive = "true",
createDrivePath = "true"
},
attributes = new
{
maxConnections = "",
maxConnectionsPerUser = ""
}
};
var json = JsonSerializer.Serialize(connection);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(
$"{baseUrl}/api/session/data/{dataSource}/connections?token={token}",
content);
if (response.IsSuccessStatusCode)
{
var resultJson = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize(resultJson);
var connectionId = result.GetProperty("identifier").GetString();
_logger.LogInformation("创建 RDP 连接成功: {Id}", connectionId);
return connectionId;
}
else
{
var error = await response.Content.ReadAsStringAsync();
_logger.LogError("创建 RDP 连接失败: {Status} - {Error}", response.StatusCode, error);
return null;
}
}
catch (Exception ex)
{
_logger.LogError(ex, "创建 RDP 连接异常");
return null;
}
}
///
/// 创建 VNC 连接
///
public async Task CreateVncConnectionAsync(string name, string hostname, string password, int port = 5900)
{
var token = await GetAuthTokenAsync();
if (string.IsNullOrEmpty(token)) return null;
try
{
var baseUrl = _config["Guacamole:BaseUrl"] ?? "http://localhost:8080/guacamole";
var dataSource = _config["Guacamole:DataSource"] ?? "mysql";
var connection = new
{
parentIdentifier = "ROOT",
name = name,
protocol = "vnc",
parameters = new
{
hostname = hostname,
port = port.ToString(),
password = password
},
attributes = new
{
maxConnections = "",
maxConnectionsPerUser = ""
}
};
var json = JsonSerializer.Serialize(connection);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(
$"{baseUrl}/api/session/data/{dataSource}/connections?token={token}",
content);
if (response.IsSuccessStatusCode)
{
var resultJson = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize(resultJson);
var connectionId = result.GetProperty("identifier").GetString();
_logger.LogInformation("创建 VNC 连接成功: {Id}", connectionId);
return connectionId;
}
else
{
var error = await response.Content.ReadAsStringAsync();
_logger.LogError("创建 VNC 连接失败: {Status} - {Error}", response.StatusCode, error);
return null;
}
}
catch (Exception ex)
{
_logger.LogError(ex, "创建 VNC 连接异常");
return null;
}
}
///
/// 删除连接
///
public async Task DeleteConnectionAsync(string connectionId)
{
var token = await GetAuthTokenAsync();
if (string.IsNullOrEmpty(token)) return false;
try
{
var baseUrl = _config["Guacamole:BaseUrl"] ?? "http://localhost:8080/guacamole";
var dataSource = _config["Guacamole:DataSource"] ?? "mysql";
var response = await _httpClient.DeleteAsync(
$"{baseUrl}/api/session/data/{dataSource}/connections/{connectionId}?token={token}");
if (response.IsSuccessStatusCode)
{
_logger.LogInformation("删除连接成功: {Id}", connectionId);
return true;
}
else
{
_logger.LogError("删除连接失败: {Status}", response.StatusCode);
return false;
}
}
catch (Exception ex)
{
_logger.LogError(ex, "删除连接异常");
return false;
}
}
///
/// 获取连接 URL(用于 iframe 嵌入)
///
public async Task GetConnectionUrlAsync(string connectionId)
{
var token = await GetAuthTokenAsync();
if (string.IsNullOrEmpty(token)) return null;
var baseUrl = _config["Guacamole:BaseUrl"] ?? "http://localhost:8080/guacamole";
var dataSource = _config["Guacamole:DataSource"] ?? "mysql";
// 构建客户端连接 URL
// 格式: BASE_URL/#/client/BASE64(connectionId + '\0' + 'c' + '\0' + dataSource)
var clientId = $"{connectionId}\0c\0{dataSource}";
var encodedClientId = Convert.ToBase64String(Encoding.UTF8.GetBytes(clientId));
return $"{baseUrl}/#/client/{encodedClientId}?token={token}";
}
///
/// 获取所有连接
///
public async Task> GetConnectionsAsync()
{
var token = await GetAuthTokenAsync();
if (string.IsNullOrEmpty(token)) return new List();
try
{
var baseUrl = _config["Guacamole:BaseUrl"] ?? "http://localhost:8080/guacamole";
var dataSource = _config["Guacamole:DataSource"] ?? "mysql";
var response = await _httpClient.GetAsync(
$"{baseUrl}/api/session/data/{dataSource}/connections?token={token}");
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
var connections = JsonSerializer.Deserialize>(json,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
return connections?.Values.ToList() ?? new List();
}
}
catch (Exception ex)
{
_logger.LogError(ex, "获取连接列表失败");
}
return new List();
}
}
public class GuacamoleConnection
{
public string Identifier { get; set; } = "";
public string Name { get; set; } = "";
public string Protocol { get; set; } = "";
public string ParentIdentifier { get; set; } = "";
}