[NewLife.XCode]角色权限

2019-07-02 17:30:28 浏览数 (1)

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

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

开源地址:https://github.com/NewLifeX/X

前面讲解了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

用户数据模型:

代码语言:javascript复制
  <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

角色数据模型:

代码语言:javascript复制
  <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

角色支持的操作权限:

代码语言:javascript复制
    /// <summary>操作权限</summary>
    [Flags]
    [Description("操作权限")]
    public enum PermissionFlags
    {
        /// <summary>无权限</summary>
        [Description("无权限")]
        None = 0,

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

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

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

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

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

主要功能点:

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

菜单 Menu

菜单数据模型:

代码语言:javascript复制
  <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

数据模型:

代码语言:javascript复制
  <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<LogProvider>()
  2. 从IManageProvider接口获取当前登录用户以及远程访问IP写入日志相应字段

在线 UserOnline

数据模型:

代码语言:javascript复制
  <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

代码语言:javascript复制
  <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

数据模型:

代码语言:javascript复制
  <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

数据模型:

代码语言:javascript复制
  <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>

0 人点赞