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

[golang gin框架] 46.Gin商城项目-微服务实战之后台Rbac客户端调用微服务权限验证以及Rbac微服务数据库抽离
一. 根据用户的权限动态显示左侧菜单微服务 1.引入 后台Rbac客户端调用微服务权限验证功能主要是: 登录后显示用户名称、根据用户的权限动态显示左侧菜单,判断当前登录用户的权限 、没有权限访问则拒绝,参考[golang gin框架] 14.Gin 商城项目-RBAC管理,该微服务功能和上一节[g…...
域名和ip的关系
域名和ip的关系 一:什么是域名 域名,简称域名、网域,是由一串用点分隔的名字组成的上某一台计算机或计算机组的名称,用于在数据传输时标识 计算机的电子方位(有时也指地理位置)。网域名称系统,有时也简称为域名…...

excel日期函数篇1
1、DAY(serial_number):返回序列数表示的某月的天数 在括号内给出一个时间对象或引用一个时间对象(年月日),返回多少日 下面结果都为20 2、MONTH(serial_number):返回序列数表示的某年的月份 在括号内给出一个时间对…...

Leetcode151 翻转字符串中的单词
给你一个字符串 s ,请你反转字符串中 单词 的顺序。 单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。 返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。 注意:输入字符串 s中可能会存在前导空格、尾随空格…...
PHP FTP的相关函数及简单使用示例
简介 FTP是ARPANet的标准文件传输协议,该网络就是现今Internet的前身。 PHP FTP函数是通过文件传输协议提供对文件服务器的客户端访问,FTP函数用于打开、登陆以及关闭连接,也用于上传、下载、重命名、删除以及获取服务器上文件信息。 安装 …...

高光谱 | 矿物识别和分类标签数据制作、农作物病虫害数据分类、土壤有机质含量回归与制图、木材含水量评估和制图
本课程提供一套基于Python编程工具的高光谱数据处理方法和应用案例。 本课程涵盖高光谱遥感的基础、方法和实践。基础篇以学员为中心,用通俗易懂的语言解释高光谱的基本概念和理论,旨在帮助学员深入理解科学原理。方法篇结合Python编程工具,…...

【数据结构】二叉树篇| 纲领思路01+刷题
博主简介:努力学习的22级计算机科学与技术本科生一枚🌸博主主页: 是瑶瑶子啦每日一言🌼: 所谓自由,不是随心所欲,而是自我主宰。——康德 目录 一、二叉树刷题纲领二、刷题1、104. 二叉树的最大深度2、 二叉…...
系统架构设计师---计算机基础知识之数据库系统结构与规范化
目录 一、基本概念 二、 数据库的结构 三、常用的数据模型 概念数据模型...

PyCharm连接Docker中的容器(ubuntu)
一、为什么要用Pycharm链接Docker中的ubuntu 因为在进行深度学习的时候,基于windows系统在开发的过程中,老是出现很多问题,大多数是环境问题。 尽管安装了Conda,也不能很好的解决问题,使用ubuntu是最好的选择。 二、…...

安防视频汇聚平台EasyCVR视频监控综合管理平台H.265转码功能更新,新增分辨率配置的具体步骤
安防视频集中存储EasyCVR视频监控综合管理平台可以根据不同的场景需求,让平台在内网、专网、VPN、广域网、互联网等各种环境下进行音视频的采集、接入与多端分发。在视频能力上,视频云存储平台EasyCVR可实现视频实时直播、云端录像、视频云存储、视频存储…...

全平台数据(数据库)管理工具 DataCap 管理 Rainbond 上的所有数据库
DataCap是用于数据转换、集成和可视化的集成软件,支持多种数据源、文件类型、大数据相关数据库、关系数据库、NoSQL数据库等。通过该 DataCap 可以实现对多个数据源的管理,对数据源下的数据进行各种操作转换,制作数据图表,监控数据…...
“深入探究JVM内部机制:从字节码到实际执行“
标题:深入探究JVM内部机制:从字节码到实际执行 摘要:本文将深入探究Java虚拟机(JVM)的内部机制,从字节码的生成、类加载、字节码解释和即时编译等环节,详细介绍JVM是如何将Java程序的字节码转化…...

C++写文件,直接写入结构体
C写文件,直接写入结构体 以前写文件都是写入字符串或者二进制再或者就是一些配置文件,今天介绍一下直接写入结构体,可以在软件参数较多的时候直接进行读写,直接将整个结构体写入和读取,看代码: #include&…...

【Spring专题】Spring之Bean的生命周期源码解析——阶段二(二)(IOC之属性填充/依赖注入)
目录 前言阅读准备阅读指引阅读建议 课程内容一、依赖注入方式(前置知识)1.1 手动注入1.2 自动注入1.2.1 XML的autowire自动注入1.2.1.1 byType:按照类型进行注入1.2.1.2 byName:按照名称进行注入1.2.1.3 constructor:…...

线程|线程的使用、四种实现方式
1.线程的实现方式 1.用户级线程 开销小,用户空间就可以创建多个。缺点是:内核无法感知用户级多个线程的存在,把其当作只有一个线程,所以只会提供一个处理器。 2.内核级线程 相对于用户级开销稍微大一点,可以利用多…...

Facebook 应用未启用:这款应用目前无法使用,应用开发者已得知这个问题。
错误:Facebook 应用未启用:这款应用目前无法使用,应用开发者已得知这个问题。应用重新启用后,你便能登录。 「应用未经过审核或未发布」: 如果一个应用还没有经过Facebook的审核或者开发者尚未将应用发布,那么它将无法…...

(十八)大数据实战——Hive的metastore元数据服务安装
前言 Hive的metastore服务作用是为Hive CLI或者Hiveserver2提供元数据访问接口。Hive的metastore 是Hive元数据的存储和管理组件,它负责管理 Hive 表、分区、列等元数据信息。元数据是描述数据的数据,它包含了关于表结构、存储位置、数据类型等信息。本…...
ubuntu 22.04 LTS 在 llvm release/17.x 分支上编译 cookbook llvm example Chapter 02
一,从源码编译 llvm 下载源码: $ git clone https://github.com/llvm/llvm-project.git 创建 对应 commit id分支: $ cd llvm-project $ git checkout 5b78868661f42a70fa30 -b 17.x.greater 源码成功编译 llvm-project commit id&…...

【仿写tomcat】三、通过socket读取http请求信息
仿写tomcat 建立Socket连接获取连接信息查看HTTP信息 建立Socket连接 这里我们也是创建一个专门管理socket的类 package com.tomcatServer.socket;import java.io.*; import java.net.ServerSocket;/*** 套接字存储** author ez4sterben* date 2023/08/15*/ public class Soc…...

Hive的窗口函数与行列转换函数及JSON解析函数
1. 系统内置函数 查看系统内置函数:show functions ; 显示内置函数的用法: desc function lag; – lag为函数名 显示详细的内置函数用法: desc function extended lag; 1.1 行转列 行转列是指多行数据转换为一个列的字段。 Hive行转列用到的函数 con…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
如何配置一个sql server使得其它用户可以通过excel odbc获取数据
要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据,你需要完成以下配置步骤: ✅ 一、在 SQL Server 端配置(服务器设置) 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到:SQL Server 网络配…...
6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙
Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...