角色和权限的关联关系在前面文章中有讲解,见[golang gin框架] 14.Gin 商城项目-RBAC管理之角色和权限关联,角色授权,在这里通过微服务来实现角色对权限的授权操作,这里要实现的有两个功能,一个是进入授权,另一个是,授权提交操作,页面如下:
一.实现后台权限管理Rbac之角色权限关联微服务服务端功能
这里需要在proto/rbacRole.proto中增加两个方法:授权操作 Auth(), 执行授权操作DoAuth(),具体参考[golang gin框架] 14.Gin 商城项目-RBAC管理_角色权限管理之增加授权方法,原始代码如下:
//授权
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()相关方法代码,代码如下:
//角色管理
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,代码如下:
//权限相关模型:参考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切片,响应方法中返回是否操作完成,代码如下:
//角色授权提交参数
message RoleDoAuthRequest{
int64 roleId=1;
repeated string accessIds=2;
}
//角色授权提交响应参数
message RoleDoAuthResponse{
bool success=1;
string message=2;
}
完整代码如下:
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模型:
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中实现方法中对应的逻辑,代码如下:
//授权
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()中旧的代码,然后增加新的角色-权限关联微服务方法,旧的方法见文章开头代码,新代码如下:
//授权
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代码如下:
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微服务之权限的增删改查微服务