[golang gin框架] 46.Gin商城项目-微服务实战之后台Rbac客户端调用微服务权限验证以及Rbac微服务数据库抽离

一. 根据用户的权限动态显示左侧菜单微服务

1.引入

后台Rbac客户端调用微服务权限验证功能主要是登录后显示用户名称、根据用户的权限动态显示左侧菜单,判断当前登录用户的权限 、没有权限访问则拒绝,参考[golang gin框架] 14.Gin 商城项目-RBAC管理,该微服务功能和上一节[golang gin框架] 45.Gin商城项目-微服务实战之后台Rbac微服务之角色权限关联功能是一样的,因为controllers/admin/mainController.goIndex()方法都是要获取所有权限列表,以及当前角色拥有的权限,代码如下:


func (con MainController) Index(c *gin.Context) {
	//获取Session里面保存的用户信息
	session := sessions.Default(c)
	userinfo := session.Get("userinfo_admin")
	//判断Session中的用户信息是否存在,如果不存在跳转到登录页面(注意需要判断) 如果存在继续向下执行
	//session.Get获取返回的结果是一个空接口类型,所以需要进行类型断言: 判断userinfo是不是一个string
	userinfoStr, ok := userinfo.(string)
	if ok { // 说明是一个string
		//1.获取用户信息
		var userinfoStruct []models.Manager
		//把获取到的用户信息转换结构体
		json.Unmarshal([]byte(userinfoStr), &userinfoStruct)

		//获取所有权限列表
		accessList := []models.Access{}
		models.DB.Where("module_id = ?", 0).Preload("AccessItem", func(db *gorm.DB) *gorm.DB {
			return db.Order("access.sort DESC")
		}).Order("sort DESC").Find(&accessList)

		//获取当前角色拥有的权限,并把权限id放在一个map对象中
		roleAccess := []models.RoleAccess{}
		models.DB.Where("role_id = ?", userinfoStruct[0].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[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/main/index.html", gin.H{
			"username": userinfoStruct[0].Username,
			"isSuper": userinfoStruct[0].IsSuper,  // 是否超级管理员, 超级管理员显示全部
			"accessList": accessList,
		})
	} else {
		c.Redirect(http.StatusFound, "/admin/login")
	}
}

2. 完善handler/rbacRole.go下的RoleAuth()方法

上面的代码和上一节的handler/rbacRole.go下的RoleAuth()方法类似,差别在于上面有排序功能,把上面排序的代码替换微服务RoleAuth()中对应的代码,然后在controllers/admin/mainController.go的Index()方法中调用一下这个微服务的RoleAuth()方法就可以了,替换后,handler/rbacRole.go下的RoleAuth()方法代码如下:

//授权
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", func(db *gorm.DB) *gorm.DB {
		return db.Order("access.sort DESC")
	}).Order("sort DESC").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
}

3. controllers/admin/mainController.go的Index()调用微服务的RoleAuth()方法,判断是否显示对应的菜单

然后在controllers/admin/mainController.go的Index()方法中调用一下这个微服务的RoleAuth()方法,代码如下:


func (con MainController) Index(c *gin.Context) {
	//获取Session里面保存的用户信息
	session := sessions.Default(c)
	userinfo := session.Get("userinfo_admin")
	//判断Session中的用户信息是否存在,如果不存在跳转到登录页面(注意需要判断) 如果存在继续向下执行
	//session.Get获取返回的结果是一个空接口类型,所以需要进行类型断言: 判断userinfo是不是一个string
	userinfoStr, ok := userinfo.(string)
	if ok { // 说明是一个string
		//1.获取用户信息
		var userinfoStruct []models.Manager
		//把获取到的用户信息转换结构体
		json.Unmarshal([]byte(userinfoStr), &userinfoStruct)

		//调用微服务获取当前角色拥有权限(菜单)
		rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)
		res, _ := rbacClient.RoleAuth(context.Background(), &pbRbac.RoleAuthRequest{
			RoleId: int64(userinfoStruct[0].RoleId),
		})

		c.HTML(http.StatusOK, "admin/main/index.html", gin.H{
			"username":   userinfoStruct[0].Username,
			"accessList": res.AccessList,  // 从微服务返回中获取数据
			"isSuper":    userinfoStruct[0].IsSuper, // 是否超级管理员, 超级管理员显示全部
		})
	} else {
		c.Redirect(http.StatusFound, "/admin/login")
	}
}

经过验证,发现没问题,这里就不再说明了,因为微服务RoleAuth()方法基本上没有发生变化,只是后台mainControllers.go的Index方法调用了而已

二.权限判断: 没有登录的用户不能进入后台管理中心

1.抽离adminAuth.go中间件代码,做成一个微服务方法

(1).proto/rbacRole.proto增加rpc MiddlewaresAuth方法

 这里的权限判断: 没有登录的用户不能进入后台管理中心功能以前是放在middlewares/adminAuth.go中间件里面进行判断的,现在,把里面的逻辑代码抽离出来,做成一个微服务,先来看看adminAuth.go中间件里面的代码:

package middlewares

//中间件: 作用: 在执行路由之前或者之后进行相关逻辑判断

import (
	"encoding/json"
	"fmt"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"gopkg.in/ini.v1"
	"goshop/models"
	"net/http"
	"os"
	"strings"
)

func InitAdminAuthMiddleware(c *gin.Context) {
	//权限判断: 没有登录的用户不能进入后台管理中心

	//1、获取Url访问的地址
	//当地址后面带参数时:,如: admin/captcha?t=0.8706946438889653,需要处理
	//strings.Split(c.Request.URL.String(), "?"): 把c.Request.URL.String()请求地址按照?分割成切片
	pathname := strings.Split(c.Request.URL.String(), "?")[0]

	//2、获取Session里面保存的用户信息
	session := sessions.Default(c)
	userinfo := session.Get("userinfo_admin")
	//3、判断Session中的用户信息是否存在,如果不存在跳转到登录页面(注意需要判断) 如果存在继续向下执行
	//session.Get获取返回的结果是一个空接口类型,所以需要进行类型断言: 判断userinfo是不是一个string
	userinfoStr, ok := userinfo.(string)
	if ok { // 说明是一个string
		var userinfoStruct []models.Manager
		//把获取到的用户信息转换结构体
		err := json.Unmarshal([]byte(userinfoStr), &userinfoStruct)
		if err != nil || !(len(userinfoStruct) > 0 && userinfoStruct[0].Username != "") {
			if pathname != "/admin/login" && pathname != "/admin/dologin" && pathname != "/admin/captcha" {
				//跳转到登录页面
				c.Redirect(http.StatusFound, "/admin/login")
			}
		} else { //表示用户登录成功
			//获取当前访问的URL对应的权限id,判断权限id是否在角色对应的权限中
			// strings.Replace 字符串替换
			urlPath := strings.Replace(pathname, "/admin/", "", 1)
			//排除权限判断:不是超级管理员并且不在相关权限内
			if userinfoStruct[0].IsSuper == 0 && !excludeAuthPath("/" + urlPath){
				//判断用户权限:当前用户权限是否可以访问url地址
				//获取当前角色拥有的权限,并把权限id放在一个map对象中
				roleAccess := []models.RoleAccess{}
				models.DB.Where("role_id = ?", userinfoStruct[0].RoleId).Find(&roleAccess)
				roleAccessMap := make(map[int]int)
				for _, v := range roleAccess {
					roleAccessMap[v.AccessId] = v.AccessId
				}
				//实例化access
				access := models.Access{}
				//查询权限id
				models.DB.Where("url = ? ", urlPath).Find(&access)
				//判断权限id是否在角色对应的权限中
				if _, ok := roleAccessMap[access.Id]; !ok {
					c.String(http.StatusOK, "没有权限")
					c.Abort() // 终止程序
				}
			}
		}
	} else {
		//4、如果Session不存在,判断当前访问的URl是否是login doLogin captcha,如果不是跳转到登录页面,如果是不行任何操作
		//说明用户没有登录
		//需要排除到不需要做权限判断的路由
		if pathname != "/admin/login" && pathname != "/admin/dologin" && pathname != "/admin/captcha" {
			//跳转到登录页面
			c.Redirect(http.StatusFound, "/admin/login")
		}
	}
}

//排除权限判断的方法
func excludeAuthPath(urlPath string) bool {
	//加载配置文件
	cfg, err := ini.Load("./conf/app.ini")
	if err != nil {
		fmt.Printf("Fail to read file: %v", err)
		os.Exit(1)
	}
	//获取需要排除的地址
	excludeAuthPath := cfg.Section("").Key("excludeAuthPath").String()
	//拆分字符串成为一个切片
	excludeAuthPathSlice := strings.Split(excludeAuthPath, ",")
	//判断传入的地址是否在排除地址内
	for _, v := range excludeAuthPathSlice {
		if v == urlPath {
			return true
		}
	}
	return false
}

从上面代码中,可以看出:当用户登录成功后,才需要判断要访问的url对应的权限id是否在对应角色的权限中,故构建微服务方法时,需要传入要访问的urlPath,以及角色id,这里把该微服务方法放到rbacRole这个微服务中,所以,在server端的proto/rbacRole.proto下的service RbacRole下增加rpc方法,如下:

//权限判断:获取当前访问的URL对应的权限id,判断权限id是否在角色对应的权限中
rpc  MiddlewaresAuth(MiddlewaresAuthRequest) returns (MiddlewaresAuthResponse) {}

然后实现上面rpc方法:

//权限判断:角色是否有访问urlPath的权限,请求参数
message MiddlewaresAuthRequest{
	int64 roleId=1;
	string urlPath=2;
}
//权限判断:响应参数 是否具有该访问权限
message MiddlewaresAuthResponse{
	bool hasPermission=1;
}

完整代码如下: 

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

    //权限判断:获取当前访问的URL对应的权限id,判断权限id是否在角色对应的权限中
    rpc  MiddlewaresAuth(MiddlewaresAuthRequest) returns (MiddlewaresAuthResponse) {}
}

//角色相关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;
}

//权限判断:角色是否有访问urlPath的权限,请求参数
message MiddlewaresAuthRequest{
	int64 roleId=1;
	string urlPath=2;
}
//权限判断:响应参数 是否具有该访问权限
message MiddlewaresAuthResponse{
	bool hasPermission=1;
}

 (2).更新proto文件夹下对应rbacRole的方法

运行命令protoc --proto_path=. --micro_out=. --go_out=:. proto/rbacRole.proto更新proto下的文件

 (3).handler/rbacRole.go下实现上面的rpc MiddlewaresAuth方法

把中间件middlewares/adminAuth.go中判断用户权限相关代码抽离出来,做成一个MiddlewaresAuth()微服务方法,代码如下:


//权限判断
func (e *RbacRole) MiddlewaresAuth(ctx context.Context, req *pb.MiddlewaresAuthRequest, res *pb.MiddlewaresAuthResponse) error {
	//判断用户权限:当前用户权限是否可以访问url地址
	// 1、根据角色获取当前角色的权限列表,然后把权限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
	}
	// 2、获取当前访问的url对应的权限id 判断权限id是否在角色对应的权限
	// pathname      /admin/manager
	access := models.Access{}
	models.DB.Where("url = ?", req.UrlPath).Find(&access)
	//3、判断当前访问的url对应的权限id 是否在权限列表的id中
	if _, ok := roleAccessMap[int(access.Id)]; !ok {
		res.HasPermission = false
	} else {
		res.HasPermission = true
	}
	return nil
}

2.项目端中间件adminAuth.go调用上面微服务方法,进行权限判断

(1).复制server端proto下rbacRole.go以及rbacRole文件夹到项目的proto下

因为修改了server中的rbacRole.proto,需要同步

(2).在项目的middlewares/adminAuth.go中间件中调用权限判断微服务方法

package middlewares

//中间件: 作用: 在执行路由之前或者之后进行相关逻辑判断

import (
	"encoding/json"
	"fmt"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"gopkg.in/ini.v1"
	"goshop/models"
	pbRbac "goshop/proto/rbacRole"
	"net/http"
	"os"
	"context"
	"strings"
)

func InitAdminAuthMiddleware(c *gin.Context) {
	//权限判断: 没有登录的用户不能进入后台管理中心

	//1、获取Url访问的地址
	//当地址后面带参数时:,如: admin/captcha?t=0.8706946438889653,需要处理
	//strings.Split(c.Request.URL.String(), "?"): 把c.Request.URL.String()请求地址按照?分割成切片
	pathname := strings.Split(c.Request.URL.String(), "?")[0]

	//2、获取Session里面保存的用户信息
	session := sessions.Default(c)
	userinfo := session.Get("userinfo_admin")
	//3、判断Session中的用户信息是否存在,如果不存在跳转到登录页面(注意需要判断) 如果存在继续向下执行
	//session.Get获取返回的结果是一个空接口类型,所以需要进行类型断言: 判断userinfo是不是一个string
	userinfoStr, ok := userinfo.(string)
	if ok { // 说明是一个string
		var userinfoStruct []models.Manager
		//把获取到的用户信息转换结构体
		err := json.Unmarshal([]byte(userinfoStr), &userinfoStruct)
		if err != nil || !(len(userinfoStruct) > 0 && userinfoStruct[0].Username != "") {
			if pathname != "/admin/login" && pathname != "/admin/dologin" && pathname != "/admin/captcha" {
				//跳转到登录页面
				c.Redirect(http.StatusFound, "/admin/login")
			}
		} else { //表示用户登录成功
			//获取当前访问的URL对应的权限id,判断权限id是否在角色对应的权限中
			// strings.Replace 字符串替换
			urlPath := strings.Replace(pathname, "/admin/", "", 1)
			//排除权限判断:不	是超级管理员并且不在相关权限内
			if userinfoStruct[0].IsSuper == 0 && !excludeAuthPath("/" + urlPath){
				//判断用户权限:当前用户权限是否可以访问url地址
				//调用微服务进行权限判断
				rbacClient := pbRbac.NewRbacRoleService("rbac", models.RbacClient)
				res, _ := rbacClient.MiddlewaresAuth(context.Background(), &pbRbac.MiddlewaresAuthRequest{
					RoleId:  int64(userinfoStruct[0].RoleId),
					UrlPath: urlPath,
				})
				//3、判断当前访问的url对应的权限id 是否在权限列表的id中
				if !res.HasPermission {
					c.String(200, "没有权限")
					c.Abort()
				}

				//获取当前角色拥有的权限,并把权限id放在一个map对象中
				//roleAccess := []models.RoleAccess{}
				//models.DB.Where("role_id = ?", userinfoStruct[0].RoleId).Find(&roleAccess)
				//roleAccessMap := make(map[int]int)
				//for _, v := range roleAccess {
				//	roleAccessMap[v.AccessId] = v.AccessId
				//}
				实例化access
				//access := models.Access{}
				查询权限id
				//models.DB.Where("url = ? ", urlPath).Find(&access)
				判断权限id是否在角色对应的权限中
				//if _, ok := roleAccessMap[access.Id]; !ok {
				//	c.String(http.StatusOK, "没有权限")
				//	c.Abort() // 终止程序
				//}
			}
		}
	} else {
		//4、如果Session不存在,判断当前访问的URl是否是login doLogin captcha,如果不是跳转到登录页面,如果是不行任何操作
		//说明用户没有登录
		//需要排除到不需要做权限判断的路由
		if pathname != "/admin/login" && pathname != "/admin/dologin" && pathname != "/admin/captcha" {
			//跳转到登录页面
			c.Redirect(http.StatusFound, "/admin/login")
		}
	}
}

//排除权限判断的方法
func excludeAuthPath(urlPath string) bool {
	//加载配置文件
	cfg, err := ini.Load("./conf/app.ini")
	if err != nil {
		fmt.Printf("Fail to read file: %v", err)
		os.Exit(1)
	}
	//获取需要排除的地址
	excludeAuthPath := cfg.Section("").Key("excludeAuthPath").String()
	//拆分字符串成为一个切片
	excludeAuthPathSlice := strings.Split(excludeAuthPath, ",")
	//判断传入的地址是否在排除地址内
	for _, v := range excludeAuthPathSlice {
		if v == urlPath {
			return true
		}
	}
	return false
}

 (3).验证上面的微服务方法是否正确

启动consul以及各个微服务,以及项目,登录后台,进行权限访问,发现该微服务方法没问题

三. Rbac微服务数据库抽离 

1.抽离rbac相关数据表

目前项目使用的数据库是同一个数据库:ginshop,rbac微服务的数据库配置见server下rbac/conf/app.ini配置文件,相关配置如下:

app_name   = rbac
# possible values: DEBUG, INFO, WARNING, ERROR, FATAL

[mysql]
ip       = 127.0.0.1
port     = 3306
user     = root
password = 123456
database = ginshop

[consul]
addr   = localhost:8082

所以,这里举例说明抽离数据库:把rbac微服务相关数据表抽离出来,独立一个数据库,创建一个新的数据库ginshoprbac,然后把rbac相关的数据表(access,manager,role,role_access)抽离到ginshoprbac数据库中,当然,在抽离数据表之前,一定要记得备份 

 2.修改app.ini配置

抽离了rbac相关数据库,那么就要数据配置app.ini,修改后的配置如下:

app_name   = rbac
# possible values: DEBUG, INFO, WARNING, ERROR, FATAL

[mysql]
ip       = 127.0.0.1
port     = 3306
user     = root
password = 123456
database = ginshoprbac

[consul]
addr   = localhost:8082

 3.删除客户端rbac相关model

因为使用的是rbac微服务了,所以原来项目客户端中的model就没用了,删除无用的model,然后测试rbac微服务功能是否正确,这里删除client下model中的access.go,role.go,roleAccess.go,修改manager.go(manager.go模型还在使用),修改后的manager.go代码如下:

package models

//管理员表

type Manager struct { // 结构体首字母大写, 和数据库表名对应, 默认访问数据表users, 可以设置访问数据表的方法
	Id  int
	Username string
	Password string
	Mobile string
	Email string
	Status int
	RoleId int
	AddTime int
	IsSuper int
}

 修改完后,重启后台客户端,以及rbac微服务端,测试,发现没有问题,说明抽离rbac微服务数据表没有问题        

好了,到这里,项目微服务相关实战内容就到这里了,查看更多文章,请看主页

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/82549.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

攻防世界-simple_php

原题 解题思路 flag被分成了两个部分&#xff1a;flag2&#xff0c;flag2。获得flag1需要满足变量a0且变量a≠0&#xff0c;这看起来不能实现&#xff0c;但实际上当变量a的值是字符时&#xff0c;与数字比较会发生强制类型转换&#xff0c;所以a为字符型数据即可&#xff0c;变…

掌控未知:项目中如何巧妙应对突发与紧急

引言 在项目管理的领域中&#xff0c;每一个项目都伴随着一系列的不确定性和挑战。这些不确定性可能源于外部环境的变化、团队内部的动态或技术的快速迭代。而在这些不确定性中&#xff0c;突发和紧急事件尤为考验项目经理的应变能力和决策智慧。那么&#xff0c;如何在项目中…

数据结构<树和二叉树>顺序表存储二叉树实现堆排

✨Blog&#xff1a;&#x1f970;不会敲代码的小张:)&#x1f970; &#x1f251;推荐专栏&#xff1a;C语言&#x1f92a;、Cpp&#x1f636;‍&#x1f32b;️、数据结构初阶&#x1f480; &#x1f4bd;座右铭&#xff1a;“記住&#xff0c;每一天都是一個新的開始&#x1…

Verilog中的 条件语句\多路分支语句\循环语句

Verilog中的条件语句\多分支语句\循环语句 文章目录 Verilog中的条件语句\多分支语句\循环语句一、背景二、if-else2.1 标准结构2.2 例子 三、case-endcase3.1 标准结构3.2 例子3.2.1 三路选择器的case部分&#xff0c;如下&#xff1a;3.2.2 casez的四路选择器&#xff0c;如下…

论文学习——PixelSNAIL:An Improved Autoregressive Geenrative Model

文章目录 引言论文翻译Abstract问题 Introduction第一部分问题 第二部分问题 Model Architecture网络结构第一部分问题第二部分问题 Experiments实验问题 Conclusion结论问题 总结参考 引言 这篇文章&#xff0c;是《PixelSNAIL:An Improved Autoregressive Geenrative Model》…

电脑上安装,多版本node

手上有一个vue3的项目&#xff0c;sass配置如下图所示&#xff1a; 安装了Python3.10和node 16.14.0&#xff0c;项目能正常install 跟run。 因工作需要&#xff0c;收上有一个vue2的项目&#xff0c;sass配置如下图所示&#xff1a; 执行npm intsall 的时候一直报Python2找不…

Influxdb数据库(centos7)

Influxdb数据库 1、简介与使用场景 简介 InfluxDB是一个由InfluxData开发的开源时序型数据库&#xff0c;专注于海量时序数据的高性能读、高性能写、高效存储与实时分析等&#xff0c;在DB-Engines Ranking时序型数据库排行榜上排名第一&#xff1a; InfluxDB广泛应用于DevOps…

ElasticSearch索引库、文档、RestClient操作

文章目录 一、索引库1、mapping属性2、索引库的crud 二、文档的crud三、RestClient 一、索引库 es中的索引是指相同类型的文档集合&#xff0c;即mysql中表的概念 映射&#xff1a;索引中文档字段的约束&#xff0c;比如名称、类型 1、mapping属性 mapping映射是对索引库中文…

MyBatis入门配置及CURD实现

目录 一、MyBatis简介 1. 什么是 MyBatis ? 2. MyBatis的特性 3. 什么是持久层框架&#xff1f; 二、MyBatis环境配置 2.1 创建maven工程 2.2 导入相关pom依赖 2.3 导入jdbc配置文件 2.4 Mybatis相关插件安装 3.5 Mybatis-cfg.xml 核心配置 2.6 引入Log4j2日志文件…

在项目中如何解除idea和Git的绑定

在项目中如何解除idea和Git的绑定 1、点击File--->Settings...(CtrlAltS)--->Version Control--->Directory Mappings--->点击取消Git的注册根路径&#xff1a; 2、回到idea界面就没有Git了&#xff1a; 3、给这个项目初始化 这样就可以重新绑定远程仓库了&#x…

前端vue自定义柱形图 选中更改柱形图颜色及文字标注颜色

随着技术的发展&#xff0c;开发的复杂度也越来越高&#xff0c;传统开发方式将一个系统做成了整块应用&#xff0c;经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改&#xff0c;造成牵一发而动全身。 通过组件化开发&#xff0c;可以有效实现…

船舶法兰盘法兰管件3D扫描尺寸测量|三维扫描检测|CAV测量-CASAIM

第一章 服务背景 船舶建造多采用分段建造法&#xff0c;即将零件、预装好的部件在胎架上组合焊接成分段或总段&#xff0c;然后由船台装配成整船的建造方法。而当船体合拢组装时&#xff0c;在船体上遍布着各种各样的管道&#xff0c;这些管道都需要互相完全适配以确保船体安装…

第8章:集成学习

个体与集成 同质&#xff1a;相同的基学习器&#xff0c;实现容易&#xff0c;但是很难保证差异性。异质&#xff1a;不同的基学习器&#xff0c;实现复杂&#xff0c;不同模型之间本来就存在差异性&#xff0c;但是很难直接比较不同模型的输出&#xff0c;需要复杂的配准方法。…

python基础5——正则、数据库操作

文章目录 一、数据库编程1.1 connect()函数1.2 命令参数1.3 常用语句 二、正则表达式2.1 匹配方式2.2 字符匹配2.3 数量匹配2.4 边界匹配2.5 分组匹配2.6 贪婪模式&非贪婪模式2.7 标志位 一、数据库编程 可以使用python脚本对数据库进行操作&#xff0c;比如获取数据库数据…

前后端分离------后端创建笔记(09)密码加密网络安全

本文章转载于【SpringBootVue】全网最简单但实用的前后端分离项目实战笔记 - 前端_大菜007的博客-CSDN博客 仅用于学习和讨论&#xff0c;如有侵权请联系 源码&#xff1a;https://gitee.com/green_vegetables/x-admin-project.git 素材&#xff1a;https://pan.baidu.com/s/…

Pixar、Adobe 和苹果等成立 OpenUSD 联盟推行 3D 内容开放标准

导读Pixar、Adobe、Apple、Autodesk 与 NVIDIA 联手 Linux 基金会旗下的联合开发基金会&#xff08;JDF&#xff09;宣布建立 OpenUSD 联盟&#xff08;AOUSD&#xff09;以推行 Pixar 创建的通用场景描述技术的标准化、开发、进化和发展。 联盟寻求通过推进开放式通用场景描述…

Crimson:高性能,高扩展的新一代 Ceph OSD

背景 随着物理硬件的不断发展&#xff0c;存储软件所使用的硬件的情况也一直在不断变化。 一方面&#xff0c;内存和 IO 技术一直在快速发展&#xff0c;硬件的性能在极速增加。在最初设计 Ceph 的时候&#xff0c;通常情况下&#xff0c;Ceph 都是被部署到机械硬盘上&#x…

Git问题:解决“ssh:connect to host github.com port 22: Connection timed out”

操作系统 Windows11 使用Git IDEA 连接方式&#xff1a;SSH 今天上传代码出现如下报错&#xff1a;ssh:connect to host github.com port 22: Connection timed out 再多尝试几次&#xff0c;依然是这样。 解决 最终发现两个解决方案&#xff1a;&#xff08;二选一&#xf…

Android 9.0 Vold挂载流程解析(上)

前言 我们分2篇文章来介绍Android 9.0中存储卡的挂载流程&#xff0c;本篇文章先介绍总体的挂载模块、Vold进程的入口main函数的详细分析&#xff0c;有了这些基础知识&#xff0c;下一篇中我们再详细介绍收到驱动层消息是怎么挂载和卸载存储卡的&#xff0c;还有framework层如…

合宙Air724UG LuatOS-Air LVGL API--对象

对象 概念 在 LVGL 中&#xff0c;用户界面的基本构建块是对象。例如&#xff0c;按钮&#xff0c;标签&#xff0c;图像&#xff0c;列表&#xff0c;图表或文本区域。 属性 基本属性 所有对象类型都共享一些基本属性&#xff1a; Position (位置) Size (尺寸) Parent (父母…