[go] 后台管理数据权限控制实现 (无业务修改)
- 2022 年 6 月 03 日
本文字数:3570 字
阅读完需:约 12 分钟
前言
这篇文章与[ts]后台管理数据权限控制实现(无业务修改)_typescript_林逸民_InfoQ写作社区内容是一样的, 只是语言由typescript(ts)
换成了golang(go)
, 该文章基于扩展[go]mongo工具类_Go_林逸民_InfoQ写作社区来实现无需业务侵入就能实现后台的数据权限控制.
数据库接口
文章就不再贴相关接口的代码了, 具体请看上面的文章.
模型
由于数据库都是mongo
, 因此只需将原先ts
模型改为go
的即可, 另外需要定义一个CRUD
的枚举, 大致代码如下:
// 模型接口
type IDbModel interface {
GetID() string
}
// 管理员权限模型
type AdminPermission struct {
ID string `bson:"_id" db:"_id"`
Permission map[string]map[DbOpValue]interface{}
}
func (m AdminPermission) GetID() string {
return m.ID
}
// 枚举
type DbOpValue string
const (
// 增加
DbOpInsert DbOpValue = "c"
// 删除
DbOpDelete DbOpValue = "d"
// 更新
DbOpUpdate DbOpValue = "u"
// 查询
DbOpQuery DbOpValue = "r"
)
IUnitOfWorkRepository - 增删改权限
由于数据库增删改都是由IUnitOfWork
管理的, 因此只需要重新实现该接口, 而内部包含mongo
版的IUnitOfWork
, 大致代码如下:
// 工作单元仓储
type unitOfWorkRepository struct {
adminPermissionDbQuery IDbQuery
uow IUnitOfWorkRepository
adminID string
modelOperation map[string]map[DbOpValue]bool
}
// 提交
func (m *unitOfWorkRepository) Commit() (err error) {
defer func() {
m.modelOperation = make(map[string]map[DbOpValue]bool)
}()
var adminPermissions []AdminPermission
err = m.adminPermissionDbQuery.Where(bson.M{
"_id": m.adminID,
}).ToArray(&adminPermissions)
if err != nil {
return
}
if len(adminPermissions) > 0 {
for k, v := range m.modelOperation {
for ck, _ := range v {
if sv, ok := adminPermissions[0].Permission[k]; ok {
if _, ok = sv[ck]; ok {
return fmt.Errorf("无权限: %s, %d", k, ck)
}
}
}
}
}
err = m.uow.Commit()
return
}
// 注冊删除
func (m *unitOfWorkRepository) RegisterDelete(entry IDbModel) {
m.addModelOperation(entry, DbOpDelete)
m.uow.RegisterDelete(entry)
}
// 注冊新增
func (m *unitOfWorkRepository) RegisterInsert(entry IDbModel) {
m.addModelOperation(entry, DbOpInsert)
m.uow.RegisterInsert(entry)
}
// 注冊更新
func (m *unitOfWorkRepository) RegisterUpdate(entry IDbModel) {
m.addModelOperation(entry, DbOpUpdate)
m.uow.RegisterUpdate(entry)
}
// 添加模型操作
func (m *unitOfWorkRepository) addModelOperation(entry IDbModel, op DbOpValue) {
modelType := reflect.TypeOf(entry)
model := modelType.Name()
if _, ok := m.modelOperation[model]; !ok {
m.modelOperation[model] = make(map[DbOpValue]bool)
}
m.modelOperation[model][op] = true
}
// 创建工作单元仓储
func newUnitOfWorkRepository(
adminPermissionDbQuery IDbQuery,
uow IUnitOfWorkRepository,
adminID string,
) IUnitOfWorkRepository {
return &unitOfWorkRepository{
adminID: adminID,
adminPermissionDbQuery: adminPermissionDbQuery,
modelOperation: make(map[string]map[DbOpValue]bool),
uow: uow,
}
}
IDbQuery - 查询权限
查询权限的实现跟增删改的实现类似, 而接口则从IUnitOfWork
换成了IDbQuery
, 大致代码如下:
type dbQuery struct {
adminPermissionDbQuery DbQuery
originDbQuery IDbQuery
filter bson.M
adminID string
model string
}
func (m *dbQuery) Count() (count int64, err error) {
var filter bson.M
if filter, err = m.getFilter(); err != nil {
return
}
return m.originDbQuery.Where(filter).Count()
}
func (m *dbQuery) Order(fields ...string) IDbQuery {
m.originDbQuery.Order(fields...)
return m
}
func (m *dbQuery) OrderByDesc(fields ...string) IDbQuery {
m.originDbQuery.OrderByDesc(fields...)
return m
}
func (m *dbQuery) Skip(v int) IDbQuery {
m.originDbQuery.Skip(v)
return m
}
func (m *dbQuery) Take(v int) IDbQuery {
m.originDbQuery.Take(v)
return m
}
func (m *dbQuery) ToArray(dst interface{}) (err error) {
var filter bson.M
if filter, err = m.getFilter(); err != nil {
return
}
err = m.originDbQuery.Where(filter).ToArray(dst)
return
}
func (m *dbQuery) Where(args ...interface{}) IDbQuery {
if len(args) == 0 {
return m
}
if f, ok := args[0].(bson.M); ok {
m.filter = f
}
return m
}
func (m *dbQuery) getFilter() (filter bson.M, err error) {
filter = m.filter
m.filter = nil
if filter == nil {
filter = bson.M{}
}
var adminPermissions []AdminPermission
err = m.adminPermissionDbQuery.Where(bson.M{
"_id": m.adminID,
}).ToArray(&adminPermissions)
if err != nil {
return
}
if len(adminPermissions) > 0 {
if v, ok := adminPermissions[0].Permission[m.model]; ok {
if cv, ok := v[DbOpQuery]; ok {
// 合并条件
var doc bson.M
bson.Unmarshal(
cv.(primitive.Binary).Data,
&doc,
)
for sk, sv := range doc {
filter[sk] = sv
}
}
}
}
return
}
func newDbQuery(
adminPermissionDbQuery IDbQuery,
originDbQuery IDbQuery,
adminID string,
model string,
) IDbQuery {
return &dbQuery{
adminPermissionDbQuery: adminPermissionDbQuery,
originDbQuery: originDbQuery,
adminID: adminID,
model: model,
}
}
IDbFactory
由于dbQuery
和unitOfWorkRepository
都有根据adminID
查询AdminPermission
, 因此可以将此段逻辑提取到工厂中以延迟加载的方式复用, dbQuery
和unitOfWorkRepository
的重构代码这里省略了, 其他代码如下:
type dbFactory struct {
mongoDbFactory IDbFactory
adminID string
adminPermissions []AdminPermission
}
func (m *dbFactory) Db(entry IDbModel, extra ...interface{}) IDbRepository {
var uow *unitOfWorkRepository
isTx := true
underscore.Chain(extra).Each(func(r interface{}, _ int) {
if v, ok := r.(*unitOfWorkRepository); ok {
uow = v
}
})
if uow == nil {
isTx = false
uow = m.Uow().(*unitOfWorkRepository)
}
modelType := reflect.TypeOf(entry)
return newDbRepository(
m.mongoDbFactory.Db(entry).Query(),
uow,
isTx,
modelType.Name(),
m,
)
}
func (m *dbFactory) GetAdminPermissions() ([]AdminPermission, error) {
if m.adminPermissions == nil {
var adminPermissions []AdminPermission
err := m.mongoDbFactory.Db(AdminPermission{}).Query().Where(bson.M{
"_id": m.adminID,
}).ToArray(&adminPermissions)
if err != nil {
return nil, err
}
m.adminPermissions = adminPermissions
}
return m.adminPermissions, nil
}
func (m *dbFactory) Uow() IUnitOfWork {
uow := m.mongoDbFactory.Uow()
return newUnitOfWorkRepository(
uow.(IUnitOfWorkRepository),
m,
)
}
// 创建数据库工厂
func NewDbFactory(
mongoDbFactory IDbFactory,
adminID string,
) IDbFactory {
return &dbFactory{
mongoDbFactory: mongoDbFactory,
adminID: adminID,
}
}
结尾
对于后台管理应用, 如果权限默认开启的话, 那么当权限刚上线的那一刻所有人都没有权限, 还得花不少时间新增所有人的权限数据, 反过来则只需要添加一些需要限制人员的数据即可, 或者后续开发一个控制页面, 让权限以灵活的方式又不影响功能的使用.
其他的部分都可以参考ts
的实现, 这里就不再提供代码了.
文章到此结束了, 如果有任何问题和疑问, 欢迎留言, 谢谢.
版权声明: 本文为 InfoQ 作者【林逸民】的原创文章。
原文链接:【http://xie.infoq.cn/article/09c954c624275a34dbbf8853e】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
林逸民
痛苦丢给身体 舒适留给灵魂 2018.11.13 加入
编程爱好者
评论