在开发keyauth之前 需要先准备环境
下面是mongo的基础概念:
docker pull mongo
docker run -itd -p 27017:27017 mongo
security:
authorization: enabled
use admin
db.createUser({user:"admin",pwd:"123456",roles:["root"]})
db.auth("admin", "123456")
use keyauth
db.createUser({user: "keyauth", pwd: "xxx", roles: [{ role: "dbOwner", db: "keyauth" }]})
安装vscode插件, 配置访问mongodb
# 1.安装protoc编译器, 项目使用版本: v3.19.1
# 下载预编译包安装: https://github.com/protocolbuffers/protobuf/releases
cp protoc-3.19.1-osx-x86_64/bin/protoc /usr/local/bin
cp protoc-3.19.1-osx-x86_64/include/* /usr/local/include
# 1.protoc-gen-go go语言查询, 项目使用版本: v1.27.1
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
# 2.安装protoc-gen-go-grpc插件, 项目使用版本: 1.1.0
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# 1.安装自定义proto tag插件
go install github.com/favadi/protoc-go-inject-tag@latest
cp -r docs/include/github.com /usr/local/include
环境准备后后,我们就可以配置好keyauth启动他
配置下keyauth, 主要是数据库Mongodb的配置
[app]
name = "keyauth"
host = "0.0.0.0"
http_port = "8050"
grpc_prot = "18050"
key = "this is your app key"
[mongodb]
endpoints = ["xxx:xxx"]
username = "xxx"
password = "xxxx"
database = "keyauth"
[log]
level = "debug"
path = "logs"
format = "text"
to = "stdout"
由于我们使用的MongoDB, 无需建表(No Schema), 因此直接初始化项目
make init
初始化有一组关键信息需要记录下:
这是为我们初始化的一个web端的凭证
make run
然后我们测试下 grpc 和 http api是否可以正常使用
keyauth的所有数据 都是存储在mongodb中的, 因此先介绍下 go操作monodb的基础知识, 下面以keyauth的user模块为例进行讲解
如何定义我们存入数据库的collection结构喃? 由于Mongo存储采用Bson, 也就是是二进制Json, 我们通过bson 标签就能定义我们struct存储结构:
这样我们就完成了Object -> Data 的映射, 是不是和ORM很像
// User info
type User struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// 用户所属部门
// @gotags: bson:"department_id" json:"department_id" validate:"lte=200"
DepartmentId string `protobuf:"bytes,1,opt,name=department_id,json=departmentId,proto3" json:"department_id" bson:"department_id" validate:"lte=200"`
// 用户账号名称
// @gotags: bson:"_id" json:"account" validate:"required,lte=60"
Account string `protobuf:"bytes,2,opt,name=account,proto3" json:"account" bson:"_id" validate:"required,lte=60"`
// 创建方式
// @gotags: bson:"create_type" json:"create_type"
CreateType CreateType `protobuf:"varint,3,opt,name=create_type,json=createType,proto3,enum=infraboard.keyauth.user.CreateType" json:"create_type" bson:"create_type"`
// 用户创建的时间
// @gotags: bson:"create_at" json:"create_at,omitempty"
CreateAt int64 `protobuf:"varint,4,opt,name=create_at,json=createAt,proto3" json:"create_at,omitempty" bson:"create_at"`
// 修改时间
// @gotags: bson:"update_at" json:"update_at,omitempty"
UpdateAt int64 `protobuf:"varint,5,opt,name=update_at,json=updateAt,proto3" json:"update_at,omitempty" bson:"update_at"`
// 如果是子账号和服务账号 都需要继承主用户Domain
// @gotags: bson:"domain" json:"domain,omitempty"
Domain string `protobuf:"bytes,6,opt,name=domain,proto3" json:"domain,omitempty" bson:"domain"`
// 是否是主账号
// @gotags: bson:"type" json:"type"
Type types.UserType `protobuf:"varint,7,opt,name=type,proto3,enum=infraboard.keyauth.user.UserType" json:"type" bson:"type"`
// 数据
// @gotags: bson:"profile" json:"profile"
Profile *Profile `protobuf:"bytes,8,opt,name=profile,proto3" json:"profile" bson:"profile"`
// 用户的角色(当携带Namesapce查询时会有)
// @gotags: bson:"-" json:"roles,omitempty"
Roles []string `protobuf:"bytes,9,rep,name=roles,proto3" json:"roles,omitempty" bson:"-"`
// 用户多久未登录时(天), 冻结改用户, 防止僵尸用户的账号被利用
// @gotags: bson:"expires_days" json:"expires_days"
ExpiresDays int32 `protobuf:"varint,10,opt,name=expires_days,json=expiresDays,proto3" json:"expires_days" bson:"expires_days"`
// 用户描述
// @gotags: json:"description"
Description string `protobuf:"bytes,11,opt,name=description,proto3" json:"description"`
// 用户是否初始化
// @gotags: bson:"is_initialized" json:"is_initialized"
IsInitialized bool `protobuf:"varint,12,opt,name=is_initialized,json=isInitialized,proto3" json:"is_initialized" bson:"is_initialized"`
// 密码相关信息
// @gotags: bson:"password" json:"password"
HashedPassword *Password `protobuf:"bytes,13,opt,name=hashed_password,json=hashedPassword,proto3" json:"password" bson:"password"`
// 用户状态
// @gotags: bson:"status" json:"status"
Status *Status `protobuf:"bytes,14,opt,name=status,proto3" json:"status" bson:"status"`
// 部门
// @gotags: bson:"-" json:"department,omitempty"
Department *department.Department `protobuf:"bytes,15,opt,name=department,proto3" json:"department,omitempty" bson:"-"`
}
为了提升collect的搜索速度,我们往往需要对 过滤条件建立缩影,防止collection全扫描, 如何为你的collection设置索引喃?
_, err := uc.Indexes().CreateMany(context.Background(), indexs)
if err != nil {
return err
}
mongo.IndexModel 是创建索引的参数,
下面是user的索引
indexs := []mongo.IndexModel{
{
Keys: bsonx.Doc{{Key: "name", Value: bsonx.Int32(-1)}},
Options: options.Index().SetUnique(true),
},
{
Keys: bsonx.Doc{{Key: "ldap_config.base_dn", Value: bsonx.Int32(-1)}},
Options: options.Index().SetUnique(true),
},
{
Keys: bsonx.Doc{{Key: "create_at", Value: bsonx.Int32(-1)}},
},
}
下面是完整的代码
func (s *service) Config() error {
s.policy = app.GetGrpcApp(policy.AppName).(policy.ServiceServer)
s.depart = app.GetGrpcApp(department.AppName).(department.ServiceServer)
s.domain = app.GetGrpcApp(domain.AppName).(domain.ServiceServer)
db := conf.C().Mongo.GetDB()
uc := db.Collection("user")
indexs := []mongo.IndexModel{
{
Keys: bsonx.Doc{{Key: "create_at", Value: bsonx.Int32(-1)}},
},
{
Keys: bsonx.Doc{{Key: "department_id", Value: bsonx.Int32(-1)}},
},
}
_, err := uc.Indexes().CreateMany(context.Background(), indexs)
if err != nil {
return err
}
s.col = uc
s.log = zap.L().Named("User")
return nil
}
索引虽好,但是有使用限制: 每个索引占据一定的存储空间, 索引也有大小限制(MySQL类似), 你应该确保该索引的大小不超过内存的限制:
func (s *service) saveAccount(u *user.User) error {
if _, err := s.col.InsertOne(context.TODO(), u); err != nil {
return exception.NewInternalServerError("inserted user(%s) document error, %s",
u.Account, err)
}
return nil
}
使用Find查找Collection:
func (r *queryUserRequest) FindFilter() bson.M {
filter := bson.M{
"type": r.UserType,
"domain": r.Domain,
}
if len(r.Accounts) > 0 {
filter["_id"] = bson.M{"$in": r.Accounts}
}
if r.DepartmentId != "" {
if r.WithAllSub {
filter["$or"] = bson.A{
bson.M{"department_id": bson.M{"$regex": r.DepartmentId, "$options": "im"}},
}
} else {
filter["department_id"] = r.DepartmentId
}
}
if r.Keywords != "" {
filter["$or"] = bson.A{
bson.M{"_id": bson.M{"$regex": r.Keywords, "$options": "im"}},
bson.M{"profile.mobile": bson.M{"$regex": r.Keywords, "$options": "im"}},
bson.M{"profile.email": bson.M{"$regex": r.Keywords, "$options": "im"}},
}
}
return filter
}
func (r *queryUserRequest) FindOptions() *options.FindOptions {
pageSize := int64(r.Page.PageSize)
skip := int64(r.Page.PageSize) * int64(r.Page.PageNumber-1)
opt := &options.FindOptions{
Sort: bson.D{{Key: "create_at", Value: -1}},
Limit: &pageSize,
Skip: &skip,
}
return opt
}
s.log.Debugf("find filter: %s", req.FindFilter())
resp, err := s.col.Find(context.TODO(), req.FindFilter(), req.FindOptions())
...
// 循环
for resp.Next(context.TODO()) {
u := new(user.User)
if err := resp.Decode(u); err != nil {
return nil, exception.NewInternalServerError("decode user error, error is %s", err)
}
u.Desensitize()
userSet.Add(u)
使用$set进行更新
_, err = s.col.UpdateOne(context.TODO(), bson.M{"_id": u.Account}, bson.M{"$set": u})
if err != nil {
return nil, exception.NewInternalServerError("update user(%s) error, %s", u.Account, err)
}
s.log.Debugf("save password to db ...")
_, err = s.col.UpdateOne(context.TODO(), bson.M{"_id": u.Account}, bson.M{"$set": bson.M{
"password": u.HashedPassword,
}})
_, err := s.col.DeleteOne(context.TODO(), bson.M{"_id": req.Account})
if err != nil {
return nil, exception.NewInternalServerError("delete user(%s) error, %s", req.Account, err)
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。