一.重构后台Rbac用户登录微服务功能
上一节讲解了后台Rbac微服务用户登录功能以及Gorm数据库配置单独抽离,Consul配置单独抽离,这一节讲解后台Rbac微服务角色增删改查微服务功能,Rbac微服务角色增删改查微服务和后台Rbac用户登录微服务 是属于同一个Rbac微服务的不同子微服务功能,为了区分不同子微服务功能,所以,在进行后台Rbac微服务角色增删改查微服务功能之前,需要对前面一节Rbac用户登录微服务进行重构
1.重新完善proto文件夹下的rbac.proto
把rbac.proto改名为rbacLogin.proto,并且修改option go_package,go_package = "./proto/rbacLogin", service Rbac该为service RbacLogin,其他代码不变,完整代码如下:
Go
syntax = "proto3";
package rbac;
option go_package = "./proto/rbacLogin";
service RbacLogin {
//登录操作
rpc Login(LoginRequest) returns (LoginResponse) {}
}
//用户信息model:参考models/manager.go,一一对应
message ManagerModel{
int64 id=1;
string username=2;
string password=3;
string mobile=4;
string email=5;
int64 status=6;
int64 roleId=7;
int64 addTime=8;
int64 isSuper=9;
}
//登录请求参数
message LoginRequest{
string username=1;
string password=2;
}
//登录返回参数
message LoginResponse{
bool isLogin=1;
repeated ManagerModel userlist=2; //因为在client端需要返回用户相关信息,故定义一个切片
}
2.删除proto生成的rbac文件夹
3.执行protoc --proto_path=. --micro_out=. --go_out=:. proto/rbacLogin.proto,生成对应的rbacLgin文件夹
4.修改handler文件夹下的rbac.go
把rbac.go文件名改为rbacLigin.go,并把pb "rbac/proto/rbac"修改为pb "rbac/proto/rbacLogin",以及type Rbac struct{}修改为type RbacLogin struct{},完整代码如下:
Go
package handler
import (
"context"
"rbac/models"
pb "rbac/proto/rbacLogin"
)
type RbacLogin struct{}
//后台用户登录的微服务
func (e *RbacLogin) Login(ctx context.Context, req *pb.LoginRequest, res *pb.LoginResponse) error {
managerList := []models.Manager{}
err := models.DB.Where("username=? AND password=?", req.Username, req.Password).Find(&managerList).Error
//处理数据
var templist []*pb.ManagerModel
for i := 0; i < len(managerList); i++ {
templist = append(templist, &pb.ManagerModel{
Id: int64(managerList[i].Id),
Username: managerList[i].Username,
Password: managerList[i].Password,
Mobile: managerList[i].Mobile,
Email: managerList[i].Email,
Status: int64(managerList[i].Status),
RoleId: int64(managerList[i].RoleId),
AddTime: int64(managerList[i].AddTime),
IsSuper: int64(managerList[i].IsSuper),
})
}
if len(managerList) > 0 {
res.IsLogin = true
} else {
res.IsLogin = false
}
res.Userlist = templist
return err
}
5.完善main.go
把main.go中的pb "rbac/proto/rbac"改成pb "rbac/proto/rbacLogin",以及下面的
pb.RegisterRbacHandler(srv.Server(), new(handler.Rbac))改为pb.RegisterRbacLoginHandler(srv.Server(), new(handler.RbacLogin)),完整代码如下:
Go
package main
import (
"rbac/handler"
"rbac/models"
pb "rbac/proto/rbacLogin"
"go-micro.dev/v4"
"go-micro.dev/v4/logger"
"github.com/go-micro/plugins/v4/registry/consul"
)
var (
service = "rbac"
version = "latest"
)
func main() {
//集成consul
consulReg := consul.NewRegistry()
// Create service
//读取.ini里面的配置
addr := models.Config.Section("consul").Key("addr").String()
srv := micro.NewService(
micro.Address(addr), //指定微服务的ip: 选择注册服务器地址,也可以不配置,默认为本机,也可以选择consul集群中的client
micro.Name(service),
micro.Version(version),
//注册consul
micro.Registry(consulReg),
)
srv.Init(
micro.Name(service),
micro.Version(version),
)
// Register handler
if err := pb.RegisterRbacLoginHandler(srv.Server(), new(handler.RbacLogin)); err != nil {
logger.Fatal(err)
}
// Run service
if err := srv.Run(); err != nil {
logger.Fatal(err)
}
}
6.运行该微服务,看看修改是否报错
7.完善客户端微服务
删除client下项目中proto文件夹下的rbac.proto文件以及rbac文件夹,把server/rbac/proto下的rbacLogin.go以及rbacLogin文件夹复制到client下项目中proto文件夹下
8.修改调用proto/rbac微服务接口的代码
这里只有controllers/login.go下调用了rbac 登录的微服务代码,所以把pbRbac "goshop/proto/rbac"修该为pbRbac "goshop/proto/rbacLogin",以及pbRbac.NewRbacService改为pbRbac.NewRbacLoginService即可
9.校验Rbac用户登录微服务操作是否成功
二.实现后台权限管理Rbac之角色增删改查微服务
1.创建Role模型
要实现角色的增删改查,就需要创建对应的模型,故在server/rbac/models下创建role.go模型文件,参考[golang gin框架] 14.Gin 商城项目-RBAC管理代码如下:
Go
package models
//角色模型
type Role struct { // 结构体首字母大写, 和数据库表名对应, 默认访问数据表users, 可以设置访问数据表的方法
Id int
Title string
Description string
Status int
AddTime int
}
//配置数据库操作的表名称
func (Role) TableName() string {
return "role"
}
2.在proto文件夹下创建rbacRole.proto
参考[golang gin框架] 14.Gin 商城项目-RBAC管理,创建rbacRole.proto.生成角色相关方法,具体代码如下:
Go
syntax = "proto3";
package rbac;
option go_package = "./proto/rbacRole";
//角色管理
service RbacRole {
//获取角色rpc方法: 请求参数RoleGetRequest, 响应参数RoleGetResponse
rpc RoleGet(RoleGetRequest) returns (RoleGetResponse) {}
//增加角色rpc方法: 请求参数RoleAddRequest, 响应参数RoleAddResponse
rpc RoleAdd(RoleAddRequest) returns (RoleAddResponse) {}
//编辑角色rpc方法: 请求参数RoleEditRequest, 响应参数RoleEditResponse
rpc RoleEdit(RoleEditRequest) returns (RoleEditResponse) {}
//删除角色rpc方法: 请求参数RoleDeleteRequest, 响应参数RoleDeleteResponse
rpc RoleDelete(RoleDeleteRequest) returns (RoleDeleteResponse) {}
}
//角色相关model
message RoleModel{
int64 id=1;
string title=2;
string description=3;
int64 status=4;
int64 addTime =5;
}
//获取角色请求参数
message RoleGetRequest{
//角色id
int64 id =1;
}
//获取角色响应参数
message RoleGetResponse{
//角色model切片
repeated RoleModel roleList=1;
}
//增加角色请求参数
message RoleAddRequest{
//角色名称
string title=1;
//说明
string description=2;
//状态
int64 status=3;
//增加时间
int64 addTime =4;
}
//增加角色响应参数
message RoleAddResponse{
//是否增加成功
bool success=1;
//返回状态说明
string message=2;
}
//编辑角色请求参数
message RoleEditRequest{
//角色id
int64 id=1;
//角色名称
string title=2;
//说明
string description=3;
//状态
int64 status=4;
//增加时间
int64 addTime =5;
}
//编辑角色响应参数
message RoleEditResponse{
//是否编辑成功
bool success=1;
//返回状态说明
string message=2;
}
//删除角色请求参数
message RoleDeleteRequest{
//角色id
int64 id=1;
}
//删除角色响应参数
message RoleDeleteResponse{
//是否删除成功
bool success=1;
//返回状态说明
string message=2;
}
3.生成role相关pb.go,pb.micro.go文件
在server/rbac下运行命令protoc --proto_path=. --micro_out=. --go_out=:. proto/rbacRole.proto即可
4.在handler文件夹下创建rbacRole.go文件,实现proto中的service方法
参考[golang gin框架] 14.Gin 商城项目-RBAC管理_角色的增删改查,具体rbacRole.go代码如下:
Go
package handler
import (
"context"
"rbac/models"
"strconv"
pb "rbac/proto/rbacRole"
)
type RbacRole struct{}
//获取角色
func (e *RbacRole) RoleGet(ctx context.Context, req *pb.RoleGetRequest, res *pb.RoleGetResponse) error {
roleList := []models.Role{}
where := "1=1"
if req.Id > 0 { // 当传入角色id时,获取对应的角色数据, 当没有传入角色id时,获取角色列表数据
where += " AND id=" + strconv.Itoa(int(req.Id))
}
models.DB.Where(where).Find(&roleList)
//处理数据
var tempList []*pb.RoleModel
for _, v := range roleList {
tempList = append(tempList, &pb.RoleModel{
Id: int64(v.Id),
Title: v.Title,
Description: v.Description,
Status: int64(v.Status),
AddTime: int64(v.AddTime),
})
}
res.RoleList = tempList
return nil
}
//增加角色
func (e *RbacRole) RoleAdd(ctx context.Context, req *pb.RoleAddRequest, res *pb.RoleAddResponse) error {
role := models.Role{}
role.Title = req.Title
role.Description = req.Description
role.Status = int(req.Status)
role.AddTime = int(req.AddTime)
err := models.DB.Create(&role).Error
if err != nil {
res.Success = false
res.Message = "增加数据失败"
} else {
res.Success = true
res.Message = "增加数据成功"
}
return err
}
//修改角色
func (e *RbacRole) RoleEdit(ctx context.Context, req *pb.RoleEditRequest, res *pb.RoleEditResponse) error {
role := models.Role{Id: int(req.Id)}
models.DB.Find(&role)
role.Title = req.Title
role.Description = req.Description
err := models.DB.Save(&role).Error
if err != nil {
res.Success = false
res.Message = "修改数据失败"
} else {
res.Success = true
res.Message = "修改数据成功"
}
return nil
}
//删除角色
func (e *RbacRole) RoleDelete(ctx context.Context, req *pb.RoleDeleteRequest, res *pb.RoleDeleteResponse) error {
role := models.Role{Id: int(req.Id)}
err := models.DB.Delete(&role).Error
if err != nil {
res.Success = false
res.Message = "删除数据失败"
} else {
res.Success = true
res.Message = "删除数据成功"
}
return nil
}
5.在main.go文件中注册注册角色微服务
只需在import中引入pbRole "rbac/proto/rbacRole"以及在main()中加入以下代码即可:
Go
// Register handler:注册角色微服务
if err := pbRole.RegisterRbacRoleHandler(srv.Server(), new(handler.RbacRole)); err != nil {
logger.Fatal(err)
}
具体代码如下:
Go
package main
import (
"rbac/handler"
"rbac/models"
pb "rbac/proto/rbacLogin"
pbRole "rbac/proto/rbacRole"
"go-micro.dev/v4"
"go-micro.dev/v4/logger"
"github.com/go-micro/plugins/v4/registry/consul"
)
var (
service = "rbac"
version = "latest"
)
func main() {
//集成consul
consulReg := consul.NewRegistry()
// Create service
//读取.ini里面的配置
addr := models.Config.Section("consul").Key("addr").String()
srv := micro.NewService(
micro.Address(addr), //指定微服务的ip: 选择注册服务器地址,也可以不配置,默认为本机,也可以选择consul集群中的client
micro.Name(service),
micro.Version(version),
//注册consul
micro.Registry(consulReg),
)
srv.Init(
micro.Name(service),
micro.Version(version),
)
// Register handler:注册登录微服务
if err := pb.RegisterRbacLoginHandler(srv.Server(), new(handler.RbacLogin)); err != nil {
logger.Fatal(err)
}
// Register handler:注册角色微服务
if err := pbRole.RegisterRbacRoleHandler(srv.Server(), new(handler.RbacRole)); err != nil {
logger.Fatal(err)
}
// Run service
if err := srv.Run(); err != nil {
logger.Fatal(err)
}
}
6.实现Rbac角色客户端微服务功能
参考[golang gin框架] 41.Gin商城项目-微服务实战之后台Rbac微服务(用户登录 、Gorm数据库配置单独抽离、 Consul配置单独抽离)
(1).复制server/rbac/proto文件夹下rbacRole.go以及rbacRole文件夹到client项目中的proto文件夹下
(2). 调用Rbac角色增删改查微服务
在controllers/admin/role.go的Index(),DoAdd(),DoEdit(),Delete()方法中调用Rbac角色增删改查微服务功能
原controllers/admin/role.go代码如下:也可参考[golang gin框架] 14.Gin 商城项目-RBAC管理
Go
package admin
import (
"github.com/gin-gonic/gin"
"goshop/models"
"net/http"
"strings"
)
type RoleController struct {
BaseController
}
//角色列表
func (con RoleController) Index(c *gin.Context) {
//定义一个角色切片
roleList := []models.Role{}
//获取角色
models.DB.Find(&roleList)
c.HTML(http.StatusOK, "admin/role/index.html", gin.H{
"roleList": roleList,
})
}
//新增角色
func (con RoleController) Add(c *gin.Context) {
c.HTML(http.StatusOK, "admin/role/add.html", gin.H{})
}
//新增角色:提交
func (con RoleController) DoAdd(c *gin.Context) {
//获取表单的提交数据
//strings.Trim(str, cutset), 去除字符串两边的cutset字符
title := strings.Trim(c.PostForm("title"), " ") // 去除字符串两边的空格
description := strings.Trim(c.PostForm("description"), " ")
//判断角色名称是否为空
if title == "" {
con.Error(c, "角色名称不能为空", "/admin/role/add")
return
}
//给角色模型赋值,并保存数据到数据库
role := models.Role{}
role.Title = title
role.Description = description
role.Status = 1
role.AddTime = int(models.GetUnix())
err := models.DB.Create(&role).Error
if err != nil {
con.Error(c, "增加角色失败,请重试", "/admin/role/add")
return
}
con.Success(c, "增加角色成功", "/admin/role")
}
//编辑角色
func (con RoleController) Edit(c *gin.Context) {
//获取角色id
id, err := models.Int(c.Query("id"))
if err != nil {
con.Error(c, "传入数据错误", "/admin/role")
} else {
role := models.Role{Id: id}
models.DB.Find(&role)
c.HTML(http.StatusOK, "admin/role/edit.html", gin.H{
"role": role,
})
}
}
//编辑角色:提交
func (con RoleController) DoEdit(c *gin.Context) {
//获取提交的表单数据
id, err := models.Int(c.PostForm("id"))
if err != nil {
con.Error(c, "传入数据错误", "/admin/role")
return
}
//获取表单的提交数据
//strings.Trim(str, cutset), 去除字符串两边的cutset字符
title := strings.Trim(c.PostForm("title"), " ") // 去除字符串两边的空格
description := strings.Trim(c.PostForm("description"), " ")
//判断角色名称是否为空
if title == "" {
con.Error(c, "角色名称不能为空", "/admin/role/add")
return
}
//查询角色是否存在
role := models.Role{Id: id}
models.DB.Find(&role)
//修改角色属性
role.Title = title
role.Description = description
err = models.DB.Save(&role).Error
if err != nil {
con.Error(c, "修改数据失败", "/admin/role/edit?id="+models.String(id))
return
}
con.Success(c, "修改数据成功", "/admin/role")
}
//删除角色
func (con RoleController) Delete(c *gin.Context) {
//获取提交的表单数据
id, err := models.Int(c.Query("id"))
if err != nil {
con.Error(c, "传入数据错误", "/admin/role")
return
}
//查询角色是否存在
role := models.Role{Id: id}
err = models.DB.Delete(&role).Error
if err != nil {
con.Error(c, "删除数据失败", "/admin/role")
return
}
con.Success(c, "删除数据成功", "/admin/role")
}
//授权
func (con RoleController) Auth(c *gin.Context) {
//获取id
id, err := models.Int(c.Query("id"))
if err != nil {
con.Error(c, "传入数据错误", "/admin/role")
return
}
role := models.Role{Id: id}
models.DB.Find(&role)
//获取所有权限列表
accessList := []models.Access{}
models.DB.Where("module_id = ?", 0).Preload("AccessItem").Find(&accessList)
//获取当前角色拥有的权限,并把权限id放在一个map对象中
roleAccess := []models.RoleAccess{}
models.DB.Where("role_id = ?", id).Find(&roleAccess)
roleAccessMap := make(map[int]int)
for _, v := range roleAccess {
roleAccessMap[v.AccessId] = v.AccessId
}
//循环遍历所有权限数据,判断当前权限的id是否在角色权限的map对象中,如果是的话给当前数据加入checked属性
for i := 0; i < len(accessList); i++ { //循环权限列表
if _, ok := roleAccessMap[accessList[i].Id]; ok { // 判断当前权限是否在角色权限的map对象中
accessList[i].Checked = true
}
for j := 0; j < len(accessList[i].AccessItem); j++ { // 判断当前权限的子栏位是否在角色权限的map中
if _, ok := roleAccessMap[accessList[i].AccessItem[j].Id]; ok { // 判断当前权限是否在角色权限的map对象中
accessList[i].AccessItem[j].Checked = true
}
}
}
c.HTML(http.StatusOK, "admin/role/auth.html", gin.H{
"roleId": id,
"accessList": accessList,
})
}
//授权提交
func (con RoleController) DoAuth(c *gin.Context) {
//获取提交的表单数据
roleId, err := models.Int(c.PostForm("role_id"))
if err != nil {
con.Error(c, "传入数据错误", "/admin/role")
return
}
//获取表单提交的权限id切片
accessIds := c.PostFormArray("access_node[]")
//先删除当前角色对应的权限
roleAccess := models.RoleAccess{}
models.DB.Where("role_id = ?", roleId).Delete(&roleAccess)
//循环遍历accessIds,增加当前角色对应的权限
for _, v := range accessIds {
roleAccess.RoleId = roleId
accessId, _ := models.Int(v)
roleAccess.AccessId = accessId
models.DB.Create(&roleAccess)
}
con.Success(c, "角色授权成功", "/admin/role")
}
调用Rbac角色增删改查微服务后,代码如下:
Go
package admin
import (
"context"
"github.com/gin-gonic/gin"
"goshop/models"
pbRbac "goshop/proto/rbacRole"
"net/http"
"strings"
)
type RoleController struct {
BaseController
}
//角色列表
func (con RoleController) Index(c *gin.Context) {
//调用Rbac微服务
rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)
res, _ := rbacClient.RoleGet(context.Background(), &pbRbac.RoleGetRequest{})
c.HTML(http.StatusOK, "admin/role/index.html", gin.H{
"roleList": res.RoleList,
})
}
//新增角色
func (con RoleController) Add(c *gin.Context) {
c.HTML(http.StatusOK, "admin/role/add.html", gin.H{})
}
//新增角色:提交
func (con RoleController) DoAdd(c *gin.Context) {
//获取表单的提交数据
//strings.Trim(str, cutset), 去除字符串两边的cutset字符
title := strings.Trim(c.PostForm("title"), " ") // 去除字符串两边的空格
description := strings.Trim(c.PostForm("description"), " ")
//判断角色名称是否为空
if title == "" {
con.Error(c, "角色名称不能为空", "/admin/role/add")
return
}
//调用微服务,实现角色的添加
rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)
res, _ := rbacClient.RoleAdd(context.Background(), &pbRbac.RoleAddRequest{
Title: title,
Description: description,
AddTime: models.GetUnix(),
Status: 1,
})
if !res.Success {
con.Error(c, "增加角色失败 请重试", "/admin/role/add")
} else {
con.Success(c, "增加角色成功", "/admin/role")
}
}
//编辑角色
func (con RoleController) Edit(c *gin.Context) {
//获取角色id
id, err := models.Int(c.Query("id"))
if err != nil {
con.Error(c, "传入数据错误", "/admin/role")
} else {
//调用微服务,获取角色信息
rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)
res, _ := rbacClient.RoleGet(context.Background(), &pbRbac.RoleGetRequest{
Id: int64(id),
})
c.HTML(http.StatusOK, "admin/role/edit.html", gin.H{
"role": res.RoleList[0],
})
}
}
//编辑角色:提交
func (con RoleController) DoEdit(c *gin.Context) {
//获取提交的表单数据
id, err := models.Int(c.PostForm("id"))
if err != nil {
con.Error(c, "传入数据错误", "/admin/role")
return
}
//获取表单的提交数据
//strings.Trim(str, cutset), 去除字符串两边的cutset字符
title := strings.Trim(c.PostForm("title"), " ") // 去除字符串两边的空格
description := strings.Trim(c.PostForm("description"), " ")
//判断角色名称是否为空
if title == "" {
con.Error(c, "角色名称不能为空", "/admin/role/add")
return
}
//调用微服务修改
rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)
res, _ := rbacClient.RoleEdit(context.Background(), &pbRbac.RoleEditRequest{
Id: int64(id),
Title: title,
Description: description,
})
if !res.Success {
con.Error(c, "修改数据失败", "/admin/role/edit?id="+models.String(id))
return
}
con.Success(c, "修改数据成功", "/admin/role")
}
//删除角色
func (con RoleController) Delete(c *gin.Context) {
//获取提交的表单数据
id, err := models.Int(c.Query("id"))
if err != nil {
con.Error(c, "传入数据错误", "/admin/role")
return
}
rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)
res, _ := rbacClient.RoleDelete(context.Background(), &pbRbac.RoleDeleteRequest{
Id: int64(id),
})
if res.Success {
con.Success(c, "删除数据成功", "/admin/role")
return
}
con.Error(c, "删除数据失败", "/admin/role")
}
//授权
func (con RoleController) Auth(c *gin.Context) {
//获取id
id, err := models.Int(c.Query("id"))
if err != nil {
con.Error(c, "传入数据错误", "/admin/role")
return
}
role := models.Role{Id: id}
models.DB.Find(&role)
//获取所有权限列表
accessList := []models.Access{}
models.DB.Where("module_id = ?", 0).Preload("AccessItem").Find(&accessList)
//获取当前角色拥有的权限,并把权限id放在一个map对象中
roleAccess := []models.RoleAccess{}
models.DB.Where("role_id = ?", id).Find(&roleAccess)
roleAccessMap := make(map[int]int)
for _, v := range roleAccess {
roleAccessMap[v.AccessId] = v.AccessId
}
//循环遍历所有权限数据,判断当前权限的id是否在角色权限的map对象中,如果是的话给当前数据加入checked属性
for i := 0; i < len(accessList); i++ { //循环权限列表
if _, ok := roleAccessMap[accessList[i].Id]; ok { // 判断当前权限是否在角色权限的map对象中
accessList[i].Checked = true
}
for j := 0; j < len(accessList[i].AccessItem); j++ { // 判断当前权限的子栏位是否在角色权限的map中
if _, ok := roleAccessMap[accessList[i].AccessItem[j].Id]; ok { // 判断当前权限是否在角色权限的map对象中
accessList[i].AccessItem[j].Checked = true
}
}
}
c.HTML(http.StatusOK, "admin/role/auth.html", gin.H{
"roleId": id,
"accessList": accessList,
})
}
//授权提交
func (con RoleController) DoAuth(c *gin.Context) {
//获取提交的表单数据
roleId, err := models.Int(c.PostForm("role_id"))
if err != nil {
con.Error(c, "传入数据错误", "/admin/role")
return
}
//获取表单提交的权限id切片
accessIds := c.PostFormArray("access_node[]")
//先删除当前角色对应的权限
roleAccess := models.RoleAccess{}
models.DB.Where("role_id = ?", roleId).Delete(&roleAccess)
//循环遍历accessIds,增加当前角色对应的权限
for _, v := range accessIds {
roleAccess.RoleId = roleId
accessId, _ := models.Int(v)
roleAccess.AccessId = accessId
models.DB.Create(&roleAccess)
}
con.Success(c, "角色授权成功", "/admin/role")
}
三.校验权限管理Rbac微服务功能
1.先启动服务端
见[golang gin框架] 40.Gin商城项目-微服务实战之Captcha验证码微服务代码, 这里还要启动验证码captcha微服务服务端代码以及Rbac用户登录微服务服务端代码才行
2.启动客户端
在项目根目录下运行 :go run main.go,启动项目
3.校验权限管理Rbac角色增删改查微服务操作是否成功
访问后台登录页面,输入用户名,密码,验证码,登录到后台后,进入角色管理页面,对角色进行增删改查
好了,权限管理Rbac角色增删改查微服务功能客户端操作完成,这里微服务操作的服务端,客户端功能大致与[golang gin框架] 41.Gin商城项目-微服务实战之后台Rbac微服务(用户登录 、Gorm数据库配置单独抽离、 Consul配置单独抽离)类似,可参考该文章操作,下面一节继续讲解权限管理Rbac微服务管理员的增删改查以及管理员和角色关联功能
[上一节]R[golang gin框架] 41.Gin商城项目-微服务实战之后台Rbac微服务(用户登录 、Gorm数据库配置单独抽离、 Consul配置单独抽离)