[NewLife.XCode]角色权限


NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netcore,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode。

整个系列教程会大量结合示例代码和运行日志来进行深入分析,蕴含多年开发经验于其中,代表作有百亿级大数据实时计算项目。

开源地址:https://github.com/NewLifeX/X (求star, 864+)

前面讲解了XCode的各种用法,这一章我们来讲讲内置的Membership,同时也是XCode的第一标准示例!

设计背景

现代管理信息系统绝大部分采用BS架构,无一例外需要用户角色权限的支持!

结合团队诸多兄弟姐妹的经验,设计了一个大小适中的用户权限系统Membership,目标是满足80%的使用场景,并具备一定的扩展性。

Membership刚开始就采用了角色授权体系,每个用户只有一种角色,角色拥有菜单资源权限集。

随着Membership实用性日益增加,2015年初正式合并进入XCode,作为一个模块存在。

2016年第二代魔方NewLife.Cube采用ASP.Net MVC5重构,让Membership的荣誉达到了鼎峰!

在MVC中,每个Controller就是一个菜单资源,其下的Search/Detail/Insert/Update/Delete等Action作为角色在该菜单资源下的权限子项,保存在角色属性数据中。

2018年为了增强魔方功能,在某些场景下支持单用户多角色,且兼容已有系统,用户表增加RoleIDs字段,保存扩展角色,原来的RoleID作为主角色。

管理提供者

管理提供者接口 IManageProvider ,提供了Membership基本操作实现。

  1. 当前登录用户 GetCurrent、SetCurrent,静态访问 ManageProvider.User
  2. 查找用户 FindByID、FindByName
  3. 注册登录注销 Register、Login、Logout
  4. 当前用户主机(访问者IP)ManageProvider.UserHost
  5. IManageProvider 默认由XCode.Membership中的UserX/Role/Menu支持,如若用户使用自己的用户权限表,可重新实现该接口

用户权限

用户 UserX

用户数据模型:

  <Table Name="User" Description="用户" RenderGenEntity="true">
    <Columns>
      <Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
      <Column Name="Name" DataType="String" Master="True" Nullable="False" Description="名称。登录用户名" />
      <Column Name="Password" DataType="String" Description="密码" />
      <Column Name="DisplayName" DataType="String" Description="昵称" />
      <Column Name="Sex" DataType="Int32" Description="性别。未知、男、女" Type="SexKinds" />
      <Column Name="Mail" DataType="String" Description="邮件" />
      <Column Name="Mobile" DataType="String" Description="手机" />
      <Column Name="Code" DataType="String" Description="代码。身份证、员工编号等" />
      <Column Name="Avatar" DataType="String" Length="200" Description="头像" />
      <Column Name="RoleID" DataType="Int32" Description="角色。主要角色" />
      <Column Name="RoleIDs" DataType="String" Length="200" Description="角色组。次要角色集合" />
      <Column Name="DepartmentID" DataType="Int32" Description="部门。组织机构" />
      <Column Name="Online" DataType="Boolean" Description="在线" />
      <Column Name="Enable" DataType="Boolean" Description="启用" />
      <Column Name="Logins" DataType="Int32" Description="登录次数" />
      <Column Name="LastLogin" DataType="DateTime" Description="最后登录" />
      <Column Name="LastLoginIP" DataType="String" Description="最后登录IP" />
      <Column Name="RegisterTime" DataType="DateTime" Description="注册时间" />
      <Column Name="RegisterIP" DataType="String" Description="注册IP" />
      <Column Name="Ex1" DataType="Int32" Description="扩展1" />
      <Column Name="Ex2" DataType="Int32" Description="扩展2" />
      <Column Name="Ex3" DataType="Double" Description="扩展3" />
      <Column Name="Ex4" DataType="String" Description="扩展4" />
      <Column Name="Ex5" DataType="String" Description="扩展5" />
      <Column Name="Ex6" DataType="String" Description="扩展6" />
      <Column Name="UpdateUser" DataType="String" Description="更新用户" />
      <Column Name="UpdateUserID" DataType="Int32" Description="更新用户" />
      <Column Name="UpdateIP" DataType="String" Description="更新地址" />
      <Column Name="UpdateTime" DataType="DateTime" Nullable="False" Description="更新时间" />
      <Column Name="Remark" DataType="String" Length="200" Description="备注" />
    Columns>
    <Indexes>
      <Index Columns="Name" Unique="True" />
      <Index Columns="RoleID" />
      <Index Columns="UpdateTime" />
    Indexes>
  Table>

常用字段有ID、用户名和密码,登录注册相关信息;

角色RoleID、RoleIDs用于实现权限集控制;

部分场景需要邮箱Mail、手机Mobile或者工号Code登录;

如果仍然不能满足要求,可以考虑使用Ex1~Ex6等扩展字段。

常用功能点:

  1. 初始化时,如果数据表为空,自动插入admin/admin用户账号,角色是“管理员”
  2. 支持注册登录,使用MD5保存密码
  3. 支持编号查询FindByID和名称查询FindByName,分别采用了对象缓存和对象从键,轻松实现百万级账号快速查询
  4. 支持IIdentity接口

角色 Role

角色数据模型:

  <Table Name="Role" Description="角色" RenderGenEntity="true">
    <Columns>
      <Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
      <Column Name="Name" DataType="String" Master="True" Nullable="False" Description="名称" />
      <Column Name="Enable" DataType="Boolean" Description="启用" />
      <Column Name="IsSystem" DataType="Boolean" Description="系统。用于业务系统开发使用,不受数据权限约束,禁止修改名称或删除" />
      <Column Name="Permission" DataType="String" Length="500" Description="权限。对不同资源的权限,逗号分隔,每个资源的权限子项竖线分隔" />
      <Column Name="Ex1" DataType="Int32" Description="扩展1" />
      <Column Name="Ex2" DataType="Int32" Description="扩展2" />
      <Column Name="Ex3" DataType="Double" Description="扩展3" />
      <Column Name="Ex4" DataType="String" Description="扩展4" />
      <Column Name="Ex5" DataType="String" Description="扩展5" />
      <Column Name="Ex6" DataType="String" Description="扩展6" />
      <Column Name="CreateUser" DataType="String" Description="创建用户" />
      <Column Name="CreateUserID" DataType="Int32" Description="创建用户" />
      <Column Name="CreateIP" DataType="String" Description="创建地址" />
      <Column Name="CreateTime" DataType="DateTime" Nullable="False" Description="创建时间" />
      <Column Name="UpdateUser" DataType="String" Description="更新用户" />
      <Column Name="UpdateUserID" DataType="Int32" Description="更新用户" />
      <Column Name="UpdateIP" DataType="String" Description="更新地址" />
      <Column Name="UpdateTime" DataType="DateTime" Nullable="False" Description="更新时间" />
      <Column Name="Remark" DataType="String" Length="200" Description="备注" />
    Columns>
    <Indexes>
      <Index Columns="Name" Unique="True" />
    Indexes>
  Table>

角色表比较简单主要是名称和启用,以及保存菜单权限数据的Permission

角色支持的操作权限:

    /// 操作权限
    [Flags]
    [Description("操作权限")]
    public enum PermissionFlags
    {
        /// 无权限
        [Description("无权限")]
        None = 0,

        /// 查看权限
        [Description("查看")]
        Detail = 1,

        /// 添加权限
        [Description("添加")]
        Insert = 2,

        /// 修改权限
        [Description("修改")]
        Update = 4,

        /// 删除权限
        [Description("删除")]
        Delete = 8,

        /// 所有权限
        [Description("所有")]
        All = 0xFF,
    }

主要功能点:

  1. 数据表为空时初始化4个基本角色:管理员、高级用户、普通用户、游客
  2. 启动时角色权限校验,清理角色中无效的权限项(可能菜单已删除),以及授权管理员访问所有角色都无权访问的新菜单
  3. 支持编号查询FindByID和名称查询FindByID,采用实体缓存,目标系统不会超过1000个角色
  4. 支持权限判断与设置 Has/Get/Set/Reset 等
  5. 重载实体类 Delete/Save/Update/OnLoad/OnPropertyChanged,加载实体对象时展开权限,保存时合并

菜单 Menu

菜单数据模型:

  <Table Name="Menu" Description="菜单" BaseType="EntityTree" RenderGenEntity="true">
    <Columns>
      <Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
      <Column Name="Name" DataType="String" Master="True" Nullable="False" Description="名称" />
      <Column Name="DisplayName" DataType="String" Description="显示名" />
      <Column Name="FullName" DataType="String" Length="200" Description="全名" />
      <Column Name="ParentID" DataType="Int32" Description="父编号" />
      <Column Name="Url" DataType="String" Length="200" Description="链接" />
      <Column Name="Sort" DataType="Int32" Description="排序" />
      <Column Name="Icon" DataType="String" Description="图标" />
      <Column Name="Visible" DataType="Boolean" Description="可见" />
      <Column Name="Necessary" DataType="Boolean" Description="必要。必要的菜单,必须至少有角色拥有这些权限,如果没有则自动授权给系统角色" />
      <Column Name="Permission" DataType="String" Length="200" Description="权限子项。逗号分隔,每个权限子项名值竖线分隔" />
      <Column Name="Ex1" DataType="Int32" Description="扩展1" />
      <Column Name="Ex2" DataType="Int32" Description="扩展2" />
      <Column Name="Ex3" DataType="Double" Description="扩展3" />
      <Column Name="Ex4" DataType="String" Description="扩展4" />
      <Column Name="Ex5" DataType="String" Description="扩展5" />
      <Column Name="Ex6" DataType="String" Description="扩展6" />
      <Column Name="CreateUser" DataType="String" Description="创建用户" />
      <Column Name="CreateUserID" DataType="Int32" Description="创建用户" />
      <Column Name="CreateIP" DataType="String" Description="创建地址" />
      <Column Name="CreateTime" DataType="DateTime" Nullable="False" Description="创建时间" />
      <Column Name="UpdateUser" DataType="String" Description="更新用户" />
      <Column Name="UpdateUserID" DataType="Int32" Description="更新用户" />
      <Column Name="UpdateIP" DataType="String" Description="更新地址" />
      <Column Name="UpdateTime" DataType="DateTime" Nullable="False" Description="更新时间" />
      <Column Name="Remark" DataType="String" Length="200" Description="备注" />
    Columns>
    <Indexes>
      <Index Columns="Name" />
      <Index Columns="ParentID,Name" Unique="True" />
    Indexes>
  Table>

菜单实体类采用树形实体基类 EntityTree ,通过 ParentID 实现上下级关联,同级 ParentID+Name 唯一

主要功能点:

  1. 支持自动扫描Controller作为菜单,因此魔方只需要增加Controller,即可在菜单表看到新页面
  2. 实体树适用于1000行以内树形数据表,一次性加载数据到内存,在内存中根据ParentID构造实体对象树,最常用树形是Parent/Childs

日志统计

日志 Log

数据模型:

  <Table Name="Log" Description="日志" ConnName="Log" RenderGenEntity="true">
    <Columns>
      <Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
      <Column Name="Category" DataType="String" Description="类别" />
      <Column Name="Action" DataType="String" Description="操作" />
      <Column Name="LinkID" DataType="Int32" Description="链接" />
      <Column Name="UserName" DataType="String" Description="用户名" />
      <Column Name="Ex1" DataType="Int32" Description="扩展1" />
      <Column Name="Ex2" DataType="Int32" Description="扩展2" />
      <Column Name="Ex3" DataType="Double" Description="扩展3" />
      <Column Name="Ex4" DataType="String" Description="扩展4" />
      <Column Name="Ex5" DataType="String" Description="扩展5" />
      <Column Name="Ex6" DataType="String" Description="扩展6" />
      <Column Name="CreateUser" DataType="String" Description="创建用户" />
      <Column Name="CreateUserID" DataType="Int32" Description="用户编号" />
      <Column Name="CreateIP" DataType="String" Description="IP地址" />
      <Column Name="CreateTime" DataType="DateTime" Nullable="False" Description="时间" />
      <Column Name="Remark" DataType="String" Length="500" Description="详细信息" />
    Columns>
    <Indexes>
      <Index Columns="Category" />
      <Index Columns="CreateUserID" />
      <Index Columns="CreateTime" />
    Indexes>
  Table>

日志表记录分类、操作和日志内容。

主要功能点:

  1. 日志提供者LogProvider,提供了唯一核心方法 WriteLog,默认实现就是写该日志表。可从对象容器取得日志提供者 ObjectContainer.Resolve()
  2. 从IManageProvider接口获取当前登录用户以及远程访问IP写入日志相应字段

在线 UserOnline

数据模型:

  <Table Name="UserOnline" Description="用户在线" ConnName="Log">
    <Columns>
      <Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
      <Column Name="UserID" DataType="Int32" Description="用户" />
      <Column Name="Name" DataType="String" Master="True" Description="名称" />
      <Column Name="SessionID" DataType="String" Description="会话。Web的SessionID或Server的会话编号" />
      <Column Name="Times" DataType="Int32" Description="次数" />
      <Column Name="Page" DataType="String" Description="页面" />
      <Column Name="Status" DataType="String" Length="200" Description="状态" />
      <Column Name="OnlineTime" DataType="Int32" Description="在线时间。本次在线总时间,秒" />
      <Column Name="CreateIP" DataType="String" Description="创建地址" />
      <Column Name="CreateTime" DataType="DateTime" Nullable="False" Description="创建时间" />
      <Column Name="UpdateTime" DataType="DateTime" Nullable="False" Description="修改时间" />
    Columns>
    <Indexes>
      <Index Columns="UserID" />
      <Index Columns="SessionID" />
      <Index Columns="CreateTime" />
    Indexes>
  Table>

借助用户行为模块 UserBehaviorModule , 维护用户在线记录,持久化在 UserOnline 表

访问统计 VisitStat

  <Table Name="VisitStat" Description="访问统计" ConnName="Log">
    <Columns>
      <Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
      <Column Name="Level" DataType="Int32" Description="层级" Type="XCode.Statistics.StatLevels" />
      <Column Name="Time" DataType="DateTime" Description="时间" />
      <Column Name="Page" DataType="String" Nullable="False" Description="页面" />
      <Column Name="Title" DataType="String" Master="True" Description="标题" />
      <Column Name="Times" DataType="Int32" Description="次数" />
      <Column Name="Users" DataType="Int32" Description="用户" />
      <Column Name="IPs" DataType="Int32" Description="IP" />
      <Column Name="Error" DataType="Int32" Description="错误" />
      <Column Name="Cost" DataType="Int32" Description="耗时。毫秒" />
      <Column Name="MaxCost" DataType="Int32" Description="最大耗时。毫秒" />
      <Column Name="CreateTime" DataType="DateTime" Nullable="False" Description="创建时间" />
      <Column Name="UpdateTime" DataType="DateTime" Nullable="False" Description="更新时间" />
      <Column Name="Remark" DataType="String" Length="5000" Description="详细信息" />
    Columns>
    <Indexes>
      <Index Columns="Page,Level,Time" Unique="True" />
      <Index Columns="Level,Time" />
    Indexes>
  Table>

借助用户行为模块 UserBehaviorModule , 维护用户访问记录,写入日志表,并写入访问统计表。

主要功能要点:

  1. 记录页面访问统计,简单支持IP数和用户数
  2. 支持年月日三级统计,作为XCode日期统计表的标准示例

其它

部门 Department

数据模型:

  <Table Name="Department" Description="部门。组织机构,多级树状结构" BaseType="EntityTree" RenderGenEntity="true">
    <Columns>
      <Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
      <Column Name="Code" DataType="String" Description="代码" />
      <Column Name="Name" DataType="String" Master="True" Nullable="False" Description="名称" />
      <Column Name="FullName" DataType="String" Length="200" Description="全名" />
      <Column Name="ParentID" DataType="Int32" Description="父级" />
      <Column Name="Level" DataType="Int32" Description="层级。树状结构的层级" />
      <Column Name="Sort" DataType="Int32" Description="排序。同级内排序" />
      <Column Name="Enable" DataType="Boolean" Description="启用" />
      <Column Name="Visible" DataType="Boolean" Description="可见" />
      <Column Name="Ex1" DataType="Int32" Description="扩展1" />
      <Column Name="Ex2" DataType="Int32" Description="扩展2" />
      <Column Name="Ex3" DataType="Double" Description="扩展3" />
      <Column Name="Ex4" DataType="String" Description="扩展4" />
      <Column Name="Ex5" DataType="String" Description="扩展5" />
      <Column Name="Ex6" DataType="String" Description="扩展6" />
      <Column Name="CreateUser" DataType="String" Description="创建用户" />
      <Column Name="CreateUserID" DataType="Int32" Description="创建用户" />
      <Column Name="CreateIP" DataType="String" Description="创建地址" />
      <Column Name="CreateTime" DataType="DateTime" Nullable="False" Description="创建时间" />
      <Column Name="UpdateUser" DataType="String" Description="更新用户" />
      <Column Name="UpdateUserID" DataType="Int32" Description="更新用户" />
      <Column Name="UpdateIP" DataType="String" Description="更新地址" />
      <Column Name="UpdateTime" DataType="DateTime" Nullable="False" Description="更新时间" />
      <Column Name="Remark" DataType="String" Length="200" Description="备注" />
    Columns>
    <Indexes>
      <Index Columns="Name" />
      <Index Columns="ParentID,Name" Unique="True" />
      <Index Columns="Code" />
      <Index Columns="UpdateTime" />
    Indexes>
  Table>

字典参数 Parameter

数据模型:

  <Table Name="Parameter" Description="字典参数">
    <Columns>
      <Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" />
      <Column Name="Category" DataType="String" Description="类别" />
      <Column Name="Name" DataType="String" Master="True" Description="名称" />
      <Column Name="Value" DataType="String" Length="200" Description="数值" />
      <Column Name="LongValue" DataType="String" Length="2000" Description="长数值" />
      <Column Name="Kind" DataType="Int32" Description="种类。0普通,21列表,22名值" Type="XCode.Membership.ParameterKinds" />
      <Column Name="Enable" DataType="Boolean" Description="启用" />
      <Column Name="Ex1" DataType="Int32" Description="扩展1" />
      <Column Name="Ex2" DataType="Int32" Description="扩展2" />
      <Column Name="Ex3" DataType="Double" Description="扩展3" />
      <Column Name="Ex4" DataType="String" Description="扩展4" />
      <Column Name="Ex5" DataType="String" Description="扩展5" />
      <Column Name="Ex6" DataType="String" Description="扩展6" />
      <Column Name="CreateUser" DataType="String" Description="创建用户" />
      <Column Name="CreateUserID" DataType="Int32" Description="创建用户" />
      <Column Name="CreateIP" DataType="String" Description="创建地址" />
      <Column Name="CreateTime" DataType="DateTime" Nullable="False" Description="创建时间" />
      <Column Name="UpdateUser" DataType="String" Description="更新用户" />
      <Column Name="UpdateUserID" DataType="Int32" Description="更新用户" />
      <Column Name="UpdateIP" DataType="String" Description="更新地址" />
      <Column Name="UpdateTime" DataType="DateTime" Nullable="False" Description="更新时间" />
      <Column Name="Remark" DataType="String" Length="200" Description="备注" />
    Columns>
    <Indexes>
      <Index Columns="Category,Name" Unique="True" />
      <Index Columns="Name" />
      <Index Columns="UpdateTime" />
    Indexes>
  Table>

系列教程

NewLife.XCode教程系列[2019版]

  1. 。快速展现用法,代码配置连接字符串
  2. 。建立表格字段和索引,名字以及数据类型规范,推荐字段(时间,用户,IP)
  3. 。数据类业务类,泛型基类,接口
  4. 。连接字符串,调试开关,SQL日志,慢日志,参数化,执行超时。代码与配置文件设置,连接字符串局部设置
  5. 。自动建立数据库数据表
  6. 。InitData写入初始化数据
  7. 。重载拦截,自增字段,Valid验证,实体模型(时间,用户,IP)
  8. 。如何产生,怎么利用
  9. 。高并发统计
  10. 。单表和多表,不同连接,多种写法
  11. 。多表关联,Map映射
  12. 。复杂条件,分页,自定义扩展FieldItem,查总记录数,查汇总统计
  13. 。Sql缓存,更新机制
  14. 。全表整理缓存,更新机制
  15. 。字典缓存,适用用户等数据较多场景。
  16. 。字段精炼,索引完备,合理查询,充分利用缓存
  17. 。元数据,通用处理程序
  18. 。Membership
  19. 。Xml,Json,二进制,网络或文件
  20. 。常见拆分逻辑
  21. 。聚合统计,分组统计
  22. 。批量插入,批量Upsert,异步保存
  23. 。写入级缓存,提升性能。
  24. 。备份数据,恢复数据,同步数据
  25. 。提供RPC接口服务,远程执行查询,例如SQLite网络版
  26. 。ETL抽取,调度计算处理,结果持久化