在Golang项目中实现基础权限控制,核心是先梳理清楚用户、角色、权限三者的关联关系,再通过统一的校验逻辑对接口访问进行拦截,避免无权限用户操作敏感资源。常见的权限模型采用RBAC(基于角色的访问控制),即用户关联角色,角色关联权限,权限对应具体的操作或资源访问能力。
权限模型设计
首先需要定义三个核心结构体,分别存储用户、角色、权限的基础信息,这里采用内存存储的方式做示例,实际项目中可以替换为数据库存储。
// 权限结构体,定义具体的操作权限
type Permission struct {
ID int
Name string // 权限名称,如 user:view、order:edit
Desc string // 权限描述
}
// 角色结构体,关联多个权限
type Role struct {
ID int
Name string
Permissions []*Permission // 角色拥有的权限列表
}
// 用户结构体,关联多个角色
type User struct {
ID int
Name string
Roles []*Role // 用户拥有的角色列表
}
权限校验逻辑实现
接下来需要实现两个核心方法,一个是获取用户所有权限的集合,另一个是校验用户是否拥有指定权限。
package main
import (
"fmt"
)
// 获取用户所有权限,去重后返回权限名称集合
func GetUserPermissions(user *User) map[string]bool {
permissionMap := make(map[string]bool)
// 遍历用户的所有角色
for _, role := range user.Roles {
// 遍历角色的所有权限
for _, perm := range role.Permissions {
permissionMap[perm.Name] = true
}
}
return permissionMap
}
// 校验用户是否拥有指定权限
func HasPermission(user *User, requiredPerm string) bool {
userPerms := GetUserPermissions(user)
// 判断权限集合中是否存在需要的权限
_, exists := userPerms[requiredPerm]
return exists
}
中间件实现接口权限拦截
在Gin框架中,可以通过自定义中间件的方式,在接口处理前完成权限校验,无权限则返回错误响应。
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// 权限校验中间件,需要传入接口要求的权限名称
func PermissionMiddleware(requiredPerm string) gin.HandlerFunc {
return func(c *gin.Context) {
// 这里从上下文获取当前登录用户,实际项目中从token或session中解析
user, exists := c.Get("current_user")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"msg": "未登录"})
c.Abort()
return
}
// 类型断言获取用户对象
currentUser, ok := user.(*User)
if !ok {
c.JSON(http.StatusInternalServerError, gin.H{"msg": "用户信息异常"})
c.Abort()
return
}
// 校验权限
if !HasPermission(currentUser, requiredPerm) {
c.JSON(http.StatusForbidden, gin.H{"msg": "无访问权限"})
c.Abort()
return
}
// 权限校验通过,继续执行后续逻辑
c.Next()
}
}
完整使用示例
下面演示如何初始化权限数据,注册接口并添加权限中间件。
func main() {
// 初始化权限
viewUserPerm := &Permission{ID: 1, Name: "user:view", Desc: "查看用户权限"}
editUserPerm := &Permission{ID: 2, Name: "user:edit", Desc: "编辑用户权限"}
// 初始化角色
adminRole := &Role{ID: 1, Name: "管理员", Permissions: []*Permission{viewUserPerm, editUserPerm}}
normalRole := &Role{ID: 2, Name: "普通用户", Permissions: []*Permission{viewUserPerm}}
// 初始化用户
adminUser := &User{ID: 1, Name: "管理员用户", Roles: []*Role{adminRole}}
normalUser := &User{ID: 2, Name: "普通用户", Roles: []*Role{normalRole}}
// 初始化Gin路由
r := gin.Default()
// 模拟登录接口,设置当前用户到上下文
r.POST("/login", func(c *gin.Context) {
// 实际项目中根据登录参数查询用户,这里简化为返回管理员用户
c.Set("current_user", adminUser)
c.JSON(http.StatusOK, gin.H{"msg": "登录成功"})
})
// 用户查看接口,需要 user:view 权限
r.GET("/user/list", PermissionMiddleware("user:view"), func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"msg": "用户列表"})
})
// 用户编辑接口,需要 user:edit 权限
r.POST("/user/edit", PermissionMiddleware("user:edit"), func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"msg": "编辑用户成功"})
})
r.Run(":8080")
}
扩展说明
上述实现是基础版本的权限控制,实际项目中可以根据需求扩展:比如增加权限的层级关系,支持通配符权限匹配(如user:*匹配所有用户相关权限);将用户、角色、权限数据持久化到MySQL或Redis中,提升查询效率;增加权限的缓存机制,避免每次请求都查询全量权限数据。如果是微服务架构,还可以将权限校验逻辑抽成独立的权限服务,统一处理所有服务的权限校验需求。