feat: 网关管理模块 + RBAC权限认证 + 组织架构 + 审计日志
- 网关管理:GatewayEntity/Service/Controller CRUD + 测试连接;设备 GatewayId 外键关联 - JWT 认证:登录签发 Token(权限编码写入 Claims)、RequirePermission 权限过滤器、CurrentUser 上下文 - RBAC:用户/角色/权限实体与 CRUD,预定义 4 角色 + 6 权限种子(admin/admin123) - 组织架构:sys_org 固定层级树(公司/实验室/部门/班组白夜班),层级校验,用户挂 OrgId/岗位/技能标签 - 数据权限:角色 DataScope(全部/本组织及下级),用户列表按组织子树过滤 - 防锁死保护:禁止删/禁自己,保证至少一名活跃管理员,角色摘除 user:manage 前校验 - 审计日志:AuditRecorder 接入设备增删改/指令下发/登录登出/组织变更,AuditController 查询 - 设备指令下发按网关表取连接参数;设备列表支持 gatewayId/productId 筛选 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Service.Implement
|
||||
{
|
||||
/// <summary>
|
||||
/// 密码哈希工具(PBKDF2-SHA256,格式 salt$hash,盐随机 16 字节)
|
||||
/// </summary>
|
||||
public static class PasswordHelper
|
||||
{
|
||||
private const int Iterations = 10_000;
|
||||
private const int SaltSize = 16;
|
||||
private const int HashSize = 32;
|
||||
|
||||
/// <summary>生成密码哈希(salt$hash)</summary>
|
||||
public static string Hash(string password)
|
||||
{
|
||||
var salt = RandomNumberGenerator.GetBytes(SaltSize);
|
||||
var hash = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations, HashAlgorithmName.SHA256, HashSize);
|
||||
return $"{Convert.ToHexString(salt)}${Convert.ToHexString(hash)}";
|
||||
}
|
||||
|
||||
/// <summary>校验密码是否匹配</summary>
|
||||
public static bool Verify(string password, string? stored)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(stored)) return false;
|
||||
var parts = stored.Split('$');
|
||||
if (parts.Length != 2) return false;
|
||||
var salt = Convert.FromHexString(parts[0]);
|
||||
var expected = Convert.FromHexString(parts[1]);
|
||||
var actual = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations, HashAlgorithmName.SHA256, expected.Length);
|
||||
return CryptographicOperations.FixedTimeEquals(actual, expected);
|
||||
}
|
||||
|
||||
/// <summary>生成随机盐字符串(供外部使用)</summary>
|
||||
public static string NewSalt()
|
||||
{
|
||||
var salt = RandomNumberGenerator.GetBytes(SaltSize);
|
||||
return Convert.ToHexString(salt).ToLowerInvariant();
|
||||
}
|
||||
|
||||
/// <summary>SHA256(供 RefreshToken 等场景)</summary>
|
||||
public static string Sha256(string input)
|
||||
{
|
||||
var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(input));
|
||||
return Convert.ToHexString(bytes).ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user