你用Go写过中间件吗?带你用Gin实现【用户角色权限管理中间件】

2022-10-26 14:39:07 浏览数 (2)

需求整理

  1. 管理后台有超管权限,超管拥有所有权限
  2. 普通管理员可以设置角色,角色单选
  3. 角色可以赋予多个权限,权限多选
  4. 这样我们就实现了对普通管理员的角色和权限的灵活管理

文档说明

  1. 基于golang语言开发
  2. 基于gin网络框架开发
  3. 基于MySQL5.8开发
  4. 把权限管理部分封装成中间件,在rourter文件中引用
  5. 非核心代码已省略,用3个竖着排列的点号.表示

数据库表结构设计

管理员表

权限表

角色表

角色表permission字段示意

代码部分

路由文件

代码语言:javascript复制
package server

import (
	.
	.
	.
	"os"
	"github.com/gin-gonic/gin"
)

// NewRouter 路由配置
func NewRouter() *gin.Engine {
	r := gin.Default()

	// 其他中间件
	.
	.
	.
	// 路由
	v1 := r.Group("/api/v1")
	{
		v1.POST("login", api.Login)
		auth := v1.Group("")
		//登录校验中间件
		auth.Use(middleware.AuthRequired())
		//关键代码:权限角色校验
		auth.Use(middleware.AuthCheckMiddleware)
		{
            .
            .
            .
			// 获取所有学校
			{
				auth.GET("/school/", api.GetSchoolInfo)
			}
			.
			.
			.
		}

	}
	return r
}

权限校验中间件代码

代码语言:javascript复制
package middleware

import (
	.
	.
	.
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"strings"
)

var AuthCheckMiddleware = authCheck()
func authCheck() gin.HandlerFunc {
	return func(c *gin.Context) {
		if admin, _ := c.Get("admin"); admin != nil {
			method := c.Request.Method
			url := c.Request.URL.Path
			adminInfo := admin.(**service.RunningClaims)
			isSuper := (*adminInfo).IsSuper //是否是超管
			roleId := (*adminInfo).RoleId

			if isSuper != 1 {
				fmt.Println("method:", method)
				fmt.Println("url:", url)
				permissionFunc := strings.ToLower(fmt.Sprintf("%s_%s", method, url))
				haveAuth := model.CheckRolePermission(uint(roleId), permissionFunc)
				fmt.Println("haveAuth  ", haveAuth)
				if !haveAuth {
					c.JSON(http.StatusOK, api.ReturnJson{http.StatusForbidden, "", "无权访问"})
					c.Abort()
				}
			}
			c.Next()
		}
	}
}

角色model层代码

  1. CheckRolePermission是关键代码
代码语言:javascript复制
//角色部分
type StringArray []string

//角色
type Role struct {
	Id          int          `gorm:"column:id" form:"id" json:"id" comment:"自增id" sql:"int(11),PRI"`
	Name        string       `gorm:"column:name" form:"name" json:"name" comment:"角色名" sql:"varchar(255)"`
	Description string       `gorm:"column:description" form:"description" json:"description" comment:"描述" sql:"varchar(255)"`
	Permission  *StringArray `gorm:"type:json;column:permission" form:"permission" json:"permission" comment:"权限"`
}

func (data *StringArray) Scan(val interface{}) (err error) {
	if val == nil {
		return nil
	}
	if payload, ok := val.([]byte); ok {
		var value []string
		err = json.Unmarshal(payload, &value)
		if err == nil {
			*data = value
		}
	}
	return
}

func CheckRolePermission(roleId uint, permissionFunc string) bool {
	if roleId == 0 {
		return false
	}

	var myRole Role
	err := DB.Where("id = ?", roleId).First(&myRole).Error
	if err != gorm.ErrRecordNotFound {
		fmt.Printf("%v", myRole)
		permissions := myRole.Permission
		permissions.Scan(permissions)
		for _, permission := range *permissions {
			fmt.Println("permissionFunc:", permissionFunc)
			fmt.Println("permission:", permission)
			if strings.HasPrefix(permissionFunc, permission) {
				return true
			}
		}
	}
	return false
}

运行效果

有权限

代码语言:javascript复制
{
    "code": 403,
    "data": "",
    "message": "无权访问"
}

无权限

代码语言:javascript复制
{
    "code": 200,
    "data": true,
    "message": "更新成功"
}

后续

下一章封装管理后台的操作日志管理:以中间件 goroutine的方式保存管理员的操作日志

0 人点赞