php 网站 教程,重庆建筑网络平台,网络推广目标怎么写,垂直类门户网站文章目录 0. 背景1. 准备工作2. gin中间件2.1 中间件代码2.2 中间件使用2.3 测试中间件使用结果 3. 添加权限管理API3.1 获取所有用户3.2 获取所有角色组3.3 获取所有角色组的策略3.4 修改角色组策略3.5 删除角色组策略3.6 添加用户到组3.7 从组中删除用户3.8 测试API 4. 最终目… 文章目录 0. 背景1. 准备工作2. gin中间件2.1 中间件代码2.2 中间件使用2.3 测试中间件使用结果 3. 添加权限管理API3.1 获取所有用户3.2 获取所有角色组3.3 获取所有角色组的策略3.4 修改角色组策略3.5 删除角色组策略3.6 添加用户到组3.7 从组中删除用户3.8 测试API 4. 最终目录结构和代码4.1 main.go4.2 casbin.go4.3 middleware.go 5. 更进一步 0. 背景 Casbin是用于Golang项目的功能强大且高效的开源访问控制库。 强大通用也意味着概念和配置较多具体到实际应用(以Gin Web框架开发需要解决以下问题 权限配置的存储以及增删改查Gin框架的中间件如何实现 经过一番摸索实践出经验计划分为三个章节循序渐进的介绍使用方法 1. Casbin概念介绍以及库使用 2. 使用Gorm存储Casbin权限配置以及增删改查 3.实现Gin鉴权中间件 代码地址 https://gitee.com/leobest2/gin-casbin-example
1. 准备工作 上一章已实现了casbin和gorm权限模型设计以及增删改查操作本章在此基础上实现以下需求 集成到gin中添加一个鉴权中间件提供角色用户增删改查API接口: 至此当前目录结构 .
├── casbin.go
├── go.mod
├── go.sum
└── test.dbcasbin.go完整代码见上一章结尾: 2. gin中间件
2.1 中间件代码 添加一个middleware.go, 这里简便起见假设用户从url传递 /xxxx?usernameleo实际应用中可以结合jwt等鉴权 HTTP GET /api/user?usernameleo package mainimport github.com/gin-gonic/ginfunc NewCasbinAuth(srv *CasbinService) gin.HandlerFunc {return func(ctx *gin.Context) {err : srv.enforcer.LoadPolicy()if err ! nil {ctx.String(500, err.Error())ctx.Abort()return}// 简便起见假设用户从url传递 /xxxx?usernameleo实际应用可以结合jwt等鉴权username, _ : ctx.GetQuery(username)ok, err : srv.enforcer.Enforce(username, ctx.Request.URL.Path, ctx.Request.Method)if err ! nil {ctx.String(500, err.Error())ctx.Abort()return} else if !ok {ctx.String(403, 验证权限失败!)ctx.Abort()return}ctx.Next()}
}2.2 中间件使用 main.go package mainimport (github.com/gin-gonic/gingithub.com/glebarez/sqlitegorm.io/gorm
)func main() {db, err : gorm.Open(sqlite.Open(test.db), gorm.Config{})if err ! nil {panic(failed to connect database: err.Error())}casbinService, err : NewCasbinService(db)if err ! nil {panic(failed to new casbin service: err.Error())}r : gin.Default()auth : r.Group(/api)auth.Use(NewCasbinAuth(casbinService))auth.GET(/api/user, func(ctx *gin.Context) {ctx.String(200, get /api/user success)})auth.DELETE(/api/user, func(ctx *gin.Context) {ctx.String(200, delete /api/user success)})r.Run(:8000)
}
2.3 测试中间件使用结果 测试权限数据库内容 ptypev0v1v2v3v4v5padmin/api/userGETpadmin/api/userDELETEpuser/api/userGETgleoadmingleo2user 测试脚本 # 权限失败
curl -X GET http://localhost:8000/api/user?usernameguest
# 权限成功
curl -X GET http://localhost:8000/api/user?usernameleo
# 权限成功
curl -X DELETE http://localhost:8000/api/user?usernameleo
# 权限失败
curl -X DELETE http://localhost:8000/api/user?usernameleo2测试结果 3. 添加权限管理API 以下使用上一章casbin_service提供的方法示例API如下可进一步定制 3.1 获取所有用户 // 获取所有用户auth.GET(/casbin/users, func(ctx *gin.Context) {ctx.JSON(200, casbinService.GetUsers())})3.2 获取所有角色组 // 获取所有角色组auth.GET(/casbin/roles, func(ctx *gin.Context) {ctx.JSON(200, casbinService.GetRoles())})3.3 获取所有角色组的策略 // 获取所有角色组的策略auth.GET(/casbin/rolepolicy, func(ctx *gin.Context) {roles, err : casbinService.GetRolePolicy()if err ! nil {ctx.String(500, 获取所有角色及权限失败: err.Error())} else {ctx.JSON(200, roles)}})3.4 修改角色组策略 /* 修改角色组策略type RolePolicy struct {RoleName string gorm:column:v0Url string gorm:column:v1Method string gorm:column:v2}*/auth.POST(/casbin/rolepolicy, func(ctx *gin.Context) {var p RolePolicyctx.BindJSON(p)err : casbinService.CreateRolePolicy(p)if err ! nil {ctx.String(500, 创建角色策略失败: err.Error())} else {ctx.JSON(200, 成功!)}})3.5 删除角色组策略 /* 删除角色组策略type RolePolicy struct {RoleName string gorm:column:v0Url string gorm:column:v1Method string gorm:column:v2}*/auth.DELETE(/casbin/rolepolicy, func(ctx *gin.Context) {var p RolePolicyctx.BindJSON(p)err : casbinService.DeleteRolePolicy(p)if err ! nil {ctx.String(500, 删除角色策略失败: err.Error())} else {ctx.JSON(200, 成功!)}})3.6 添加用户到组 // 添加用户到组, /casbin/user-role?usernameleorolenameadminauth.POST(/casbin/user-role, func(ctx *gin.Context) {username : ctx.Query(username)rolename : ctx.Query(rolename)err : casbinService.UpdateUserRole(username, rolename)if err ! nil {ctx.String(500, 添加用户到组失败: err.Error())} else {ctx.JSON(200, 成功!)}})3.7 从组中删除用户 // 从组中删除用户, /casbin/user-role?usernameleorolenameadminauth.DELETE(/casbin/user-role, func(ctx *gin.Context) {username : ctx.Query(username)rolename : ctx.Query(rolename)err : casbinService.DeleteUserRole(username, rolename)if err ! nil {ctx.String(500, 从组中删除用户失败: err.Error())} else {ctx.JSON(200, 成功!)}})3.8 测试API 因为这些API也用到了casbin_auth需要自行准备下权限 上述API测试两个不一一列举 4. 最终目录结构和代码 目录结构 ├── casbin.go
├── go.mod
├── go.sum
├── main.go
├── middleware.go
└── test.db4.1 main.go
package mainimport (github.com/gin-gonic/gingithub.com/glebarez/sqlitegorm.io/gorm
)func main() {db, err : gorm.Open(sqlite.Open(test.db), gorm.Config{})if err ! nil {panic(failed to connect database: err.Error())}casbinService, err : NewCasbinService(db)if err ! nil {panic(failed to new casbin service: err.Error())}r : gin.Default()auth : r.Group(/)auth.Use(NewCasbinAuth(casbinService))auth.GET(/api/user, func(ctx *gin.Context) {ctx.String(200, get /api/user success)})auth.DELETE(/api/user, func(ctx *gin.Context) {ctx.String(200, delete /api/user success)})// 获取所有用户auth.GET(/casbin/users, func(ctx *gin.Context) {ctx.JSON(200, casbinService.GetUsers())})// 获取所有角色组auth.GET(/casbin/roles, func(ctx *gin.Context) {ctx.JSON(200, casbinService.GetRoles())})// 获取所有角色组的策略auth.GET(/casbin/rolepolicy, func(ctx *gin.Context) {roles, err : casbinService.GetRolePolicy()if err ! nil {ctx.String(500, 获取所有角色及权限失败: err.Error())} else {ctx.JSON(200, roles)}})/* 修改角色组策略type RolePolicy struct {RoleName string gorm:column:v0Url string gorm:column:v1Method string gorm:column:v2}*/auth.POST(/casbin/rolepolicy, func(ctx *gin.Context) {var p RolePolicyctx.BindJSON(p)err : casbinService.CreateRolePolicy(p)if err ! nil {ctx.String(500, 创建角色策略失败: err.Error())} else {ctx.JSON(200, 成功!)}})/* 删除角色组策略type RolePolicy struct {RoleName string gorm:column:v0Url string gorm:column:v1Method string gorm:column:v2}*/auth.DELETE(/casbin/rolepolicy, func(ctx *gin.Context) {var p RolePolicyctx.BindJSON(p)err : casbinService.DeleteRolePolicy(p)if err ! nil {ctx.String(500, 删除角色策略失败: err.Error())} else {ctx.JSON(200, 成功!)}})// 添加用户到组, /casbin/user-role?usernameleorolenameadminauth.POST(/casbin/user-role, func(ctx *gin.Context) {username : ctx.Query(username)rolename : ctx.Query(rolename)err : casbinService.UpdateUserRole(username, rolename)if err ! nil {ctx.String(500, 添加用户到组失败: err.Error())} else {ctx.JSON(200, 成功!)}})// 从组中删除用户, /casbin/user-role?usernameleorolenameadminauth.DELETE(/casbin/user-role, func(ctx *gin.Context) {username : ctx.Query(username)rolename : ctx.Query(rolename)err : casbinService.DeleteUserRole(username, rolename)if err ! nil {ctx.String(500, 从组中删除用户失败: err.Error())} else {ctx.JSON(200, 成功!)}})r.Run(:8000)
}
4.2 casbin.go
package mainimport (github.com/casbin/casbin/v2github.com/casbin/casbin/v2/modelgormadapter github.com/casbin/gorm-adapter/v3gorm.io/gorm
)/*
按如下约定:1. 所有策略只针对角色组设置2. 用户关联到组(一个用户可以有多个组)
---------------------------------------------
| ptype | v0 | v1 | v2 | v3 | v4 | v5 |
---------------------------------------------
| p | admin | /api/user | GET | | | |
---------------------------------------------
| p | admin | /api/user | DELETE | | | |
---------------------------------------------
| p | user | /api/user | GET | | | |
---------------------------------------------
| ... | ... | ... | | | | |
---------------------------------------------
| g | leo | admin | | | | |
---------------------------------------------
| g | leo2 | admin | | | | |
---------------------------------------------
| g | leo3 | user | | | | |
---------------------------------------------
*/
type CasbinService struct {enforcer *casbin.Enforceradapter *gormadapter.Adapter
}func NewCasbinService(db *gorm.DB) (*CasbinService, error) {a, err : gormadapter.NewAdapterByDB(db)if err ! nil {return nil, err}m, err : model.NewModelFromString([request_definition]r sub, obj, act[policy_definition]p sub, obj, act[role_definition]g _, _[policy_effect]e some(where (p.eft allow))[matchers]m g(r.sub, p.sub) keyMatch2(r.obj,p.obj) r.act p.act)if err ! nil {return nil, err}e, err : casbin.NewEnforcer(m, a)if err ! nil {return nil, err}return CasbinService{adapter: a, enforcer: e}, nil
}// (RoleName, Url, Method) 对应于 CasbinRule 表中的 (v0, v1, v2)
type RolePolicy struct {RoleName string gorm:column:v0Url string gorm:column:v1Method string gorm:column:v2
}// 获取所有角色组
func (c *CasbinService) GetRoles() []string {return c.enforcer.GetAllRoles()
}// 获取所有角色组权限
func (c *CasbinService) GetRolePolicy() (roles []RolePolicy, err error) {err c.adapter.GetDb().Model(gormadapter.CasbinRule{}).Where(ptype p).Find(roles).Errorif err ! nil {return nil, err}return
}// 创建角色组权限, 已有的会忽略
func (c *CasbinService) CreateRolePolicy(r RolePolicy) error {// 不直接操作数据库利用enforcer简化操作err : c.enforcer.LoadPolicy()if err ! nil {return err}_, err c.enforcer.AddPolicy(r.RoleName, r.Url, r.Method)if err ! nil {return err}return c.enforcer.SavePolicy()
}// 修改角色组权限
func (c *CasbinService) UpdateRolePolicy(old, new RolePolicy) error {_, err : c.enforcer.UpdatePolicy([]string{old.RoleName, old.Url, old.Method},[]string{new.RoleName, new.Url, new.Method})if err ! nil {return err}return c.enforcer.SavePolicy()
}// 删除角色组权限
func (c *CasbinService) DeleteRolePolicy(r RolePolicy) error {_, err : c.enforcer.RemovePolicy(r.RoleName, r.Url, r.Method)if err ! nil {return err}return c.enforcer.SavePolicy()
}type User struct {UserName stringRoleNames []string
}// 获取所有用户以及关联的角色
func (c *CasbinService) GetUsers() (users []User) {p : c.enforcer.GetGroupingPolicy()usernameUser : make(map[string]*User, 0)for _, _p : range p {username, usergroup : _p[0], _p[1]if v, ok : usernameUser[username]; ok {usernameUser[username].RoleNames append(v.RoleNames, usergroup)} else {usernameUser[username] User{UserName: username, RoleNames: []string{usergroup}}}}for _, v : range usernameUser {users append(users, *v)}return
}// 角色组中添加用户, 没有组默认创建
func (c *CasbinService) UpdateUserRole(username, rolename string) error {_, err : c.enforcer.AddGroupingPolicy(username, rolename)if err ! nil {return err}return c.enforcer.SavePolicy()
}// 角色组中删除用户
func (c *CasbinService) DeleteUserRole(username, rolename string) error {_, err : c.enforcer.RemoveGroupingPolicy(username, rolename)if err ! nil {return err}return c.enforcer.SavePolicy()
}// 验证用户权限
func (c *CasbinService) CanAccess(username, url, method string) (ok bool, err error) {return c.enforcer.Enforce(username, url, method)
}
4.3 middleware.go
package mainimport (loggithub.com/gin-gonic/gin
)func NewCasbinAuth(srv *CasbinService) gin.HandlerFunc {return func(ctx *gin.Context) {err : srv.enforcer.LoadPolicy()if err ! nil {ctx.String(500, err.Error())ctx.Abort()return}// 简便起见假设用户从url传递 /xxxx?usernameleo实际应用可以结合jwt等鉴权username, _ : ctx.GetQuery(username)log.Println(username, ctx.Request.URL.Path, ctx.Request.Method)ok, err : srv.enforcer.Enforce(username, ctx.Request.URL.Path, ctx.Request.Method)if err ! nil {ctx.String(500, err.Error())ctx.Abort()return} else if !ok {ctx.String(403, 验证权限失败!)ctx.Abort()return}ctx.Next()}
}
5. 更进一步 主要记录一下casbin概念和使用经验距离业务使用还有以下等需要调整 用户身份识别是通过URL参数username获得实际使用中可配合jwt、session等使用API接口可根据业务规范调整重写用户其他信息需要关联到casbin_rule表增加前端界面操作管理权限…