一步一步搭建,功能最全的权限管理系统(一)

这篇具有很好参考价值的文章主要介绍了一步一步搭建,功能最全的权限管理系统(一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  一、前言

  这是一篇搭建权限管理系统的系列文章。

  随着网络的发展,信息安全对应任何企业来说都越发的重要,而本系列文章将和大家一起一步一步搭建一个全新的权限管理系统。

  说明:由于搭建一个全新的项目过于繁琐,所有作者将挑选核心代码和核心思路进行分享。

  二、技术选择

一步一步搭建,功能最全的权限管理系统(一)

 文章来源地址https://www.toymoban.com/news/detail-843810.html

  三、开始设计

  1、自主搭建vue前端和.net core webapi后端,网上有很多搭建教程。

  这是我搭建的

 后端: 一步一步搭建,功能最全的权限管理系统(一)  前端:一步一步搭建,功能最全的权限管理系统(一)

 

  搭建好之后,vue需要把基础配置做好,比如路由、响应请求等,网上都有教程。

   vue配置较为简单,webapi的框架我使用DDD领域启动设计方式,各个层的介绍如下下。

  • ProjectManageWebApi webapi接口层,属于启动项
  • Model 业务模型,代表着系统中的具体业务对象。
  • Infrastructure 仓储层,是数据存储层,提供持久化对象的方法。
  • Domain  领域层,是整个系统运行时核心业务对象的载体,是业务逻辑处理的领域。
  • Subdomain 子域,子域是领域层更加细微的划分,处理整个系统最核心业务逻辑。
  • Utility  工具层,存放系统的辅助工具类。

  2、搭建数据库

  菜单对于一个系统来说,是必不可少的,我们搭建权限管理系统就从这里开始

  任务:建立菜单表,并通过程序把菜单动态加载到页面,实现树形菜单。

  这是我的菜单表结构

  一步一步搭建,功能最全的权限管理系统(一)

 

  我采用的是一张表存储系统菜单,用id和pid存储上下级关系。当然这不是唯一的,根据情况可以把它拆分层多张表。

  3、创建基础仓储和菜单仓储

  在webapi中Infrastructure 仓储层创建基础仓储,以便提供持久化支持。

  我orm框架使用的是dapper来提共数据库和编程语言间的映射关系。

  首先需要建立一个增删改查的仓储接口,大致如下:

一步一步搭建,功能最全的权限管理系统(一)一步一步搭建,功能最全的权限管理系统(一)
/// <summary>
/// 仓储接口定义
/// </summary>
public interface IRepository
{

}

/// <summary>
/// 定义泛型仓储接口
/// </summary>
/// <typeparam name="T">实体类型</typeparam>
/// <typeparam name="object">主键类型</typeparam>
public interface IRepository<T> : IRepository where T : class, new()
{
    /// <summary>
    /// 新增
    /// </summary>
    /// <param name="entity">实体</param>
    /// <param name="innserSql">新增sql</param>
    /// <returns></returns>
    int Insert(T entity, string innserSql);

    /// <summary>
    /// 修改
    /// </summary>
    /// <param name="entity">实体</param>
    /// <param name="updateSql">更新sql</param>
    /// <returns></returns>
    int Update(T entity, string updateSql);

    /// <summary>
    /// 删除
    /// </summary>
    /// <param name="deleteSql">删除sql</param>
    /// <returns></returns>
    int Delete(string key,string deleteSql);

    /// <summary>
    /// 根据主键获取模型
    /// </summary>
    /// <param name="key">主键</param>
    /// <param name="selectSql">查询sql</param>
    /// <returns></returns>
    T GetByKey(string key, string selectSql);

    /// <summary>
    /// 获取所有数据
    /// </summary>
    /// <param name="selectAllSql">查询sql</param>
    /// <returns></returns>
    List<T> GetAll(string selectAllSql);

    /// <summary>
    /// 根据唯一主键验证数据是否存在
    /// </summary>
    /// <param name="id">主键</param>
    /// <param name="selectSql">查询sql</param>
    /// <returns>返回true存在,false不存在</returns>
    bool IsExist(string id, string selectSql);

    /// <summary>
    /// dapper通用分页方法
    /// </summary>
    /// <typeparam name="T">泛型集合实体类</typeparam>
    /// <param name="pageResultModel">分页模型</param>
    /// <returns></returns>
    PageResultModel<T> GetPageList<T>(PageResultModel pageResultModel);

}
View Code

  然后实现这个仓储接口

一步一步搭建,功能最全的权限管理系统(一)一步一步搭建,功能最全的权限管理系统(一)
/// <summary>
/// 仓储基类
/// </summary>
/// <typeparam name="T">实体类型</typeparam>
/// <typeparam name="TPrimaryKey">主键类型</typeparam>
public abstract class Repository<T> : IRepository<T> where T : class, new()
{
    /// <summary>
    /// 删除
    /// </summary>
    /// <param name="deleteSql">删除sql</param>
    /// <returns></returns>
    public int Delete(string key, string deleteSql)
    {
        using var connection = DataBaseConnectConfig.GetSqlConnection();
        return connection.Execute(deleteSql, new { Key = key });
    }

    /// <summary>
    /// 根据主键获取模型
    /// </summary>
    /// <param name="id">主键</param>
    /// <param name="selectSql">查询sql</param>
    /// <returns></returns>
    public T GetByKey(string id, string selectSql)
    {
        using var connection = DataBaseConnectConfig.GetSqlConnection();
        return connection.QueryFirstOrDefault<T>(selectSql, new { Key = id });
    }

    /// <summary>
    /// 获取所有数据
    /// </summary>
    /// <param name="selectAllSql">查询sql</param>
    /// <returns></returns>
    public List<T> GetAll(string selectAllSql)
    {
        using var connection = DataBaseConnectConfig.GetSqlConnection();
        return connection.Query<T>(selectAllSql).ToList();
    }

    /// <summary>
    /// 新增
    /// </summary>
    /// <param name="entity">新增实体</param>
    /// <param name="innserSql">新增sql</param>
    /// <returns></returns>
    public int Insert(T entity, string innserSql)
    {
        using var connection = DataBaseConnectConfig.GetSqlConnection();
        return connection.Execute(innserSql, entity);
    }

    /// <summary>
    /// 根据唯一主键验证数据是否存在
    /// </summary>
    /// <param name="id">主键</param>
    /// <param name="selectSql">查询sql</param>
    /// <returns>返回true存在,false不存在</returns>
    public bool IsExist(string id, string selectSql)
    {
        using var connection = DataBaseConnectConfig.GetSqlConnection();
        var count = connection.QueryFirst<int>(selectSql, new { Key = id });
        if (count > 0)
            return true;
        else
            return false;
    }

    /// <summary>
    /// 更新
    /// </summary>
    /// <param name="entity">更新实体</param>
    /// <param name="updateSql">更新sql</param>
    /// <returns></returns>
    public int Update(T entity, string updateSql)
    {
        using var connection = DataBaseConnectConfig.GetSqlConnection();
        return connection.Execute(updateSql, entity);
    }

    /// <summary>
    /// 分页方法
    /// </summary>
    /// <typeparam name="T">泛型集合实体类</typeparam>
    /// <param name="pageResultModel">分页模型</param>
    /// <returns></returns>
    public PageResultModel<T> GetPageList<T>(PageResultModel pageResultModel)
    {
        PageResultModel<T> resultModel = new();
        using var connection = DataBaseConnectConfig.GetSqlConnection();
        int skip = 1;
        var orderBy = string.Empty;
        if (pageResultModel.pageIndex > 0)
        {
            skip = (pageResultModel.pageIndex - 1) * pageResultModel.pageSize + 1;
        }
        if (!string.IsNullOrEmpty(pageResultModel.orderByField))
        {
            orderBy = string.Format(" ORDER BY {0} {1} ", pageResultModel.orderByField, pageResultModel.sortType);
        }
        StringBuilder sb = new StringBuilder();
        sb.AppendFormat("SELECT COUNT(1) FROM {0} where 1=1 {1};", pageResultModel.tableName, pageResultModel.selectWhere);
        sb.AppendFormat(@"SELECT  *
                            FROM(SELECT ROW_NUMBER() OVER( {3}) AS RowNum,{0}
                                      FROM  {1}
                                      where 1=1 {2}
                                    ) AS result
                            WHERE  RowNum >= {4}   AND RowNum <= {5}
                             ", pageResultModel.tableField, pageResultModel.tableName, pageResultModel.selectWhere, orderBy, skip, pageResultModel.pageIndex * pageResultModel.pageSize);
        using var reader = connection.QueryMultiple(sb.ToString());
        resultModel.total = reader.ReadFirst<int>();
        resultModel.data = reader.Read<T>().ToList();
        return resultModel;
    }

}
View Code

  以上两段代码就实现了对数据库的增删改查。当然在上述仓储接口中有一个分页查询接口,它对于的模型如下

/// <summary>
/// 分页模型
/// </summary>
public class PageResultModel
{
    /// <summary>
    /// 当前页
    /// </summary>
    public int pageIndex { get; set; }

    /// <summary>
    /// 每页显示条数
    /// </summary>
    public int pageSize { get; set; }

    /// <summary>
    /// 查询表字段
    /// </summary>
    public string tableField { get; set; }

    /// <summary>
    /// 查询表
    /// </summary>
    public string tableName { get; set; }

    /// <summary>
    /// 查询条件
    /// </summary>
    public string selectWhere { get; set; }

    /// <summary>
    /// 查询条件json
    /// </summary>
    public string filterJson { get; set; }

    /// <summary>
    /// 当前菜单id
    /// </summary>
    public string menuId { get; set; }

    /// <summary>
    /// 排序字段(不能为空)
    /// </summary>
    public string orderByField { get; set; }

    /// <summary>
    /// 排序类型
    /// </summary>
    public string sortType { get; set; }

    /// <summary>
    /// 总数
    /// </summary>
    public int total { get; set; }
}

/// <summary>
/// 查询数据
/// </summary>
/// <typeparam name="T"></typeparam>
public class PageResultModel<T> : PageResultModel 
{
    /// <summary>
    /// 数据
    /// </summary>
    public List<T> data { get; set; }
}

上述代码解释:上述仓储接口中定义了所有基础接口,因为它们都是数据库操作最基本的存在,为了统一管理,降低耦合把它们定义到仓储中,以备后用。

  建立好基础仓储后,我们需要建立菜单表的仓储

  菜单仓储接口

 /// <summary>
 /// 菜单仓储
 /// </summary>
 public interface ISysMenuRepository : IRepository<Menu>
 {}

  菜单仓储接口实现

/// <summary>
/// 菜单仓储
/// </summary>
public class SysMenuRepository : Repository<Menu>, ISysMenuRepository
{}

  上述代码解释:可以看见上述代码继承了IRepository和Repository,这说明菜单拥有了增删改查等功能。

   4、创建领域服务,递归组织树形菜单结构

  在Domain领域层创建领域服务接口和实现接口

  领域服务接口

 /// <summary>
 /// 菜单服务接口
 /// </summary>
 public interface ISysMenuService
 {
      /// <summary>
     /// 获取所有菜单--上下级关系
     /// </summary>
     /// <returns></returns>
     List<MenuDao> GetAllChildren();
}

  领域接口实现

/// <summary>
/// 菜单服务实现
/// </summary>
public class SysMenuService : ISysMenuService
{

    //仓储接口
    private readonly ISysMenuRepository _menuRepository;

    /// <summary>
    /// 构造函数 实现依赖注入
    /// </summary>
    /// <param name="userRepository">仓储对象</param>
    public SysMenuService(ISysMenuRepository menuRepository)
    {
        _menuRepository = menuRepository;
    }

    /// <summary>
    /// 获取菜单--上下级关系
    /// </summary>
    /// <returns></returns>
    public List<MenuDao> GetAllChildren()
    {
        var list = _menuRepository.GetMenusList();
        var menuDaoList = MenuCore.GetMenuDao(list);
        return menuDaoList;
    }
}

  5、在Subdomain子域中创建菜单核心代码

  为什么在子域中创建菜单核心,应该菜单是整个系统的核心之一,考虑到之后系统会频繁使用,所以创建在子域中,以便提供给其他业务领域使用

  下面是递归菜单实现树形结构的核心

 public static class MenuCore
 {
     #region 用于菜单导航的树形结构

     /// <summary>
     /// 递归获取菜单结构--呈现上下级关系
     /// 用于菜单的树形结构
     /// </summary>
     /// <returns></returns>
     public static List<MenuDao> GetMenuDao(List<Menu> menuList)
     {
         List<MenuDao> list = new();
         List<MenuDao> menuListDto = new();
         foreach (var item in menuList)
         {
             MenuDao model = new()
             {
                 Title = item.MenuTitle,
                 Icon = item.MenuIcon,
                 Id = item.MenuUrl + "?MneuId=" + item.Id,
                 MenuKey = item.Id,
                 PMenuKey = item.Pid,
                 Component = item.Component,
                 Path = item.Path,
                 RequireAuth = item.RequireAuth,
                 Name = item.Name,
                 Redirect = item.Redirect,
                 IsOpen = item.IsOpen
             };
             list.Add(model);
         }
         foreach (var data in list.Where(f => f.PMenuKey == 0 && f.IsOpen))
         {
             var childrenList = GetChildrenMenu(list, data.MenuKey);
             data.children = childrenList.Count == 0 ? null : childrenList;
             menuListDto.Add(data);
         }
         return menuListDto;
     }

     /// <summary>
     /// 实现递归
     /// </summary>
     /// <param name="moduleOutput"></param>
     /// <param name="id"></param>
     /// <returns></returns>
     private static List<MenuDao> GetChildrenMenu(List<MenuDao> moduleOutput, int id)
     {
         List<MenuDao> sysShowTempMenus = new();
         //得到子菜单
         var info = moduleOutput.Where(w => w.PMenuKey == id && w.IsOpen).ToList();
         //循环
         foreach (var sysMenuInfo in info)
         {
             var childrenList = GetChildrenMenu(moduleOutput, sysMenuInfo.MenuKey);
             //把子菜单放到Children集合里
             sysMenuInfo.children = childrenList.Count == 0 ? null : childrenList;
             //添加父级菜单
             sysShowTempMenus.Add(sysMenuInfo);
         }
         return sysShowTempMenus;
     }
}

  以上便是后端实现动态菜单的核心代码,到这一节点,后端的工作基本完成。

  在Controller创建好接口后,运行后端代码,出现如图所示,便说明成功。

  一步一步搭建,功能最全的权限管理系统(一)

   6、vue 动态路由搭建

  配置vue动态路由前,需要看你选择的前端框架是什么,不同的框架,解析的字段不一样,我选择的是layui vue,动态配置如下:

一步一步搭建,功能最全的权限管理系统(一)一步一步搭建,功能最全的权限管理系统(一)
export const generator = (
  routeMap: any[],
  parentId: string | number,
  routeItem?: any | [],
) => {
  return routeMap
    //.filter(item => item.menuKey === parentId)
    .map(item => {

      const { title, requireAuth, menuKey } = item || {};
      const currentRouter: RouteRecordRaw = {
        // 如果路由设置了 path,则作为默认 path,否则 路由地址 动态拼接生成如 /dashboard/workplace
        path: item.path,
        // 路由名称,建议唯一
        //name: `${item.id}`,
        // meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
        meta: {
          title,
          requireAuth,
          menuKey
        },
        name: item.name,
        children: [],
        // 该路由对应页面的 组件 (动态加载 @/views/ 下面的路径文件)
        component: item.component && defineRouteComponentKeys.includes(item.component)
          ? defineRouteComponents[item.component]
          : () => url.value,

      };

      // 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
      if (!currentRouter.path.startsWith('http')) {
        currentRouter.path = currentRouter.path.replace('//', '/');
      }

      // 重定向
      item.redirect && (currentRouter.redirect = item.redirect);
      if (item.children != null) {
        // 子菜单,递归处理
        currentRouter.children = generator(item.children, item.menuKey, currentRouter);
      }
      if (currentRouter.children === undefined || currentRouter.children.length <= 0) {
        currentRouter.children;
      }
      return currentRouter;
    })
    .filter(item => item);
};
View Code

  通过以上代码,获取动态路由,然后把它加入到你的路由菜单中,这样便实现了页面菜单动态加载。

  四、项目效果

一步一步搭建,功能最全的权限管理系统(一)

  五、说明

  以上便是实现vue+webapi实现动态路由的全部核心代码

  注:关注我,我们一起搭建完整的权限管理系统。

   1、预览地址:http://139.155.137.144:8012

   2、qq群:801913255

 

到了这里,关于一步一步搭建,功能最全的权限管理系统(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [Openwrt]一步一步搭建MT7981A uboot、atf、openwrt-21.02开发环境操作说明

    软件安装包 ubuntu-18.04-desktop-amd64.iso  sudo passwd [sudo] password for w1804:  Enter new UNIX password:  Retype new UNIX password:  passwd: password updated successfully sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup 将以下内容添加到sources.list文件

    2024年02月12日
    浏览(119)
  • 超详细【入门精讲】数据仓库原理&实战 一步一步搭建数据仓库 内附相应实验代码和镜像数据和脚本

    文章对应的博客园链接:点击这里 建议搭配视频内容食用,效果更加。也可以直接按博客内容学习完成搭建 B站课程链接: 课程链接地址 下载UP主 哈喽鹏程 提供的资源镜像及脚本包 后续所使用的SQL脚本和shell脚本及job脚本文件,均在此下载,请自行下载 下载连接: 数据仓

    2024年02月03日
    浏览(52)
  • 一步一步搭建S32K14x的AutoSar项目----MCAL之MCU模块

    1-1、时钟对于任何一款微控制器是很重要的,所以我们首先要了解S32K144的时钟树,才能为后续的MCAL中MCU模块配置做好准备,废话不多说,先上一张图片,聊了解下 对应芯片手册的第27章 Clock Distribution 。 1、SOSC 就是连接外部的高速时钟,我们一般情况下会优先选择它,精准

    2024年01月22日
    浏览(45)
  • Vue + Element UI 实现权限管理系统 前端篇(十四):菜单功能实现菜

    菜单接口封装 菜单管理是一个对菜单树结构的增删改查操作。 提供一个菜单查询接口,查询整颗菜单树形结构。 http/modules/menu.js 添加 findMenuTree 接口。 菜单管理界面 菜单管理界面是使用封装的表格树组件显示菜单结构,并提供增删改查的功能。 Menu.vue 其中对表格树组件进

    2024年02月10日
    浏览(41)
  • 使用WebApi+Vue3从0到1搭建《权限管理系统》:二、搭建JWT系统鉴权

    视频地址:【WebApi+Vue3从0到1搭建《权限管理系统》系列视频:搭建JWT系统鉴权-哔哩哔哩】 https://b23.tv/R6cOcDO qq群:801913255 一、在appsettings.json中设置鉴权属性 二、新建模型 添加模型JwtSettingModel其中字段和appsettings.json中的字段一样,如下 三、新建解析appsettings.json节点的帮助类

    2024年04月22日
    浏览(36)
  • 基于VUE3+Layui从头搭建通用后台管理系统(前端篇)二:登录界面及对应功能实现

      本章介绍系统登录界面、登录流程、登录接口等相关内容的开发,实现包括账号密码登录、短信验证登录等不同的登录方式,使用svg-capter生成图形验证码,使用expressjwt实现登录token的生成及验证。 1. 详细课程地址: https://edu.csdn.net/course/detail/38183 2. 源码下载地址: 点击

    2024年02月11日
    浏览(64)
  • 史上功能最全的Java权限认证框架!

    大家好,我是 Java 陈序员 。权限认证是我们日常开发绕不过的话题,这是因为我们的应用程序需要防护,防止被窜入和攻击。 在 Java 后端开发中,实现权限认证有很多种方案可以选择,一个拦截器、过滤器也许就可以轻松搞定。当然,现在也有很多成熟的框架,供我们选择

    2024年02月05日
    浏览(47)
  • 一步一步学习 Stable Diffusion

    网上看了很多 Stable Diffusion 的视频,还有一些网站的资料之后,决定自己整理一份一步一步学习 Stable Diffusion 的学习资料。 请参照 Windows 11 本地部署 Stable Diffusion web UI 或者 Windows 11 上从零开始基于 wsl-ubuntu 搭建 AI 学习环境及部署多种私有 ChatGPT 进行部署。 访问 Extensions =

    2024年02月09日
    浏览(65)
  • 基于VUE3+Layui从头搭建通用后台管理系统(前端篇)十六:统计报表模块相关功能实现

      本章使用Echarts及DataV实现常用图表、特殊图表、地图及综合图表等图表展示功能。 1. 详细课程地址: https://edu.csdn.net/course/detail/38183 2. 源码下载地址: 点击下载 基于VUE3+Layui从

    2024年02月04日
    浏览(59)
  • 基于VUE3+Layui从头搭建通用后台管理系统(前端篇)三:找回密码界面及对应功能实现

      本章实现找回密码功能,包括短信验证码找回、邮箱验证码找回等功能,并通过node-send-email发送邮箱验证码,实现找回密码界面、接口等功能。 1. 详细课程地址: https://edu.csdn.net/course/detail/38183 2. 源码下载地址: 点击下载

    2024年02月12日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包