Skip to content

一步步实现微服务权限管理系统(8)

前言

  • 前端参考了很多框架,可谓百花齐放,但很多封装过剩,不利于学习和应用,最终我选择了 [vue-pure-admin](https://github.com/pure-admin/vue-pure-admin)
  • 后端我将使用 go-zero 来带领大家一步步实现一个权限管理系统
  • 本系列项目存放在 purezeroadmin 中,每一部分我都将打tag,并保证每个tag能正常运行。请多点赞和评论。
  • 后面示例均为 purezeroadmin 项目为例,你们可以根据需要自建工程来进行试验。均采用vscode进行试验。
  • go-zero 常用命令我将放入其对应的 makefile 文件中。

本节概述

  • 路由数据持久化
  • 路由数据组装

后端修改

新增 tb_router 表及相关数据

  • 生成对应数据
/*!40101 SET NAMES utf8mb4 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`purezeroadmin` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;

USE `purezeroadmin`;

/*Table structure for table `tb_router` */

CREATE TABLE `tb_router` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `parent_id` bigint NOT NULL DEFAULT '0',
  `path` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
  `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
  `component` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
  `meta_title` varchar(64) NOT NULL,
  `meta_icon` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '',
  `meta_rank` bigint NOT NULL DEFAULT '0',
  `meta_roles` json DEFAULT NULL,
  `meta_auths` json DEFAULT NULL,
  `create_ts` bigint NOT NULL DEFAULT '0',
  `update_ts` bigint NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

/*Data for the table `tb_router` */

insert  into `tb_router`(`id`,`parent_id`,`path`,`name`,`component`,`meta_title`,`meta_icon`,`meta_rank`,`meta_roles`,`meta_auths`,`create_ts`,`update_ts`) values (1,0,'/permission','','','权限管理','ep:lollipop',10,NULL,NULL,0,0);
insert  into `tb_router`(`id`,`parent_id`,`path`,`name`,`component`,`meta_title`,`meta_icon`,`meta_rank`,`meta_roles`,`meta_auths`,`create_ts`,`update_ts`) values (2,1,'/permission/page/index','PermissionPage','','页面权限','',0,'[\"admin\"]',NULL,0,0);
insert  into `tb_router`(`id`,`parent_id`,`path`,`name`,`component`,`meta_title`,`meta_icon`,`meta_rank`,`meta_roles`,`meta_auths`,`create_ts`,`update_ts`) values (3,1,'/permission/button','','','按钮权限','',0,'[\"admin\", \"common\"]',NULL,0,0);
insert  into `tb_router`(`id`,`parent_id`,`path`,`name`,`component`,`meta_title`,`meta_icon`,`meta_rank`,`meta_roles`,`meta_auths`,`create_ts`,`update_ts`) values (4,3,'/permission/button/router','PermissionButtonRouter','permission/button/index','路由返回按钮权限','',0,NULL,'[\"permission:btn:add\", \"permission:btn:edit\", \"permission:btn:delete\"]',0,0);
insert  into `tb_router`(`id`,`parent_id`,`path`,`name`,`component`,`meta_title`,`meta_icon`,`meta_rank`,`meta_roles`,`meta_auths`,`create_ts`,`update_ts`) values (5,3,'/permission/button/login','PermissionButtonLogin','permission/button/perms','登录接口返回按钮权限','',0,NULL,NULL,0,0);
  • 生成 models
goctl model mysql datasource --url "root:123456@tcp(127.0.0.1:55506)/purezeroadmin"  -t="*" --dir user-api/models --home template
  • 配置 muser-api/internal/svc/servicecontext.go
type ServiceContext struct {
    Config        config.Config
    TbUserModel   models.TbUserModel
    TbRoleModel   models.TbRoleModel
    TbRouterModel models.TbRouterModel
}

func NewServiceContext(c config.Config) *ServiceContext {
    conn := sqlx.NewMysql(c.Dsn)
    return &ServiceContext{
        Config:        c,
        TbUserModel:   models.NewTbUserModel(conn),
        TbRoleModel:   models.NewTbRoleModel(conn),
        TbRouterModel: models.NewTbRouterModel(conn),
    }
}
  • user-api/models/tbroutermodel.go 增加函数
...

TbRouterModel interface {
    tbRouterModel
    withSession(session sqlx.Session) TbRouterModel
    FindAllFromParentID(ctx context.Context, parentID int64) ([]*TbRouter, error)
}

...

func (m *customTbRouterModel) FindAllFromParentID(ctx context.Context, parentID int64) ([]*TbRouter, error) {
    var resp []*TbRouter
    err := m.conn.QueryRowsCtx(ctx, &resp, "select * from tb_router where parent_id = ?", parentID)
    return resp, err
}

user-api/api/user.apiRouterData 返回修改为指针形式

type RouterData {
    Path      string        `json:"path"`
    Name      string        `json:"name,omitempty"`
    Component string        `json:"component,omitempty"`
    Meta      Meta          `json:"meta"`
    Children  []*RouterData `json:"children,omitempty"`
}

...

@server (
    jwt: Auth // 开启 jwt 认证
)
service user-api {
    @doc "获取路由"
    @handler userRouter
    get /api/get-async-routes (UserRouterReq) returns ([]*RouterData)
}

user-api/internal/logic/userrouterlogic.go 主要生成逻辑

func (l *UserRouterLogic) UserRouter(req *types.UserRouterReq) (resp []*types.RouterData, err error) {
    userID, err := helper.GetUserIDFromContext(l.ctx)
    if err != nil {
        return nil, err
    }

    tbUser, err := l.svcCtx.TbUserModel.FindOne(l.ctx, userID)
    if err != nil {
        return nil, err
    }

    roles, permissions, err := helper.GetAuths(l.ctx, l.svcCtx, tbUser)
    if err != nil {
        return nil, err
    }

    isAdmin := arrutil.Contains(roles, "admin")

    return l.GetRecursionRoutersByParentID(0, isAdmin, roles, permissions)
}

func (l *UserRouterLogic) GetRouterByID(id int64, isAdmin bool, roles, permissions []string) (routerData *types.RouterData, err error) {

    router, err := l.svcCtx.TbRouterModel.FindOne(l.ctx, id)
    if err != nil {
        return nil, err
    }

    return helper.RouterToData(router, isAdmin, roles, permissions)
}

func (l *UserLoginLogic) GetRoutersByParentID(parentID int64, isAdmin bool, roles, permissions []string) (routerDatas []*types.RouterData, err error) {
    routers, err := l.svcCtx.TbRouterModel.FindAllFromParentID(l.ctx, parentID)
    if err != nil {
        return nil, err
    }

    for _, v := range routers {
        routerData, err := helper.RouterToData(v, isAdmin, roles, permissions)
        if err != nil {
            return nil, err
        }
        routerDatas = append(routerDatas, routerData)
    }

    return routerDatas, nil
}

func (l *UserRouterLogic) UpdateRouterData(routerData *types.RouterData, id int64, isAdmin bool, roles, permissions []string) (err error) {
    routers, err := l.svcCtx.TbRouterModel.FindAllFromParentID(l.ctx, id)
    if err != nil {
        return err
    }

    for _, v := range routers {
        child, err := l.GetRecursionRouterByID(v.Id, isAdmin, roles, permissions)
        if err != nil {
            return err
        }
        if child == nil {
            continue
        }
        routerData.Children = append(routerData.Children, child)
    }
    return nil
}

func (l *UserRouterLogic) GetRecursionRouterByID(id int64, isAdmin bool, roles, permissions []string) (routerData *types.RouterData, err error) {

    routerData, err = l.GetRouterByID(id, isAdmin, roles, permissions)
    if err != nil {
        return nil, err
    }

    err = l.UpdateRouterData(routerData, id, isAdmin, roles, permissions)
    return routerData, err
}

func (l *UserRouterLogic) GetRecursionRoutersByParentID(parentID int64, isAdmin bool, roles, permissions []string) (routerDatas []*types.RouterData, err error) {
    routers, err := l.svcCtx.TbRouterModel.FindAllFromParentID(l.ctx, parentID)
    if err != nil {
        return nil, err
    }

    for _, v := range routers {
        routerData, err := helper.RouterToData(v, isAdmin, roles, permissions)
        if err != nil {
            return nil, err
        }
        err = l.UpdateRouterData(routerData, v.Id, isAdmin, roles, permissions)
        if err != nil {
            return nil, err
        }
        routerDatas = append(routerDatas, routerData)
    }

    return routerDatas, nil
}

测试

  • 登陆不同用户可观察到菜单变化

tag版本

purezeroadmin 项目下

git checkout v1.7.0

接下来

casbin的引入

Comments