配置段/环境变量、Launcher、健康检查 API、OpenAPI 与前后端文案同步;兼容探测旧 SimpleLite 进程名。 Co-authored-by: Cursor <cursoragent@cursor.com>
83 lines
3.5 KiB
C#
83 lines
3.5 KiB
C#
using System.Security.Cryptography;
|
||
|
||
namespace MiGu.Server.Auth;
|
||
|
||
/// <summary>
|
||
/// MiGu.Server ↔ Simple3 8222 之间的 **内部共享 token**。
|
||
///
|
||
/// 设计动机:Simple3 8222 的 EmbedIO WebApi(ReflectionApi / PersistenceApi / MapEditApi)
|
||
/// 历史上完全无鉴权,远程访问 = 接管调度内核。为不在 Simple3 侧实现完整 JWT 验签
|
||
/// (减少 Simple3 复杂度),采用一个轻量约定:
|
||
///
|
||
/// - <b>本机回环</b>(127.0.0.1 / ::1)Simple3 直接放行(开发机直连不受影响);
|
||
/// - <b>其它来源</b>必须携带 <c>X-Platform-Internal-Token</c> header;
|
||
/// - MiGu.Server YARP 反代 <c>/api/sl/*</c> 到 Simple3 8222 时,YARP transform
|
||
/// 会自动追加该 header;
|
||
/// - Simple3 端通过 <c>simple.json:platform.internalToken</c> 或环境变量
|
||
/// <c>SIMPLE3__PLATFORM__INTERNALTOKEN</c> 配置同一个 token;两端不一致即拒绝。
|
||
///
|
||
/// Token 来源优先级:
|
||
/// 1) appsettings.json:Internal:Token 显式配置(生产推荐:strong, length ≥ 32)
|
||
/// 2) 环境变量 PLATFORM__INTERNAL__TOKEN
|
||
/// 3) 兜底:进程随机 64 字节 base64,并在日志告警 + 写入 <c>data/.internal-token</c>
|
||
/// 文件供本机 Simple3 读取(同机部署常见场景)
|
||
/// </summary>
|
||
public sealed class InternalTokenStore
|
||
{
|
||
public string Token { get; }
|
||
public bool IsEphemeral { get; }
|
||
public string? PersistedFilePath { get; }
|
||
|
||
public InternalTokenStore(IConfiguration config, IWebHostEnvironment env, ILogger<InternalTokenStore> logger)
|
||
{
|
||
var configured = config["Internal:Token"];
|
||
if (!string.IsNullOrWhiteSpace(configured) && configured != "REPLACE_ME")
|
||
{
|
||
Token = configured;
|
||
IsEphemeral = false;
|
||
logger.LogInformation("Internal token 从配置读取(长度 {Len})。", Token.Length);
|
||
return;
|
||
}
|
||
|
||
// 兜底:进程随机 + 落地到 data/.internal-token 便于同机 Simple3 读取
|
||
var dataDir = Path.Combine(env.ContentRootPath, "data");
|
||
Directory.CreateDirectory(dataDir);
|
||
var file = Path.Combine(dataDir, ".internal-token");
|
||
if (File.Exists(file))
|
||
{
|
||
try
|
||
{
|
||
var existing = File.ReadAllText(file).Trim();
|
||
if (existing.Length >= 32)
|
||
{
|
||
Token = existing;
|
||
IsEphemeral = false;
|
||
PersistedFilePath = file;
|
||
logger.LogInformation("Internal token 复用 {File}(长度 {Len})。", file, Token.Length);
|
||
return;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.LogWarning(ex, "读取 {File} 失败,将重新生成 internal token。", file);
|
||
}
|
||
}
|
||
|
||
Token = Convert.ToBase64String(RandomNumberGenerator.GetBytes(48));
|
||
IsEphemeral = true;
|
||
try
|
||
{
|
||
File.WriteAllText(file, Token);
|
||
PersistedFilePath = file;
|
||
logger.LogWarning(
|
||
"Internal token 未配置 —— 已生成进程随机值并写入 {File}(重启后保留)。" +
|
||
"若 MiGu.Server 与 Simple3 不在同一台机器,请把同一个值写入 Simple3 端 simple.json:platform.internalToken。",
|
||
file);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.LogError(ex, "internal token 落盘失败 —— 远程 Simple3 调用将无法通过鉴权(每次重启 token 都不同)。");
|
||
}
|
||
}
|
||
}
|