[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是不是一个stringuserinfoStr, 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.AccessModelfor _, v := range accessList {var tempItemList []*pb.AccessModelfor _, 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 = tempListreturn 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是不是一个stringuserinfoStr, 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是不是一个stringuserinfoStr, ok := userinfo.(string)if ok { // 说明是一个stringvar 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}//实例化accessaccess := models.Access{}//查询权限idmodels.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, 响应参数RoleGetResponserpc RoleGet(RoleGetRequest) returns (RoleGetResponse) {}//增加角色rpc方法: 请求参数RoleAddRequest, 响应参数RoleAddResponserpc RoleAdd(RoleAddRequest) returns (RoleAddResponse) {}//编辑角色rpc方法: 请求参数RoleEditRequest, 响应参数RoleEditResponserpc RoleEdit(RoleEditRequest) returns (RoleEditResponse) {}//删除角色rpc方法: 请求参数RoleDeleteRequest, 响应参数RoleDeleteResponserpc 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{//角色idint64 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{//角色idint64 id=1;//角色名称string title=2;//说明string description=3;//状态int64 status=4;//增加时间int64 addTime =5;
}//编辑角色响应参数
message RoleEditResponse{	//是否编辑成功bool success=1;//返回状态说明string message=2;
}//删除角色请求参数
message RoleDeleteRequest{//角色idint64 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/manageraccess := 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是不是一个stringuserinfoStr, ok := userinfo.(string)if ok { // 说明是一个stringvar 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  intUsername stringPassword stringMobile stringEmail stringStatus intRoleId intAddTime intIsSuper int
}

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.Net Core 动态加载和卸载程序集

从 .Net Core 3.0开始支持程序集的加载和卸载&#xff0c;在 .Net FrameWork中使用独立的应用程序域来实现同样的功能&#xff0c;.Net Core 不支持创建多个应用程序域&#xff0c;所以无法使用多个应用程序域来实现程序集动态加载和卸载。 AssemblyLoadContext 程序集加载上下…

lvs-DR模式:

lvs-DR数据包流向分析 客户端发送请求到 Director Server&#xff08;负载均衡器&#xff09;&#xff0c;请求的数据报文&#xff08;源 IP 是 CIP,目标 IP 是 VIP&#xff09;到达内核空间。 Director Server 和 Real Server 在同一个网络中&#xff0c;数据通过二层数据链路…

猿辅导Motiff亮相IXDC 2023国际体验设计大会,发布新功能获行业高度关注

近日&#xff0c;“IXDC 2023国际体验设计大会”在北京国家会议中心拉开序幕&#xff0c;3000设计师、1000企业、200全球商业领袖&#xff0c;共襄为期5天的用户体验创新盛会。据了解&#xff0c;此次大会是以“设计领导力”为主题&#xff0c;分享全球设计、科技、商业的前沿趋…

iptables

1、iptables iptables分为四表五链&#xff1a; 四表&#xff1a;mangle raw nat filter 五链&#xff1a;input output forward prerouting postrouting 2、查看 查看iptables规则 iptables -vnL -v 查看时显示更详细信息 -n 所有字段以数字形式显示 -L 查看规则列表 …

dockers搭建基本服务

1、使用mysql:5.6和 owncloud 镜像&#xff0c;构建一个个人网盘。 拉取mysql-5.6和owncloud的镜像 docker run -d --name mdb --env MYSQL_ROOT_PASSWORD123 cytopia/mysql-5.6 docker run -d -p 90:80 --name webdcloud --link mdb:mdb owncloud 注册的时候&#xff0c;数据…

攻防世界-fileclude

原题 解题思路 直接展示源码了&#xff0c;flag.php应该存放了flag&#xff0c;在file1与file2都不为空且file2是“hello ctf”时file1将被导入。接下来做法很明显&#xff0c;让file为flag.php&#xff0c;file2为“hello ctf”。“?file1php://filter/readconvert.base64-en…

docker搭建es+kibana

docker搭建eskibana 0 安装docker 如果是mac或者windows&#xff0c;可以直接安装Docker Desktop更加便捷。 前提条件&#xff1a; Docker可以运行在Windows、Mac、CentOS、Ubuntu等操作系统上 Docker支持以下的CentOS版本&#xff1a; CentOS 7 (64-bit)CentOS 6.5 (64-bit…

Qt安卓开发经验技巧总结V202308

01&#xff1a;01-05 pro中引入安卓拓展模块 QT androidextras 。pro中指定安卓打包目录 ANDROID_PACKAGE_SOURCE_DIR $$PWD/android 指定引入安卓特定目录比如程序图标、变量、颜色、java代码文件、jar库文件等。 AndroidManifest.xml 每个程序唯一的一个全局配置文件&…

SLAM-VIO视觉惯性里程计

SLAM 文章目录 SLAM前言IMU与视觉比较单目视觉缺陷&#xff1a;融合IMU优势&#xff1a;相机-IMU标定松耦合紧耦合基于滤波的融合方案&#xff1a;基于优化的融合方案&#xff1a; 前言 VIO&#xff08;visual-inertial odometry&#xff09;即视觉惯性里程计&#xff0c;有时…

记录首次面试2023-08-18

人生第一次面试&#xff0c;大概一个小时左右。没有问我C的&#xff0c;上来一个数据库事务&#xff0c;虽然没有复习&#xff0c;但是还是能够记住一些&#xff0c;主要问的一些事务的隔离级别&#xff0c;以及都有什么作用&#xff0c;我是举例回答的&#xff0c;客户端A和客…

【RabbitMQ】消息队列-RabbitMQ篇章

文章目录 1、RabbitMQ是什么1.1、RabbitMQ---使用场景一般场景解耦削峰异步 2、Dokcer安装RabbitMQ2.1安装Dokcer2.2安装rabbitmq 3、RabbitMQ入门案例 - Simple 简单模式4、RabbitMQ的核心组成部分4.1 RabbitMQ整体架构4.2RabbitMQ的运行流程 5、RabbitMQ的模式5.1 发布订阅模…

最新ChatGPT网站程序源码+AI系统+详细图文搭建教程/支持GPT4.0/AI绘画/H5端/Prompt知识库

一、前言 SparkAi系统是基于国外很火的ChatGPT进行开发的Ai智能问答系统。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。 那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧&#xff01…

Linux(入门篇)

Linux&#xff08;入门篇&#xff09; Linux概述Linux是什么Linux的诞生Linux和Unix的渊源GNU/LinuxLinux的发行版Linux VS Windows Linux概述 Linux是什么 Linux是一个操作系统(OS) Linux的诞生 作者&#xff1a;李纳斯托瓦兹&#xff08;git也是他开发的&#x1f602;&am…

微人事 部门管理 模块 (十五)

部门管理的树展示和搜索 数据展示页是个树&#xff0c;我们一次性把数据加载出来也可以通过点一次id加载查询出来出来子部门&#xff0c;我们用一次拿到说有json数据加载出来 数据不多可以用递归&#xff0c;数据很多就用懒加载的方式 由于子部门比较深就不适合&#xff0c;权…

无涯教程-Perl - sub函数

描述 此函数定义一个新的子例程。上面显示的参数遵循以下规则- NAME是子例程的名称。可以在有或没有原型规范的情况下预先声明命名的子例程(没有关联的代码块)。 匿名子例程必须具有定义。 PROTO定义了函数的原型,调用该函数以验证提供的参数时将使用该原型。 ATTRS为解析…

关于查看处理端口号和进程[linux]

查看端口号 lsof -i:端口号如果-bash: lsof: 未找到命令那我们可以执行yum install lsof 删除端口号进程 一般我们都会使用kill命令 kill -l#列出所有可用信号1 (HUP)&#xff1a;重新加载进程。9 (KILL)&#xff1a;杀死一个进程。15 (TERM)&#xff1a;正常停止一个进程。 …

Java后端面试题——Mysql篇

在Mysql中&#xff0c;如何定位慢查询呢&#xff1f; 表象&#xff1a;页面加载过慢、接口压测响应时间过长&#xff08;超过1s&#xff09; 原因&#xff1a;聚合查询 多表查询 表数据量过大查询 深度分页查询 方案&#xff1a;MySQL自带慢日志 需要在MySQL的配置文件&…

【汇编语言】栈及栈操作的实现

文章目录 栈结构栈操作栈的小结 栈结构 栈是一种只能在一端插入或删除的数据结构&#xff1b;栈有两个基本的操作&#xff1a;入栈和出栈&#xff1b; 入栈&#xff1a;将一个新的元素放到栈顶&#xff1b;出栈&#xff1a;从栈顶取出一个元素&#xff1b; 栈的操作规则&#…

Echarts升级7:环状图+标题设置+legend属性

效果 注意的点&#xff1a; title样式颜色设置legend颜色设置legend textStyle 样式设置&#xff1b;formatter样式设置&#xff0c;文字拆分 代码&#xff1a; <template><div style"width: 100%; height: 100%;"><div id"siteStatusStatis…

FL Studio21.1中文完整版Win/Mac

FL Studio All Plugins Edition【中文完整版 Win/Mac】适合音乐制作人/工作室使用&#xff0c;全套插件!&#xff08;20.9新增Vintage Chorus&#xff0c;Pitch Shifter变调插件&#xff09;FL Studio是超多顶级音乐人的启蒙首选&#xff01;包括百大DJ冠军Martin Garrix&…