[golang gin框架] 45.Gin商城项目-微服务实战之后台Rbac微服务之角色权限关联

角色和权限的关联关系在前面文章中有讲解,见[golang gin框架] 14.Gin 商城项目-RBAC管理之角色和权限关联,角色授权,在这里通过微服务来实现角色对权限的授权操作,这里要实现的有两个功能,一个是进入授权,另一个是,授权提交操作,页面如下:

一.实现后台权限管理Rbac之角色权限关联微服务服务端功能

这里需要在proto/rbacRole.proto中增加两个方法:授权操作 Auth(), 执行授权操作DoAuth(), 具体参考[golang gin框架] 14.Gin 商城项目-RBAC管理_角色权限管理之增加授权方法,原始代码如下:

Go 复制代码
//授权
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")
}

1.在proto/rbacRole.proto下增加Auth(),DoAuth()方法相关代码

参考上述方法,在角色微服务的proto中的service RbacRole中增加Auth() ,**DoAuth()**相关方法代码,代码如下:

Go 复制代码
//角色管理
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) {}

    //授权
	rpc RoleAuth(RoleAuthRequest) returns (RoleAuthResponse) {}
	//授权提交
    rpc RoleDoAuth(RoleDoAuthRequest) returns (RoleDoAuthResponse) {}
}

这里需要实现角色授权请求方法RoleAuthRequest() ,角色授权请求响应方法RoleAuthResponse(),通过上述Auth()方法可以确定:RoleAuthRequest()请求中需要传递一个角色id roleId, 响应方法中返回权限切片 ,需要构建一个**message AccessModel,**代码如下:

Go 复制代码
//权限相关模型:参考models/access.go
message AccessModel{
	int64 id=1;
	string moduleName =2;
	string actionName=3;
	int64 type=4;
	string url=5;
	int64 moduleId=6;
	int64 sort =7;
	string description=8;
	int64 status=9;
 	int64 addTime=10;
	bool checked=11;
	repeated AccessModel accessItem=12;
}

//角色授权参数
message RoleAuthRequest{
	int64 roleId=1;
}
//角色授权响应参数
message RoleAuthResponse{
	repeated AccessModel accessList=1;
}

而实现角色授权提交请求方法RoleDoAuthRequest(),角色授权提交请求响应方法RoleDoAuthResponse(),通过上述Auth()方法可以确定:RoleDoAuthRequest请求中需要传递角色id roleId 以及角色对应的权限id切片,响应方法中返回是否操作完成,代码如下:

Go 复制代码
//角色授权提交参数
message RoleDoAuthRequest{
	int64 roleId=1;
	repeated string accessIds=2;
}

//角色授权提交响应参数
message RoleDoAuthResponse{
	bool success=1;
	string message=2;
}

完整代码如下:

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) {}

    //授权
	rpc RoleAuth(RoleAuthRequest) returns (RoleAuthResponse) {}
	//授权提交
    rpc RoleDoAuth(RoleDoAuthRequest) returns (RoleDoAuthResponse) {}
}

//角色相关model
message RoleModel{
	int64 id=1;
	string title=2;
	string description=3;
	int64 status=4;
	int64 addTime =5;
}

//权限相关模型:参考models/access.go
message AccessModel{
	int64 id=1;
	string moduleName =2;
	string actionName=3;
	int64 type=4;
	string url=5;
	int64 moduleId=6;
	int64 sort =7;
	string description=8;
	int64 status=9;
 	int64 addTime=10;
	bool checked=11;
	repeated AccessModel accessItem=12;
}

//获取角色请求参数
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;
}

//角色授权参数
message RoleAuthRequest{
	int64 roleId=1;
}
//角色授权响应参数
message RoleAuthResponse{
	repeated AccessModel accessList=1;
}

//角色授权提交参数
message RoleDoAuthRequest{
	int64 roleId=1;
	repeated string accessIds=2;
}

//角色授权提交响应参数
message RoleDoAuthResponse{
	bool success=1;
	string message=2;
}

2.编译一下rbacRole.proto

因为修改了rbacRole.proto,那么就需要再次使用命令protoc --proto_path=. --micro_out=. --go_out=:. proto/rbacRole.proto在在server/rbac下运行,重新编译,生成role相关pb.go,pb.micro.go文件

3.在handler/rbacRole.go文件中,实现新增的Auth(),DoAuth()方法

在实现之前,需要在models下引入角色-权限关联模型:RoleAcces.go模型:

Go 复制代码
package models

//角色-权限 关联表

type RoleAccess struct {
	AccessId int
	RoleId   int
}

func (RoleAccess) TableName() string {
	return "role_access"
}

然后才实现Auth(),DoAuth()方法,参考[golang gin框架] 14.Gin 商城项目-RBAC管理_角色权限关联 中的Auth(),DoAuth方法,并抽离其中的代码,在handler/rbacRole.go中实现方法中对应的逻辑,代码如下:

Go 复制代码
//授权
func (e *RbacRole) RoleAuth(ctx context.Context, req *pb.RoleAuthRequest, res *pb.RoleAuthResponse) error {
	//1、获取角色id  req.RoleId
	//获取所有权限列表
	accessList := []models.Access{}
	models.DB.Where("module_id = ?", 0).Preload("AccessItem").Find(&accessList)

	//获取当前角色拥有的权限,并把权限id放在一个map对象中
	roleAccess := []models.RoleAccess{}
	models.DB.Where("role_id = ?",  req.RoleId).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[int(accessList[i].Id)]; ok { // 判断当前权限是否在角色权限的map对象中, 需要进行类型转换
			accessList[i].Checked = true
		}
		for j := 0; j < len(accessList[i].AccessItem); j++ { // 判断当前权限的子栏位是否在角色权限的map中
			if _, ok := roleAccessMap[int(accessList[i].AccessItem[j].Id)]; ok { // 判断当前权限是否在角色权限的map对象中
				accessList[i].AccessItem[j].Checked = true
			}
		}
	}
	//处理数据:进行类型转换匹配操作
	var tempList []*pb.AccessModel
	for _, v := range accessList {
		var tempItemList []*pb.AccessModel
		for _, k := range v.AccessItem {
			tempItemList = append(tempItemList, &pb.AccessModel{
				Id:          int64(k.Id),
				ModuleName:  k.ModuleName,
				ActionName:  k.ActionName,
				Type:        int64(k.Type),
				Url:         k.Url,
				ModuleId:    int64(k.ModuleId),
				Sort:        int64(k.Sort),
				Description: k.Description,
				Status:      int64(k.Status),
				Checked:     k.Checked,
				AddTime:     int64(k.AddTime),
			})
		}
		tempList = append(tempList, &pb.AccessModel{
			Id:          int64(v.Id),
			ModuleName:  v.ModuleName,
			ActionName:  v.ActionName,
			Type:        int64(v.Type),
			Url:         v.Url,
			ModuleId:    int64(v.ModuleId),
			Sort:        int64(v.Sort),
			Description: v.Description,
			Status:      int64(v.Status),
			AddTime:     int64(v.AddTime),
			Checked:     v.Checked,
			AccessItem:  tempItemList,
		})
	}

	res.AccessList = tempList

	return nil
}

//执行授权
func (e *RbacRole) RoleDoAuth(ctx context.Context, req *pb.RoleDoAuthRequest, res *pb.RoleDoAuthResponse) error {
	//先删除当前角色对应的权限
	roleAccess := models.RoleAccess{}
	models.DB.Where("role_id = ?", req.RoleId).Delete(&roleAccess)

	//循环遍历accessIds,增加当前角色对应的权限
	for _, v := range req.AccessIds {
		roleAccess.RoleId = int(req.RoleId)
		accessId, _ :=strconv.Atoi(v)
		roleAccess.AccessId = accessId
		models.DB.Create(&roleAccess)
	}

	res.Success = true
	res.Message = "授权成功"
	return nil
}

4.实现后台权限管理Rbac之角色权限关联微服务客户端调用功能

因为修改了rbacRole.proto文件,并重新编译了,故要复制server/rbac/proto文件夹下rbacRole.go以及rbacRole文件夹到client项目中的proto文件夹下

然后在controllers/admin/roleController.go中实现Auth(),DoAuth()调用角色-权限关联微服务方法,需要先删除Auth(),DoAuth()中旧的代码,然后增加新的角色-权限关联微服务方法,旧的方法见文章开头代码,新代码如下:

Go 复制代码
//授权
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)

	//调用微服务获取角色授权相关数据
	rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)
	res, _ := rbacClient.RoleAuth(context.Background(), &pbRbac.RoleAuthRequest{
		RoleId: int64(id),
	})

	c.HTML(http.StatusOK, "admin/role/auth.html", gin.H{
		"roleId":     id,
		"accessList": res.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[]")
	//调用微服务执行授权
	rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)
	res, _ := rbacClient.RoleDoAuth(context.Background(), &pbRbac.RoleDoAuthRequest{
		RoleId:    int64(roleId),
		AccessIds: accessIds,
	})

	if res.Success {
		con.Success(c, "角色授权成功", "/admin/role")
		return
	}
	con.Error(c, "授权失败", "/admin/role/auth?id="+models.String(roleId))
}

完整的roleController.go代码如下:

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)

	//调用微服务获取角色授权相关数据
	rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)
	res, _ := rbacClient.RoleAuth(context.Background(), &pbRbac.RoleAuthRequest{
		RoleId: int64(id),
	})

	c.HTML(http.StatusOK, "admin/role/auth.html", gin.H{
		"roleId":     id,
		"accessList": res.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[]")
	//调用微服务执行授权
	rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)
	res, _ := rbacClient.RoleDoAuth(context.Background(), &pbRbac.RoleDoAuthRequest{
		RoleId:    int64(roleId),
		AccessIds: accessIds,
	})

	if res.Success {
		con.Success(c, "角色授权成功", "/admin/role")
		return
	}
	con.Error(c, "授权失败", "/admin/role/auth?id="+models.String(roleId))
}

二.校验权限管理Rbac之角色权限关联微服务功能

参考[golang gin框架] 43.Gin商城项目-微服务实战之后台Rbac微服务之管理员的增删改查以及管理员和角色关联

1.先启动服务端

[golang gin框架] 40.Gin商城项目-微服务实战之Captcha验证码微服务代码, 这里还要启动验证码captcha微服务服务端代码以及权限管理Rbac微服务(用户登录微服务服务端,角色管理微服务服务端,管理员管理微服务服务端)服务端才行

2.启动客户端

在项目根目录下运行 :go run main.go,启动项目

3.校验权限管理Rbac之角色权限关联微服务操作是否成功

访问后台登录页面,输入用户名,密码,验证码,登录到后台后,进入角色管理列表页面,对角色进行授权操作

好了,权限管理Rbac之角色权限关联微服务功能客户端操作完成,下面一节讲解:修改商城客户端调用微服务的权限验证以及Rbac微服务数据库抽离功能

上一节\][\[golang gin框架\] 44.Gin商城项目-微服务实战之后台Rbac微服务之权限的增删改查微服务](https://blog.csdn.net/zhoupenghui168/article/details/132008948 "[golang gin框架] 44.Gin商城项目-微服务实战之后台Rbac微服务之权限的增删改查微服务")

相关推荐
刀法如飞7 小时前
一款Go语言Gin框架MVC脚手架,满足大部分场景
go·mvc·gin
阿里云云原生21 小时前
MSE Nacos Prompt 管理:让 AI Agent 的核心配置真正可治理
微服务·云原生
阿里云云原生1 天前
阿里云微服务引擎 MSE 及 API 网关 2026 年 1 月产品动态
微服务
花酒锄作田1 天前
Gin 框架中的规范响应格式设计与实现
golang·gin
麦聪聊数据2 天前
统一 Web SQL 平台如何收编企业内部的“野生数据看板”?
数据库·sql·低代码·微服务·架构
qwfys2002 天前
How to install golang 1.26.0 to Ubuntu 24.04
ubuntu·golang·install
云司科技codebuddy2 天前
技术支持过硬Trae核心代理
大数据·运维·python·微服务
codeejun2 天前
每日一Go-25、Go语言进阶:深入并发模式1
开发语言·后端·golang
递归尽头是星辰2 天前
微服务事务分级治理:从 Seata 全模式到 TDSQL 实战
微服务·云原生·架构·分布式事务·事务分级治理
没有bug.的程序员2 天前
订单系统重构史诗:从单体巨兽到微服务矩阵的演进、数据一致性内核与分布式事务
java·微服务·矩阵·重构·分布式事务·数据一致性·订单系统