上一节抽离了captcha验证码功能,集成了验证码微服务功能,这一节来看看后台Rbac功能,并抽离其中的用户登录,管理员管理,角色管理,权限管理等功能作为微服务来调用
一.引入
后台操作从登录到后台首页,然后其中的管理员管理,角色管理,权限管理等功能可以抽离出来作为 一个Rbac微服务,因为他们都是共同关系的,并且使用人员也不是太多,当然,也可以每个管理功能单独作为一个微服务来进行操作
二.创建权限管理Rbac微服务服务端以及实现后台用户登录微服务
1.生成权限管理Rbac微服务代码
在server目录下运行: go-micro new service rbac,生成 rbac微服务服务端代码
2.编写proto/rbac.proto文件,实现用户登录微服务逻辑
在这个文件中编写 用户登录相关代码,可参考: [golang gin框架] 13.Gin 商城项目-配置公共基类实现公共的成功,失败提示页面 用户登录、退出登录、以及权限判断
参考代码 controllers/admin/loginController.go如下:
//执行登录操作
func (con LoginController) DoIndex(c *gin.Context) {
//获取表单中的数据
captchaId := c.PostForm("captchaId") // 验证码id
verifyValue := c.PostForm("verifyValue") //验证码的值
//获取用户名以及密码
username := c.PostForm("username")
password := c.PostForm("password")
// 1.判断验证码是否验证成功: 这里调用的是上一节验证码微服务的功能
if flag := models.VerifyCaptcha(captchaId, verifyValue); flag {
//2.查询数据库,判断用户以及密码是否正确
userinfo := []models.Manager{}
password = models.Md5(password)
models.DB.Where("username = ? and password = ? ", username, password).Find(&userinfo)
if len(userinfo) > 0 {
//3.执行登录,保存用户信息,执行跳转操作
session := sessions.Default(c)
//注意: session.Set没法保存结构体对应的切片,所以需要把结构体转换成json字符串
userinfoSlice, _ := json.Marshal(userinfo)
session.Set("userinfo_admin", string(userinfoSlice))
session.Save()
con.Success(c, "登录成功", "/admin")
} else {
con.Error(c, "用户名或密码错误", "/admin/login")
}
} else {
con.Error(c, "验证码验证失败", "/admin/login")
}
}
从上面可以看出:在权限管理Rbac微服务中要实现:
//2.查询数据库,判断用户以及密码是否正确
userinfo := []models.Manager{}
password = models.Md5(password)
models.DB.Where("username = ? and password = ? ", username, password).Find(&userinfo)
而实现上面的逻辑,则需要连接数据库,创建数据模型,这里使用GORM方式
(1).创建数据模型
在models下创建manager.go,可参考[golang gin框架] 13.Gin 商城项目-配置公共基类实现公共的成功,失败提示页面 用户登录、退出登录、以及权限判断
package models
//管理员表
type Manager struct { // 结构体首字母大写, 和数据库表名对应, 默认访问数据表users, 可以设置访问数据表的方法
Id int
Username string
Password string
Mobile string
Email string
Status int
RoleId int
AddTime int
IsSuper int
}
//配置数据库操作的表名称
func (Manager) TableName() string {
return "manager"
}
(2).创建conf/app.ini文件
在微服务项目Rbac下创建conf/app.ini配置文件,一些配置,如:数据库配置,redis配置,consul配置信息都可以放到该文件下,以便使用,使用了这个文件下的配置,改变里面的数据不用重启项目,具体参考:[golang gin框架] 9.Gin GORM 中使用事务以及go-ini加载.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]
#指定微服务的ip,一般一个微服务对应一台服务器,当在同一台服务器上有多个微服务时,以不同端口区分
addr = localhost:8082
(3).定义initInt.go文件
在models下创建initInt.go文件,自动初始化app.ini,很多地方调用app.ini时,就可以从这里介入,该文件目的是减少代码的复用
package models
//自动初始化app.ini:这里很多地方都要用到
import (
"fmt"
"os"
"gopkg.in/ini.v1"
)
var Config *ini.File
var iniErr error
func init() {
Config, iniErr = ini.Load("./conf/app.ini")
if iniErr != nil {
fmt.Printf("Fail to read file: %v", iniErr)
os.Exit(1)
}
}
(4).创建initMysql.go
在models下创建initMysql.go文件,该文件目的是创建mysql数据库连接引擎
package models
//https://gorm.io/zh_CN/docs/connecting_to_the_database.html
//微服务项目中: 推荐一个微服务对应一个数据库
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var DB *gorm.DB
var err error
func init() {
//读取.ini里面的数据库配置,相关配置从initIni.go中读取
ip := Config.Section("mysql").Key("ip").String()
port := Config.Section("mysql").Key("port").String()
user := Config.Section("mysql").Key("user").String()
password := Config.Section("mysql").Key("password").String()
database := Config.Section("mysql").Key("database").String()
// dsn := "root:123456@tcp(192.168.0.6:3306)/gin?charset=utf8mb4&parseTime=True&loc=Local"
dsn := fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?charset=utf8mb4&parseTime=True&loc=Local", user, password, ip, port, database)
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
QueryFields: true, //打印sql
//SkipDefaultTransaction: true, //禁用事务
})
// DB.Debug()
if err != nil {
fmt.Println(err)
}
}
(5).编写proto/rbac.proto文件
编写proto/rbac.proto文件,实现微服务相关数据逻辑,可参考:[golang gin框架] 39.Gin商城项目-微服务实战之微服务架构
syntax = "proto3";
package rbac;
option go_package = "./proto/rbac";
service Rbac {
//登录操作
rpc Login(LoginRequest) returns (LoginResponse) {}
}
//用户信息model:参考models/manager.go,一一对应
message ManagerModel{
int64 id=1;
string username=2;
string password=3;
string mobile=4;
string email=5;
int64 status=6;
int64 roleId=7;
int64 addTime=8;
int64 isSuper=9;
}
//登录请求参数
message LoginRequest{
string username=1;
string password=2;
}
//登录返回参数
message LoginResponse{
bool isLogin=1;
repeated ManagerModel userlist=2; //因为在client端需要返回用户相关信息userinfo,故定义一个切片
}
(6).生成proto相关文件
参考[golang 微服务] 7. go-micro框架介绍,go-micro脚手架,go-micro结合consul搭建微服务案例,windows下运行 Makefile里面的proto下的代码 :protoc --proto_path=. --micro_out=. --go_out=:. proto/captcha.proto, 当然,如果是初次使用go-micro,则还需运行init下面的代码(@go xxx),引入相关包
(7).初始化服务端项目
初始化项目,引入项目所需要的包
go mod init rbac
go mod tidy
如果出现:
F:\www\go-data\src\go_code\micro\shop\server\rbac>go mod tidy
go: finding module for package go-micro.dev/v4/client
go: finding module for package gopkg.in/ini.v1
go: finding module for package go-micro.dev/v4
go: finding module for package go-micro.dev/v4/logger
go: finding module for package go-micro.dev/v4/server
go: finding module for package gorm.io/gorm
go: finding module for package gorm.io/driver/mysql
go: finding module for package go-micro.dev/v4/api
go: finding module for package google.golang.org/protobuf/proto
go: finding module for package google.golang.org/protobuf/reflect/protoreflect
go: finding module for package google.golang.org/protobuf/runtime/protoimpl
go: found go-micro.dev/v4 in go-micro.dev/v4 v4.10.2
go: found go-micro.dev/v4/logger in go-micro.dev/v4/logger v1.18.0
go: found gopkg.in/ini.v1 in gopkg.in/ini.v1 v1.67.0
go: found gorm.io/driver/mysql in gorm.io/driver/mysql v1.5.1
go: found gorm.io/gorm in gorm.io/gorm v1.25.2
go: found go-micro.dev/v4/api in go-micro.dev/v4/api v1.18.0
go: found go-micro.dev/v4/client in go-micro.dev/v4/client v1.18.0
go: found go-micro.dev/v4/server in go-micro.dev/v4/server v1.18.0
go: found google.golang.org/protobuf/proto in google.golang.org/protobuf v1.31.0
go: found google.golang.org/protobuf/reflect/protoreflect in google.golang.org/protobuf v1.31.0
go: found google.golang.org/protobuf/runtime/protoimpl in google.golang.org/protobuf v1.31.0
go: rbac/proto/rbac imports
go-micro.dev/v4/api: go-micro.dev/v4/api@v1.18.0: parsing go.mod:
module declares its path as: github.com/micro/go-micro
but was required as: go-micro.dev/v4/api
说明下载"go-micro.dev/v4"这个包失败了,这时则需运行命令 go get go-micro.dev/v4,如图:
然后再次执行go mod tidy就可以了,这样项目中import包就会引入,不会变红
(8).编写handler/rbac.go用户登录微服务逻辑
因为要实现
//2.查询数据库,判断用户以及密码是否正确
userinfo := []models.Manager{}
password = models.Md5(password)
models.DB.Where("username = ? and password = ? ", username, password).Find(&userinfo)
所以在handler/rbac.go 中需要返回用户相关信息,实现用户登录逻辑代码如下:
package handler
import (
"context"
"rbac/models"
pb "rbac/proto/rbac"
)
type Rbac struct{}
//后台用户登录的微服务
func (e *Rbac) Login(ctx context.Context, req *pb.LoginRequest, res *pb.LoginResponse) error {
managerList := []models.Manager{}
err := models.DB.Where("username=? AND password=?", req.Username, req.Password).Find(&managerList).Error
//处理数据
var templist []*pb.ManagerModel
for i := 0; i < len(managerList); i++ {
templist = append(templist, &pb.ManagerModel{
Id: int64(managerList[i].Id),
Username: managerList[i].Username,
Password: managerList[i].Password,
Mobile: managerList[i].Mobile,
Email: managerList[i].Email,
Status: int64(managerList[i].Status),
RoleId: int64(managerList[i].RoleId),
AddTime: int64(managerList[i].AddTime),
IsSuper: int64(managerList[i].IsSuper),
})
}
if len(managerList) > 0 {
res.IsLogin = true
} else {
res.IsLogin = false
}
res.Userlist = templist
return err
}
(9).配置consul服务发现
在main.go配置consul服务发现
package main
import (
"rbac/handler"
"rbac/models"
pb "rbac/proto/rbac"
"go-micro.dev/v4"
"go-micro.dev/v4/logger"
"github.com/go-micro/plugins/v4/registry/consul"
)
var (
service = "rbac"
version = "latest"
)
func main() {
//集成consul
consulReg := consul.NewRegistry()
// Create service
//读取.ini里面的配置
addr := models.Config.Section("consul").Key("addr").String()
srv := micro.NewService(
micro.Address(addr), //指定微服务的ip: 选择注册服务器地址,也可以不配置,默认为本机,也可以选择consul集群中的client
micro.Name(service),
micro.Version(version),
//注册consul
micro.Registry(consulReg),
)
srv.Init(
micro.Name(service),
micro.Version(version),
)
// Register handler
if err := pb.RegisterRbacHandler(srv.Server(), new(handler.Rbac)); err != nil {
logger.Fatal(err)
}
// Run service
if err := srv.Run(); err != nil {
logger.Fatal(err)
}
}
3.启动consul服务发现
在cmd中运行命令: consul agent -dev,具体参考: [golang 微服务] 5. 微服务服务发现介绍,安装以及consul的使用,Consul集群
4.注册验证码微服务服务端到服务发现(consul)
rbac目录下运行go run main.go,然后在consul UI 查看,是否注册成功
注册成功了
三.创建权限管理Rbac微服务客户端
1.首先把server/rbac/proto文件夹复制到项目中
2.配置consul服务发现
在models下创建initRbacConsul.go,配置consul服务发现,以便调用,代码如下:
package models
//Rbac consul(服务发现)初始化配置
//微服务客户端配置: 初始化consul配置,当一个项目中多个微服务时,就很方便了
//建议:一个微服务对应一个客户端,这样好管理
import (
"github.com/go-micro/plugins/v4/registry/consul"
"go-micro.dev/v4"
"go-micro.dev/v4/client"
"go-micro.dev/v4/registry"
)
//RbacClient: 全局变量 在外部的包中可以调用
var RbacClient client.Client
//init 方法: 当程序运行时就会自动执行
func init() {
consulRegistry := consul.NewRegistry(
//指定微服务的ip: 选择注册服务器地址,默认为本机,也可以选择consul集群中的client,建议一个微服务对应一个consul集群的client
registry.Addrs("127.0.0.1:8500"),
)
// Create service
srv := micro.NewService(
micro.Registry(consulRegistry),
)
srv.Init()
RbacClient = srv.Client()
}
在这里,也许没有引入,执行命令:go mod tidy引入对应的包
3.调用Rbac微服务
在controllers/login.go的DoIndex方法中调用Rbac微服务
原DoIndex方法代码如下:
//执行登录操作
func (con LoginController) DoIndex(c *gin.Context) {
//获取表单中的数据
captchaId := c.PostForm("captchaId") // 验证码id
verifyValue := c.PostForm("verifyValue") //验证码的值
//获取用户名以及密码
username := c.PostForm("username")
password := c.PostForm("password")
// 1.判断验证码是否验证成功
if flag := models.VerifyCaptcha(captchaId, verifyValue); flag {
//2.查询数据库,判断用户以及密码是否正确
userinfo := []models.Manager{}
password = models.Md5(password)
models.DB.Where("username = ? and password = ? ", username, password).Find(&userinfo)
if len(userinfo) > 0 {
//3.执行登录,保存用户信息,执行跳转操作
session := sessions.Default(c)
//注意: session.Set没法保存结构体对应的切片,所以需要把结构体转换成json字符串
userinfoSlice, _ := json.Marshal(userinfo)
session.Set("userinfo_admin", string(userinfoSlice))
session.Save()
con.Success(c, "登录成功", "/admin")
} else {
con.Error(c, "用户名或密码错误", "/admin/login")
}
} else {
con.Error(c, "验证码验证失败", "/admin/login")
}
}
然后在//2.查询数据库,判断用户以及密码是否正确这一步调用Rbac微服务,代码修改如下:
//这里需要import (
// "context"
// pbRbac "goshop/proto/rbac"
// )
//执行登录操作
func (con LoginController) DoIndex(c *gin.Context) {
//获取表单中的数据
captchaId := c.PostForm("captchaId") // 验证码id
verifyValue := c.PostForm("verifyValue") //验证码的值
//获取用户名以及密码
username := c.PostForm("username")
password := c.PostForm("password")
// 1.判断验证码是否验证成功
if flag := models.VerifyCaptcha(captchaId, verifyValue); flag {
//2.查询数据库,判断用户以及密码是否正确
//userinfo := []models.Manager{}
//password = models.Md5(password)
//models.DB.Where("username = ? and password = ? ", username, password).Find(&userinfo)
//调用Rbac微服务
rbacClient := pbRbac.NewRbacService("rbac", models.RbacClient)
res, _ := rbacClient.Login(context.Background(), &pbRbac.LoginRequest{
Username: username,
Password: models.Md5(password),
})
//通过Rbac微服务结果,判断是否登录
if res.IsLogin {
//3.执行登录,保存用户信息,执行跳转操作
session := sessions.Default(c)
//注意: session.Set没法保存结构体对应的切片,所以需要把结构体转换成json字符串
userinfoSlice, _ := json.Marshal(res.Userlist) //res.Userlist:微服务返回的用户信息
session.Set("userinfo_admin", string(userinfoSlice))
session.Save()
con.Success(c, "登录成功", "/admin")
} else {
con.Error(c, "用户名或密码错误", "/admin/login")
}
} else {
con.Error(c, "验证码验证失败", "/admin/login")
}
}
好了,权限管理Rbac微服务服客户端代码就ok了,接下来验证Rbac微服务是否成功
四.校验权限管理Rbac微服务功能
1.先启动服务端
见 [golang gin框架] 40.Gin商城项目-微服务实战之Captcha验证码微服务代码, 这里还要启动验证码captcha微服务服务端代码才行
2.启动客户端
在项目根目录下运行 :go run main.go,启动项目
3.校验权限管理Rbac微服务操作是否成功
访问后台登录页面,输入用户名,密码,验证码,看看是否成功
好了,权限管理Rbac微服务后台用户登录功能客户端操作完成,这里微服务操作的服务端,客户端功能大致与[golang gin框架] 40.Gin商城项目-微服务实战之Captcha验证码微服务类似,可参考该文章操作,下面一节继续讲解权限管理Rbac微服务的角色增删改查微服务功能
[上一节][golang gin框架] 40.Gin商城项目-微服务实战之Captcha验证码微服务