.Net Core2.2 使用Identity时替换ORM(Dapper替换EF、Oracle替换Sqlserver)层 (一):基本配置

参考文档:

1、https://docs.microsoft.com/zh-cn/aspnet/core/security/?view=aspnetcore-2.2

2、https://github.com/aspnet/AspNetCore.Docs/tree/live/aspnetcore/security/authentication/identity/sample/src

3、https://blog.youkuaiyun.com/sD7O95O/article/details/78623698

在Core2.2中,Identity的ORM默认使用了EF,但是这样,一些老项目迁移过程中就会遇到问题,因为很多项目还是使用的sql语句的方式访问数据库,这种情况下,显然不能重构项目为EF,不过,微软还是给Identity留下了自定义ORM的入口。

因为官方默认使用EF+sqlserver且使用了CodeFirst,所以下边的例子则为dapper+oracle,完全使用sql语句访问数据库,尽可能做到差异化。

 

一、数据库结构:

   因为是简单demo,所以配置一个简单的数据库,一个表,两个列足矣

    sys_user(userid varchar2,username varchar2,password varchar2)

二、新建一个core2.2项目:

测试一下,可以运行:

二、基础配置:

    1、appsettings.json配置文件中配置连接字符串“OracleConStr”:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "OracleConStr": "Data Source=****;Persist Security Info=True;User ID=**;Password=**;"
}

    2、使用NuGet安装所需类库:

1、Dapper
2、Oracle.ManagedDataAccess.Core

    3、创建SysUser实体类

    public class SysUser : IIdentity
    {
        //必须实现的接口
        public string AuthenticationType { get; set; }
        public bool IsAuthenticated { get; set; }
        public string Name { get; set; }

        //根据表自定义的字段
        public string UserID { get; set; }
        public string UserName { get; set; }
        public string NormalizedUserName { get; set; }
        public string Password { get; set; }
        public string PasswordHash { get; set; }
    }

    4、创建表sysuser对应的Dal类SysUserDal,使用Dapper实现基本的增删改查

    public class SysUserDal
    {
        private readonly OracleConnection _connection;
        public SysUserDal(OracleConnection connection)
        {
            _connection = connection;
        }

        public async Task<IdentityResult> CreateAsync(SysUser user)
        {
            string sql = "insert into sys_user (userid,username,password) values (:userid, :username,:password)";

            int rows = await _connection.ExecuteAsync(sql, new { userid = user.UserID, username = user.UserName, password = user.PasswordHash });

            if (rows > 0)
            {
                return IdentityResult.Success;
            }
            return IdentityResult.Failed(new IdentityError { Description = $"Could not insert user {user.UserID}." });
        }

        public async Task<IdentityResult> DeleteAsync(SysUser user)
        {
            string sql = "delete from sys_user where userid = :userid";
            int rows = await _connection.ExecuteAsync(sql, new { user.UserID });

            if (rows > 0)
            {
                return IdentityResult.Success;
            }
            return IdentityResult.Failed(new IdentityError { Description = $"Could not delete user {user.UserID}." });
        }

        public async Task<IdentityResult> UpdateAsync(SysUser user)
        {
            string sql = "update sys_user set password=:password,username=:username where userid=:userid";
            int rows = await _connection.ExecuteAsync(sql, new { userid = user.UserID, password = user.PasswordHash });
            if (rows > 0)
            {
                return IdentityResult.Success;
            }
            return IdentityResult.Failed(new IdentityError { Description = $"Could not update user {user.UserID}." });
        }

        public async Task<SysUser> FindByIdAsync(string userId)
        {
            string sql = "select * from sys_user where userid = :userid";

            return await _connection.QuerySingleOrDefaultAsync<SysUser>(sql, new
            {
                userid = userId
            });
        }
        public async Task<SysUser> FindByNameAsync(string userName)
        {
            string sql = "select * from sys_user where username = :username";

            return await _connection.QuerySingleOrDefaultAsync<SysUser>(sql, new
            {
                username = userName
            });
        }
    }

    4、创建SysuserRole

    public class SysUserRole
    {
        public string UserID { get; set; } 
        public string UserName { get; set; } 
        public string Password { get; set; }
    }

    5、创建CustomUserStore

   public class CustomUserStore : IUserStore<SysUser>, IUserPasswordStore<SysUser>
    {
        private readonly SysUserDal _usersTable;

        public CustomUserStore(SysUserDal usersTable)
        {
            _usersTable = usersTable;
        }

        #region createuser
        public async Task<IdentityResult> CreateAsync(SysUser user,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (user == null) throw new ArgumentNullException(nameof(user));

            return await _usersTable.CreateAsync(user);
        }
        #endregion

        public async Task<IdentityResult> DeleteAsync(SysUser user,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (user == null) throw new ArgumentNullException(nameof(user));

            return await _usersTable.DeleteAsync(user);

        }

        public void Dispose()
        {
        }

        public async Task<SysUser> FindByIdAsync(string userId,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (userId == null) throw new ArgumentNullException(nameof(userId));
            return await _usersTable.FindByIdAsync(userId);

        }

        public async Task<SysUser> FindByNameAsync(string userName,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (userName == null) throw new ArgumentNullException(nameof(userName));

            return await _usersTable.FindByNameAsync(userName);
        }

        public Task<string> GetNormalizedUserNameAsync(SysUser user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<string> GetPasswordHashAsync(SysUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (user == null) throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.Password);
        }

        public Task<string> GetUserIdAsync(SysUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (user == null) throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.UserID);
        }
        public Task<bool> CheckPasswordAsync(SysUser user, string pwd,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            cancellationToken.ThrowIfCancellationRequested();
            return Task.FromResult(user.Password == pwd);
        }
        public Task<bool> IsEmailConfirmedAsync(SysUser user,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            cancellationToken.ThrowIfCancellationRequested();
            return Task.FromResult(true);
        }

        public Task<string> GetUserNameAsync(SysUser user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (user == null) throw new ArgumentNullException(nameof(user));

            return Task.FromResult(user.UserID);
        }

        public Task<bool> HasPasswordAsync(SysUser user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task SetNormalizedUserNameAsync(SysUser user, string normalizedName, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (user == null) throw new ArgumentNullException(nameof(user));
            if (normalizedName == null) throw new ArgumentNullException(nameof(normalizedName));

            user.NormalizedUserName = normalizedName;
            return Task.FromResult<object>(null);
        }

        public Task SetPasswordHashAsync(SysUser user, string passwordHash, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (user == null) throw new ArgumentNullException(nameof(user));
            if (passwordHash == null) throw new ArgumentNullException(nameof(passwordHash));

            user.PasswordHash = passwordHash;
            return Task.FromResult<object>(null);

        }

        public Task SetUserNameAsync(SysUser user, string userName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<IdentityResult> UpdateAsync(SysUser user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }
    }

    6、创建CustomRoleStore

    public class CustomRoleStore : IRoleStore<SysUserRole>
    {
        public Task<IdentityResult> CreateAsync(SysUserRole role, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<IdentityResult> DeleteAsync(SysUserRole role, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public void Dispose()
        {
        }

        public Task<SysUserRole> FindByIdAsync(string roleId, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<SysUserRole> FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<string> GetNormalizedRoleNameAsync(SysUserRole role, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<string> GetRoleIdAsync(SysUserRole role, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<string> GetRoleNameAsync(SysUserRole role, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task SetNormalizedRoleNameAsync(SysUserRole role, string normalizedName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task SetRoleNameAsync(SysUserRole role, string roleName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<IdentityResult> UpdateAsync(SysUserRole role, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }
    }

    7、StartUp注入:ConfigureServices方法中添加

   string connectionString = Configuration["OracleConStr"];
   services.AddTransient(e => new OracleConnection(connectionString));
   services.AddTransient<SysUserDal>();
   services.AddIdentity<SysUser, SysUserRole>().AddDefaultTokenProviders();
   services.AddTransient<IUserStore<SysUser>, CustomUserStore>();
   services.AddTransient<IRoleStore<SysUserRole>, CustomRoleStore>();

    8、StartUp中Configure的UseMVC之前添加

 app.UseAuthentication();

    至此,基本的代码配置就完成了,下一步就是在controller中调用

三、controller+view使用Identity,因为是简单demo,所以代码比较简单,并不考虑实际使用场景

    1、创建AccountController

    public class AccountController : Controller
    {
        private readonly UserManager<SysUser> _userManager;
        private readonly SignInManager<SysUser> _signInManager;
        public AccountController(UserManager<SysUser> userManager,
            SignInManager<SysUser> signInManager)
        {
            _userManager = userManager;
            _signInManager = signInManager;
        }
        public IActionResult Index()
        {
            return View();
        }
        public async Task<IActionResult> Login()
        {
            string userid = "test";
            string pwd = "123ABCabc.";

            var result = await _signInManager.PasswordSignInAsync(userid, pwd, false, false);
            if (result.Succeeded)
            {
                //dosomething
            }
            else if (result.IsLockedOut)
            {
                //dosomething
            }
            else
            {
                ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            }
            return View();
        }
        public async Task<IActionResult> Register()
        {

            var user = new SysUser { UserID = "test", UserName = "测试" };
            var result = await _userManager.CreateAsync(user, "123ABCabc.");
            if (result.Succeeded)
            {
                //await _signInManager.SignInAsync(user, isPersistent: false);
            }
            else
            {
                //dosomething
            }
            return View();
        }
        public async Task<IActionResult> GetUser()
        {
            var res = await _userManager.GetUserAsync(HttpContext.User);
            return View();
        }
        public async Task<IActionResult> Logout()
        {
            await _signInManager.SignOutAsync();
            return View();
        }
    }

    2、创建每个Action的cshtml

    3、debug模式运行程序,在Account中的所有Action打断点,在SysUserDal所有方法打断点,使用浏览器地址栏调用Action,观察Identity工作流程,此处使用Register进行测试,不细看Debug过程,只看结果。

https://localhost:XXXXXX/Account/Register

观察数据库数据,发现数据已经被添加:

 

至此,可以证明Identity的ORM基础替换已经成功,此时的项目结构:

其他安全和权限类配置可根据使用场景阅读Microsoft Docs自定义开发

 

<?xml version="1.0" encoding="utf-8"?> <svg xmlns="http://www.w3.org/2000/svg" width="2200" height="1900" viewBox="0 0 2200 1900"> <!-- 定义箭头标记 --> <defs> <marker id="arrow" markerWidth="10" markerHeight="10" refX="10" refY="5" orient="auto" markerUnits="strokeWidth"> <path d="M0,0 L10,5 L0,10 z" fill="#444"/> </marker> </defs> <!-- 标题和说明 --> <text x="40" y="60" fill="#111" font-size="34" font-family="Segoe UI, Arial, sans-serif" font-weight="700">.NET 低代码 BI/报表平台(帆软风格)参考架构</text> <text x="40" y="95" fill="#555" font-size="18" font-family="Segoe UI, Arial, sans-serif">设计(Design-time)与运行(Run-time)双通道,支持插件生态、键打包部署与企业级治理能力</text> <text x="1400" y="95" fill="#333" font-size="16" font-family="Segoe UI, Arial, sans-serif">关键技术栈:.NET 8/9, ASP.NET Core, EF Core, Redis, Quartz, OTel, RabbitMQ/Kafka, Elasticsearch, MinIO/S3, Keycloak/OIDC</text> <!-- 架构级容器 - 增加了级间距,避免重叠 --> <rect x="20" y="130" width="2160" height="250" rx="20" ry="20" fill="#ffffff" stroke="#e0e6ef" stroke-width="2" stroke-dasharray="6 6"/> <rect x="20" y="410" width="2160" height="260" rx="20" ry="20" fill="#ffffff" stroke="#e0e6ef" stroke-width="2" stroke-dasharray="6 6"/> <rect x="20" y="700" width="2160" height="300" rx="20" ry="20" fill="#ffffff" stroke="#e0e6ef" stroke-width="2" stroke-dasharray="6 6"/> <rect x="20" y="1030" width="2160" height="310" rx="20" ry="20" fill="#ffffff" stroke="#e0e6ef" stroke-width="2" stroke-dasharray="6 6"/> <rect x="20" y="1370" width="2160" height="280" rx="20" ry="20" fill="#ffffff" stroke="#e0e6ef" stroke-width="2" stroke-dasharray="6 6"/> <!-- 级标题 --> <text x="32" y="158" fill="#6b7280" font-size="20" font-family="Segoe UI, Arial, sans-serif" font-weight="600">客户端 / 设计中心(前端 UI + CLI)</text> <text x="32" y="438" fill="#6b7280" font-size="20" font-family="Segoe UI, Arial, sans-serif" font-weight="600">低代码引擎(DSL/模型/编排/代码生成)</text> <text x="32" y="728" fill="#6b7280" font-size="20" font-family="Segoe UI, Arial, sans-serif" font-weight="600">应用服务(业务模块 / 微内核插件)</text> <text x="32" y="1058" fill="#6b7280" font-size="20" font-family="Segoe UI, Arial, sans-serif" font-weight="600">平台核心(网关/配置/缓存/事件/安全/可观测性/多租户)</text> <text x="32" y="1398" fill="#6b7280" font-size="20" font-family="Segoe UI, Arial, sans-serif" font-weight="600">基础设施(数据库/存储/消息/身份/CI/CD/容器/外部系统)</text> <!-- 第:客户端 / 设计中心 --> <g> <rect x="40" y="180" width="560" height="200" rx="12" ry="12" fill="#f9fbfd" stroke="#3a6ea5" stroke-width="2"/> <rect x="40" y="180" width="560" height="48" rx="12" ry="12" fill="#3a6ea5" stroke="#3a6ea5" stroke-width="2"/> <text x="56" y="212" fill="#ffffff" font-size="24" font-family="Segoe UI, Arial, sans-serif" font-weight="700">Web 可视化设计器</text> <text x="58" y="258" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 页面/报表/仪表盘拖拽</text> <text x="58" y="286" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 数据绑定与查询向导</text> <text x="58" y="314" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 组件库(表格/图表/表单/布局/地图)</text> <text x="58" y="342" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 即预览/调试(Mock 数据)</text> </g> <g> <rect x="620" y="180" width="480" height="200" rx="12" ry="12" fill="#f9fbfd" stroke="#3a6ea5" stroke-width="2"/> <rect x="620" y="180" width="480" height="48" rx="12" ry="12" fill="#3a6ea5" stroke="#3a6ea5" stroke-width="2"/> <text x="636" y="212" fill="#ffffff" font-size="24" font-family="Segoe UI, Arial, sans-serif" font-weight="700">CLI 工具</text> <text x="638" y="258" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 项目初始化(dotnet new)</text> <text x="638" y="286" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 键打包发布(EXE/Docker/NuGet)</text> <text x="638" y="314" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 脚手架 & 模板管理</text> <text x="638" y="342" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 密钥/证书/环境管理</text> </g> <g> <rect x="1120" y="180" width="520" height="200" rx="12" ry="12" fill="#f9fbfd" stroke="#3a6ea5" stroke-width="2"/> <rect x="1120" y="180" width="520" height="48" rx="12" ry="12" fill="#3a6ea5" stroke="#3a6ea5" stroke-width="2"/> <text x="1136" y="212" fill="#ffffff" font-size="24" font-family="Segoe UI, Arial, sans-serif" font-weight="700">运行态预览与权限控制</text> <text x="1138" y="258" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 多租户/多环境预览</text> <text x="1138" y="286" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• RBAC 权限模拟</text> <text x="1138" y="314" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 版本/变更对比</text> <text x="1138" y="342" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 审计日志入口</text> </g> <!-- 第二:低代码引擎 --> <g> <rect x="40" y="440" width="520" height="220" rx="12" ry="12" fill="#f9fbfd" stroke="#3a6ea5" stroke-width="2"/> <rect x="40" y="440" width="520" height="48" rx="12" ry="12" fill="#3a6ea5" stroke="#3a6ea5" stroke-width="2"/> <text x="56" y="472" fill="#ffffff" font-size="24" font-family="Segoe UI, Arial, sans-serif" font-weight="700">DSL / 模型</text> <text x="58" y="518" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• YAML/JSON/Schema</text> <text x="58" y="546" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 元数据:数据集/参数/模板/流程</text> <text x="58" y="574" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 版本与依赖管理(语义化)</text> <text x="58" y="602" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 校验器与约束(SchemaValidation)</text> </g> <g> <rect x="580" y="440" width="520" height="220" rx="12" ry="12" fill="#f9fbfd" stroke="#3a6ea5" stroke-width="2"/> <rect x="580" y="440" width="520" height="48" rx="12" ry="12" fill="#3a6ea5" stroke="#3a6ea5" stroke-width="2"/> <text x="596" y="472" fill="#ffffff" font-size="24" font-family="Segoe UI, Arial, sans-serif" font-weight="700">代码生成与模板</text> <text x="598" y="518" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• C# partials/Source Generators</text> <text x="598" y="546" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• T4/Handlebars 模板</text> <text x="598" y="574" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 最小 API / 控制台项目生成</text> <text x="598" y="602" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 插件包装与签名</text> </g> <g> <rect x="1120" y="440" width="520" height="220" rx="12" ry="12" fill="#f9fbfd" stroke="#3a6ea5" stroke-width="2"/> <rect x="1120" y="440" width="520" height="48" rx="12" ry="12" fill="#3a6ea5" stroke="#3a6ea5" stroke-width="2"/> <text x="1136" y="472" fill="#ffffff" font-size="24" font-family="Segoe UI, Arial, sans-serif" font-weight="700">流程/规则/表达式引擎</text> <text x="1138" y="518" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 状态机/BPMN-风格编排</text> <text x="1138" y="546" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 规则引擎(Expressions/JS/CSX)</text> <text x="1138" y="574" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 任务编排(依赖/重试/补偿)</text> <text x="1138" y="602" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 模板引擎(报表/打印)</text> </g> <!-- 第三:应用服务 --> <g> <rect x="40" y="730" width="520" height="260" rx="12" ry="12" fill="#f9fbfd" stroke="#3a6ea5" stroke-width="2"/> <rect x="40" y="730" width="520" height="48" rx="12" ry="12" fill="#3a6ea5" stroke="#3a6ea5" stroke-width="2"/> <text x="56" y="762" fill="#ffffff" font-size="24" font-family="Segoe UI, Arial, sans-serif" font-weight="700">报表/仪表盘服务</text> <text x="58" y="808" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 分页/填报/联动/钻取</text> <text x="58" y="836" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 导出:Excel/PDF/CSV/图片</text> <text x="58" y="864" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 模板与主题管理</text> <text x="58" y="892" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 权限与水印/脱敏</text> </g> <g> <rect x="580" y="730" width="520" height="260" rx="12" ry="12" fill="#f9fbfd" stroke="#3a6ea5" stroke-width="2"/> <rect x="580" y="730" width="520" height="48" rx="12" ry="12" fill="#3a6ea5" stroke="#3a6ea5" stroke-width="2"/> <text x="596" y="762" fill="#ffffff" font-size="24" font-family="Segoe UI, Arial, sans-serif" font-weight="700">数据源 & 查询引擎</text> <text x="598" y="808" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 适配器:SQLServer/MySQL/PostgreSQL/Oracle/SQLite</text> <text x="598" y="836" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• API/CSV/Excel/Parquet</text> <text x="598" y="864" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• SQL Builder/聚合/联接/缓存</text> <text x="598" y="892" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 连接池与熔断/重试/限流</text> </g> <g> <rect x="1120" y="730" width="520" height="260" rx="12" ry="12" fill="#f9fbfd" stroke="#3a6ea5" stroke-width="2"/> <rect x="1120" y="730" width="520" height="48" rx="12" ry="12" fill="#3a6ea5" stroke="#3a6ea5" stroke-width="2"/> <text x="1136" y="762" fill="#ffffff" font-size="24" font-family="Segoe UI, Arial, sans-serif" font-weight="700">用户/组织/权限(RBAC/ABAC)</text> <text x="1138" y="808" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• SSO(OAuth2/OIDC/SAML)</text> <text x="1138" y="836" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 租户/组织/岗位/角色</text> <text x="1138" y="864" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 细粒度数据权限(Row/Column)</text> <text x="1138" y="892" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 审计与合规(留痕/审批)</text> </g> <!-- 第四:平台核心 --> <g> <rect x="40" y="1060" width="520" height="280" rx="12" ry="12" fill="#f9fbfd" stroke="#3a6ea5" stroke-width="2"/> <rect x="40" y="1060" width="520" height="48" rx="12" ry="12" fill="#3a6ea5" stroke="#3a6ea5" stroke-width="2"/> <text x="56" y="1092" fill="#ffffff" font-size="24" font-family="Segoe UI, Arial, sans-serif" font-weight="700">API 网关 / 反向代理</text> <text x="58" y="1138" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 路由/聚合/协议转换</text> <text x="58" y="1166" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 认证鉴权(JWT/Keycloak)</text> <text x="58" y="1194" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 限流/熔断/灰度发布</text> <text x="58" y="1222" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 全链路跟踪注入</text> </g> <g> <rect x="580" y="1060" width="520" height="280" rx="12" ry="12" fill="#f9fbfd" stroke="#3a6ea5" stroke-width="2"/> <rect x="580" y="1060" width="520" height="48" rx="12" ry="12" fill="#3a6ea5" stroke="#3a6ea5" stroke-width="2"/> <text x="596" y="1092" fill="#ffffff" font-size="24" font-family="Segoe UI, Arial, sans-serif" font-weight="700">核心框架</text> <text x="598" y="1138" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• .NET 8/9 + ASP.NET Core</text> <text x="598" y="1166" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• DI/Options/配置中心</text> <text x="598" y="1194" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• EF Core/Dapper 数据访问</text> <text x="598" y="1222" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• Redis 缓存/分布式锁</text> <text x="598" y="1250" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• Quartz 调度中心</text> <text x="598" y="1278" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 事件总线(RabbitMQ/Kafka)</text> </g> <g> <rect x="1120" y="1060" width="520" height="280" rx="12" ry="12" fill="#f9fbfd" stroke="#3a6ea5" stroke-width="2"/> <rect x="1120" y="1060" width="520" height="48" rx="12" ry="12" fill="#3a6ea5" stroke="#3a6ea5" stroke-width="2"/> <text x="1136" y="1092" fill="#ffffff" font-size="24" font-family="Segoe UI, Arial, sans-serif" font-weight="700">治理与可观测性</text> <text x="1138" y="1138" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 日志/指标/追踪(OpenTelemetry)</text> <text x="1138" y="1166" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 多租户隔离(库/架构/字段)</text> <text x="1138" y="1194" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 灰度/金丝雀发布</text> <text x="1138" y="1222" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 国际化/本地化(i18n/L10n)</text> <text x="1138" y="1250" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• License 管控/插件签名验证</text> </g> <!-- 第五:基础设施 --> <g> <rect x="40" y="1400" width="520" height="240" rx="12" ry="12" fill="#f9fbfd" stroke="#3a6ea5" stroke-width="2"/> <rect x="40" y="1400" width="520" height="48" rx="12" ry="12" fill="#3a6ea5" stroke="#3a6ea5" stroke-width="2"/> <text x="56" y="1432" fill="#ffffff" font-size="24" font-family="Segoe UI, Arial, sans-serif" font-weight="700">数据与缓存</text> <text x="58" y="1478" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 数据库集群:SQL Server/MySQL/PostgreSQL/Oracle</text> <text x="58" y="1506" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 缓存:Redis Cluster</text> <text x="58" y="1534" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 搜索:Elasticsearch/OpenSearch</text> <text x="58" y="1562" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 对象存储:MinIO/S3/OSS</text> </g> <g> <rect x="580" y="1400" width="520" height="240" rx="12" ry="12" fill="#f9fbfd" stroke="#3a6ea5" stroke-width="2"/> <rect x="580" y="1400" width="520" height="48" rx="12" ry="12" fill="#3a6ea5" stroke="#3a6ea5" stroke-width="2"/> <text x="596" y="1432" fill="#ffffff" font-size="24" font-family="Segoe UI, Arial, sans-serif" font-weight="700">消息与集成</text> <text x="598" y="1478" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• RabbitMQ/Kafka</text> <text x="598" y="1506" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 定任务队列/重试/死信</text> <text x="598" y="1534" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 外部系统:SAP/ERP/CRM/表单系统</text> <text x="598" y="1562" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• Webhooks/GraphQL Federation</text> </g> <g> <rect x="1120" y="1400" width="520" height="240" rx="12" ry="12" fill="#f9fbfd" stroke="#3a6ea5" stroke-width="2"/> <rect x="1120" y="1400" width="520" height="48" rx="12" ry="12" fill="#3a6ea5" stroke="#3a6ea5" stroke-width="2"/> <text x="1136" y="1432" fill="#ffffff" font-size="24" font-family="Segoe UI, Arial, sans-serif" font-weight="700">运维与部署</text> <text x="1138" y="1478" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• Kubernetes/Docker/Helm</text> <text x="1138" y="1506" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• CI/CD(GitHub Actions/Jenkins/Azure Pipelines)</text> <text x="1138" y="1534" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• 制品库(NuGet/Nexus/ACR/ECR)</text> <text x="1138" y="1562" fill="#1b1f23" font-size="18" font-family="Segoe UI, Arial, sans-serif">• CDN/WAF/反向代理(Nginx/Traefik)</text> </g> <!-- 连接线条与说明 - 调整了线条路径,避免交叉重叠 --> <line x1="320" y1="380" x2="300" y2="440" stroke="#444" stroke-width="2.5" marker-end="url(#arrow)"/> <text x="310" y="403" fill="#444" font-size="16" font-family="Segoe UI, Arial, sans-serif">模型/DSL</text> <line x1="860" y1="380" x2="840" y2="440" stroke="#444" stroke-width="2.5" marker-end="url(#arrow)"/> <text x="850" y="403" fill="#444" font-size="16" font-family="Segoe UI, Arial, sans-serif">模板生成/构建</text> <line x1="1380" y1="380" x2="1380" y2="440" stroke="#444" stroke-width="2.5" marker-end="url(#arrow)"/> <text x="1390" y="403" fill="#444" font-size="16" font-family="Segoe UI, Arial, sans-serif">流程/规则</text> <line x1="300" y1="660" x2="300" y2="730" stroke="#444" stroke-width="2.5" marker-end="url(#arrow)"/> <text x="310" y="685" fill="#444" font-size="16" font-family="Segoe UI, Arial, sans-serif">生成报表/页面</text> <line x1="840" y1="660" x2="840" y2="730" stroke="#444" stroke-width="2.5" marker-end="url(#arrow)"/> <text x="850" y="685" fill="#444" font-size="16" font-family="Segoe UI, Arial, sans-serif">生成 API/任务</text> <line x1="1380" y1="660" x2="1380" y2="730" stroke="#444" stroke-width="2.5" marker-end="url(#arrow)"/> <text x="1390" y="685" fill="#444" font-size="16" font-family="Segoe UI, Arial, sans-serif">注册流程/权限</text> <line x1="320" y1="180" x2="300" y2="1060" stroke="#444" stroke-width="2.5" marker-end="url(#arrow)"/> <text x="220" y="620" fill="#444" font-size="16" font-family="Segoe UI, Arial, sans-serif">HTTP/gRPC</text> <line x1="860" y1="180" x2="840" y2="1060" stroke="#444" stroke-width="2.5" marker-end="url(#arrow)"/> <text x="850" y="620" fill="#444" font-size="16" font-family="Segoe UI, Arial, sans-serif">发布/回滚</text> <line x1="1380" y1="180" x2="1380" y2="1060" stroke="#444" stroke-width="2.5" marker-end="url(#arrow)"/> <text x="1390" y="620" fill="#444" font-size="16" font-family="Segoe UI, Arial, sans-serif">预览/审计</text> <line x1="300" y1="1370" x2="300" y2="1340" stroke="#444" stroke-width="2.5" marker-end="url(#arrow)"/> <text x="310" y="1350" fill="#444" font-size="16" font-family="Segoe UI, Arial, sans-serif">SQL/缓存</text> <line x1="840" y1="1370" x2="840" y2="1340" stroke="#444" stroke-width="2.5" marker-end="url(#arrow)"/> <text x="850" y="1350" fill="#444" font-size="16" font-family="Segoe UI, Arial, sans-serif">事件/队列</text> <line x1="1380" y1="1370" x2="1380" y2="1340" stroke="#444" stroke-width="2.5" marker-end="url(#arrow)"/> <text x="1390" y="1350" fill="#444" font-size="16" font-family="Segoe UI, Arial, sans-serif">日志/镜像</text> <line x1="300" y1="1340" x2="840" y2="1060" stroke="#444" stroke-width="2.5" marker-end="url(#arrow)"/> <text x="570" y="1190" fill="#444" font-size="16" font-family="Segoe UI, Arial, sans-serif">数据 API</text> <line x1="840" y1="1340" x2="300" y2="1060" stroke="#444" stroke-width="2.5" marker-end="url(#arrow)"/> <text x="570" y="1190" fill="#444" font-size="16" font-family="Segoe UI, Arial, sans-serif">API 路由</text> <line x1="1380" y1="1340" x2="840" y2="1060" stroke="#444" stroke-width="2.5" marker-end="url(#arrow)"/> <text x="1110" y="1190" fill="#444" font-size="16" font-family="Segoe UI, Arial, sans-serif">鉴权校验</text> </svg> 这个svg描述的架构怎么样
最新发布
08-31
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值