[golang gin框架] 14.Gin 商城项目-RBAC管理

一.权限介绍

RBAC 是基于角色的权限访问控制(Role-Based Access Control)。在 RBAC 中,权限与角色相
关联,用户通过成为适当角色的成员而得到这些角色的权限

二.用户 RBAC 权限管理树形图

[golang gin框架] 14.Gin 商城项目-RBAC管理_第1张图片

三.RBAC 实现流程

1、实现角色的增加修改删除

2、实现用户的增加修改删除,增加修改用户的时候需要选择角色

3、实现权限的增加修改删除 (页面菜单)

4、实现角色授权功能

5、判断当前登录的用户是否有访问菜单的权限

6、根据当前登录账户的角色信息动态显示左侧菜单

四.权限控制相关的数据库表

[golang gin框架] 14.Gin 商城项目-RBAC管理_第2张图片

五. 角色的增、删、改、查

1.创建角色模型

在models目录下创建role.go角色模型
package models

//角色模型

type Role struct { // 结构体首字母大写, 和数据库表名对应, 默认访问数据表users, 可以设置访问数据表的方法
    Id  int
    Title string
    Description string
    Status int
    AddTime int
}

//配置数据库操作的表名称
func (Role) TableName() string {
    return "role"
}

2.创建角色控制器

在controllers/admin下创建RoleController.go角色控制器
package admin

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

type RoleController struct {
    BaseController
}

//角色列表
func (con RoleController) Index(c *gin.Context) {
    //定义一个角色切片
    roleList := []models.Role{}
    //获取角色
    models.DB.Find(&roleList)
    c.HTML(http.StatusOK, "admin/role/index.html", gin.H{
        "roleList": roleList,
    })
}

//新增角色
func (con RoleController) Add(c *gin.Context) {
    c.HTML(http.StatusOK, "admin/role/add.html", gin.H{})
}

//新增角色:提交
func (con RoleController) DoAdd(c *gin.Context) {
    //获取表单的提交数据
    //strings.Trim(str, cutset), 去除字符串两边的cutset字符
    title := strings.Trim(c.PostForm("title"), " ") // 去除字符串两边的空格
    description := strings.Trim(c.PostForm("description"), " ")

    //判断角色名称是否为空
    if title == "" {
        con.Error(c, "角色名称不能为空", "/admin/role/add")
        return
    }
    //给角色模型赋值,并保存数据到数据库
    role := models.Role{}
    role.Title = title
    role.Description = description
    role.Status = 1
    role.AddTime = int(models.GetUnix())
    err := models.DB.Create(&role).Error
    if err != nil {
        con.Error(c, "增加角色失败,请重试", "/admin/role/add")
        return
    }
    con.Success(c, "增加角色成功", "/admin/role")
}

//编辑角色
func (con RoleController) Edit(c *gin.Context) {
    //获取角色id
    id, err := models.Int(c.Query("id"))
    if err != nil {
        con.Error(c, "传入数据错误", "/admin/role")
    } else {
        role := models.Role{Id: id}
        models.DB.Find(&role)
        c.HTML(http.StatusOK, "admin/role/edit.html", gin.H{
            "role": role,
        })
    }
}

//编辑角色:提交
func (con RoleController) DoEdit(c *gin.Context) {
    //获取提交的表单数据
    id, err := models.Int(c.PostForm("id"))
    if err != nil {
        con.Error(c, "传入数据错误", "/admin/role")
        return
    }
    //获取表单的提交数据
    //strings.Trim(str, cutset), 去除字符串两边的cutset字符
    title := strings.Trim(c.PostForm("title"), " ") // 去除字符串两边的空格
    description := strings.Trim(c.PostForm("description"), " ")
    //判断角色名称是否为空
    if title == "" {
        con.Error(c, "角色名称不能为空", "/admin/role/add")
        return
    }
    //查询角色是否存在
    role := models.Role{Id: id}
    models.DB.Find(&role)

    //修改角色属性
    role.Title = title
    role.Description = description
    err = models.DB.Save(&role).Error
    if err != nil {
        con.Error(c, "修改数据失败", "/admin/role/edit?id="+models.String(id))
        return
    }
    con.Success(c, "修改数据成功", "/admin/role")
}

//删除角色
func (con RoleController) Delete(c *gin.Context) {
    //获取提交的表单数据
    id, err := models.Int(c.Query("id"))
    if err != nil {
        con.Error(c, "传入数据错误", "/admin/role")
        return
    }

    //查询角色是否存在
    role := models.Role{Id: id}
    err = models.DB.Delete(&role).Error
    if err != nil {
        con.Error(c, "删除数据失败", "/admin/role")
        return
    }
    con.Success(c, "删除数据成功", "/admin/role")
}

3.创建角色html以及js

在templates/admin/role下创建角色相关html

index.html

{{ define "admin/role/index.html" }}
    {{ template "admin/public/page_header.html" .}}
    
    
{{ range $key,$value := .roleList}} {{end}}
编号 角色名称 角色说明 角色状态 操作
{{$value.Id}} {{$value.Title}} {{$value.Description}} {{if eq $value.Status 1}} {{else}} {{end}} 修改 删除
{{ end }}

add.html

{{ define "admin/role/add.html" }}
{{ template "admin/public/page_header.html" .}}
添加角色
  • 角色名称:
  • 角色描述:

{{ end }}

edit.html

{{ define "admin/role/edit.html" }}
{{ template "admin/public/page_header.html" .}}
编辑角色
  • 角色名称:
  • 角色描述:

{{ end }}

删除角色js提示

在static/admin/js/base.js下增加删除提示
$(function () {
    baseApp.init();
    //当窗口重置时,重新计算窗口高度
    $(window).resize(function () {
        baseApp.resizeIframe();
    })
})

var baseApp = {
    init: function () {
        this.initAside()
        this.confirmDelete()
        this.resizeIframe()
    },
    initAside: function () { //左侧菜单栏隐藏显示子栏
        $(".aside h4").click(function () {
            $(this).sibling("ul").slideToggle();
        })
    },
    resizeIframe: function() {  // 设置iframe高度
        $("rightMain").height($(window).height()-80)
    },
    confirmDelete: function () { // 删除提示
        $(".delete").click(function () {
            var flag = confirm("确定要删除该项?")
            return flag
        })
    }
}

4.配置路由

在routes/adminRouters.go下配置角色路由
package routers

import (
    "goshop/controllers/admin"
    "goshop/middlewares"
    "github.com/gin-gonic/gin"
)

//设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {
    //路由分组: 配置全局中间件:middlewares.InitMiddleware
    adminRouters := r.Group("/admin", middlewares.InitAdminAuthMiddleware)
    {
        //后台首页
        adminRouters.GET("/", admin.MainController{}.Index)
        adminRouters.GET("/welcome", admin.MainController{}.Welcome)

        //登录页面
        adminRouters.GET("/login", admin.LoginController{}.Index) // 实例化控制器,并访问其中方法
        adminRouters.POST("/doLogin", admin.LoginController{}.DoIndex)
        adminRouters.GET("/loginOut", admin.LoginController{}.LoginOut)

        //验证码
        adminRouters.GET("/captcha", admin.LoginController{}.Captcha)

        //角色路由
        adminRouters.GET("/role", admin.RoleController{}.Index)
        adminRouters.GET("/role/add", admin.RoleController{}.Add)
        adminRouters.POST("/role/doAdd", admin.RoleController{}.DoAdd)
        adminRouters.GET("/role/edit", admin.RoleController{}.Edit)
        adminRouters.POST("/role/doEdit", admin.RoleController{}.DoEdit)
        adminRouters.GET("/role/delete", admin.RoleController{}.Delete)
    }
}

5.界面展示如下

列表

[golang gin框架] 14.Gin 商城项目-RBAC管理_第3张图片

增加

[golang gin框架] 14.Gin 商城项目-RBAC管理_第4张图片

编辑

[golang gin框架] 14.Gin 商城项目-RBAC管理_第5张图片

删除

[golang gin框架] 14.Gin 商城项目-RBAC管理_第6张图片

六. 管理员的增、删、改、查以及管理员和角色关联

  1. 创建管理员模型

在models目录下创建manager.go角色模型
package models

//管理员表

type Manager struct { // 结构体首字母大写, 和数据库表名对应, 默认访问数据表users, 可以设置访问数据表的方法
    Id  int
    Username string
    Password string
    Mobile string
    Email string
    Status int
    RoleId int
    AddTime int
    IsSuper int
    Role Role `gorm:"foreignKey:RoleId;references:Id"`  // 配置关联关系
}

//配置数据库操作的表名称
func (Manager) TableName() string {
    return "manager"
}
  1. 创建管理员控制器

在controllers/admin下创建anagerController.go角色控制器
package admin

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

type ManagerController struct {
    BaseController
}

func (con ManagerController) Index(c *gin.Context) {
    //获取管理员列表,以及关联对应的角色
    managerList := []models.Manager{}
    models.DB.Preload("Role").Find(&managerList)

    c.HTML(http.StatusOK, "admin/manager/index.html", gin.H{
        "managerList": managerList,
    })
}

//添加管理员
func (con ManagerController) Add(c *gin.Context) {
    //获取角色
    roleList := []models.Role{}
    models.DB.Find(&roleList)
    c.HTML(http.StatusOK, "admin/manager/add.html", gin.H{
        "roleList": roleList,
    })
}

//添加管理员:提交
func (con ManagerController) DoAdd(c *gin.Context) {
    //获取角色id,判断是否合法
    roleId, err := models.Int(c.PostForm("role_id"))
    if err != nil {
        con.Error(c, "角色不合法", "/admin/manager/add")
        return
    }
    //获取提交的表单信息
    username := strings.Trim(c.PostForm("username"), " ")
    password := strings.Trim(c.PostForm("password"), " ")
    email := strings.Trim(c.PostForm("email"), " ")
    mobile := strings.Trim(c.PostForm("mobile"), " ")

    //判断用户名和密码是否符合要求
    if len(username) < 2 || len(password) < 6 {
        con.Error(c, "用户名或密码长度不合法", "/admin/manager/add")
        return
    }

    //判断管理员是否存在
    managerList := []models.Manager{}
    models.DB.Where("username = ?", username).Find(&managerList)
    if len(managerList) > 0 {
        con.Error(c, "管理员已存在", "/admin/manager/add")
        return
    }

    //实例化Manager,执行增加管理员
    manager := models.Manager{
        Username: username,
        Password: models.Md5(password),
        Email:    email,
        Mobile:   mobile,
        AddTime:  int(models.GetUnix()),
        RoleId:   roleId,
        Status:   1,
    }
    err = models.DB.Create(&manager).Error
    if err != nil {
        con.Error(c, "添加管理员失败", "/admin/manager/add")
        return
    }
    con.Success(c, "添加管理员成功", "/admin/manager")
}

//编辑管理员
func (con ManagerController) Edit(c *gin.Context) {
    //获取管理员
    id, err := models.Int(c.Query("id"))
    if err != nil {
        con.Error(c, "传入数据错误", "/admin/manager")
        return
    }
    manager := models.Manager{Id: id}
    models.DB.Find(&manager)

    if manager.Username == "" {
        con.Error(c, "管理员#" + models.String(id) + "不存在", "/admin/manager")
        return
    }
    //获取所有角色
    roleList := []models.Role{}
    models.DB.Find(&roleList)

    c.HTML(http.StatusOK, "admin/manager/edit.html", gin.H{
        "manager":  manager,
        "roleList": roleList,
    })
}

//编辑管理员提交
func (con ManagerController) DoEdit(c *gin.Context) {
    //获取管理员id,并判断
    id, err := models.Int(c.PostForm("id"))
    if err != nil {
        con.Error(c, "传入数据错误", "/admin/manager")
        return
    }
    //获取角色id,并判断
    roleId, err2 := models.Int(c.PostForm("role_id"))
    if err2 != nil {
        con.Error(c, "传入数据错误", "/admin/manager")
        return
    }

    //获取提交的表单信息
    username := strings.Trim(c.PostForm("username"), " ")
    password := strings.Trim(c.PostForm("password"), " ")
    email := strings.Trim(c.PostForm("email"), " ")
    mobile := strings.Trim(c.PostForm("mobile"), " ")
    //执行修改
    manager := models.Manager{Id: id}
    models.DB.Find(&manager)
    manager.Username = username
    manager.Email = email
    manager.RoleId = roleId
    manager.Mobile = mobile
    //判断密码, 为空 表示不修改密码
    if password != "" {
        //判断密码长度
        if len(password) < 6 {
            con.Error(c, "密码长度不合法", "/admin/manager/edit?id" + models.String(id))
            return
        }
        manager.Password = models.Md5(password)
    }
    //保存
    err = models.DB.Save(&manager).Error
    if err != nil {
        con.Error(c, "修改数据失败", "/admin/manager/edit?id="+models.String(id))
        return
    }
    con.Success(c, "修改数据成功", "/admin/manager")
}

//删除
func (con ManagerController) Delete(c *gin.Context) {
    //获取提交的表单数据
    id, err := models.Int(c.Query("id"))
    if err != nil {
        con.Error(c, "传入数据错误", "/admin/manager")
        return
    }

    //查询管理员是否存在
    manager := models.Manager{Id: id}
    err = models.DB.Delete(&manager).Error
    if err != nil {
        con.Error(c, "删除数据失败", "/admin/manager")
        return
    }
    con.Success(c, "删除数据成功", "/admin/manager")
}
  1. 创建管理员html

在templates/admin/manager下创建相关html

index.html

{{ define "admin/manager/index.html" }}
    {{ template "admin/public/page_header.html" .}}
    
    
{{range $key,$value := .managerList}} {{end}}
管理员名称 管理员电话 管理员邮箱 管理员角色 创建时间 操作
{{$value.Username}} {{$value.Mobile}} {{$value.Email}} {{$value.Role.Title}} {{UnixToTime $value.AddTime}} 修改 删除
{{end}}

add.html

{{ define "admin/manager/add.html" }}
{{ template "admin/public/page_header.html" .}}
增加管理员
  • 管理员名称:
  • 管理员密码:
  • 管理员电话:
  • 管理员邮箱:
  • 管理员角色:

{{end}}

edit.html

{{ define "admin/manager/edit.html" }}
    {{ template "admin/public/page_header.html" .}}
    
修改管理员
  • 管理员名称:
  • 管理员密码:
  • 管理员电话:
  • 管理员邮箱:
  • 管理员角色:

{{end}}

4.配置路由

在routes/adminRouters.go下配置角色路由
package routers

import (
    "goshop/controllers/admin"
    "goshop/middlewares"
    "github.com/gin-gonic/gin"
)

//设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {
    //路由分组: 配置全局中间件:middlewares.InitMiddleware
    adminRouters := r.Group("/admin", middlewares.InitAdminAuthMiddleware)
    {
        //后台首页
        adminRouters.GET("/", admin.MainController{}.Index)
        adminRouters.GET("/welcome", admin.MainController{}.Welcome)

        //登录页面
        adminRouters.GET("/login", admin.LoginController{}.Index) // 实例化控制器,并访问其中方法
        adminRouters.POST("/doLogin", admin.LoginController{}.DoIndex)
        adminRouters.GET("/loginOut", admin.LoginController{}.LoginOut)

        //验证码
        adminRouters.GET("/captcha", admin.LoginController{}.Captcha)

        //管理员路由
        adminRouters.GET("/manager", admin.ManagerController{}.Index)
        adminRouters.GET("/manager/add", admin.ManagerController{}.Add)
        adminRouters.POST("/manager/doAdd", admin.ManagerController{}.DoAdd)
        adminRouters.GET("/manager/edit", admin.ManagerController{}.Edit)
        adminRouters.POST("/manager/doEdit", admin.ManagerController{}.DoEdit)
        adminRouters.GET("/manager/delete", admin.ManagerController{}.Delete)


        //角色路由
        adminRouters.GET("/role", admin.RoleController{}.Index)
        adminRouters.GET("/role/add", admin.RoleController{}.Add)
        adminRouters.POST("/role/doAdd", admin.RoleController{}.DoAdd)
        adminRouters.GET("/role/edit", admin.RoleController{}.Edit)
        adminRouters.POST("/role/doEdit", admin.RoleController{}.DoEdit)  
    }
}

5.界面展示如下

列表

[golang gin框架] 14.Gin 商城项目-RBAC管理_第7张图片

增加

[golang gin框架] 14.Gin 商城项目-RBAC管理_第8张图片

编辑

[golang gin框架] 14.Gin 商城项目-RBAC管理_第9张图片

删除

[golang gin框架] 14.Gin 商城项目-RBAC管理_第10张图片

七. 权限的增、删、改、查以及权限的自关联

  1. 创建权限模型

在models目录下创建.go角色模型
package models

//权限模型

type Access struct {
    Id          int
    ModuleName  string //模块名称
    ActionName  string //操作名称
    Type        int    //节点类型 :  1、表示模块    2、表示菜单     3、操作
    Url         string //路由跳转地址
    ModuleId    int    //此module_id和当前模型的id关联       module_id= 0 表示模块
    Sort        int
    Description string
    Status      int
    AddTime     int
    AccessItem  []Access `gorm:"foreignKey:ModuleId;references:Id"`  // 表的自关联,获取该数据的子分类

    Checked     bool     `gorm:"-"` // 用户是否有该权, 忽略本字段,给struct加一个自定义属性,和数据库没有关系
}

func (Access) TableName() string {
    return "access"
}
  1. 创建权限控制器

在controllers/admin下创建AccessController.go角色控制器
package admin

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

type AccessController struct {
    BaseController
}

func (con AccessController) Index(c *gin.Context) {
    //获取权限列表
    accessList := []models.Access{}
    models.DB.Where("module_id = ?", 0).Preload("AccessItem").Find(&accessList)
    c.HTML(http.StatusOK, "admin/access/index.html", gin.H{
        "accessList": accessList,
    })
}

func (con AccessController) Add(c *gin.Context) {
    //获取顶级模块
    accessList := []models.Access{}
    models.DB.Where("module_id = ?", 0).Find(&accessList)

    c.HTML(http.StatusOK, "admin/access/add.html", gin.H{
        "accessList": accessList,
    })
}

func (con AccessController) DoAdd(c *gin.Context) {
    //获取表单数据
    moduleName := strings.Trim(c.PostForm("module_name"), " ")
    actionName := strings.Trim(c.PostForm("action_name"), " ")
    accessType, err1 := models.Int(c.PostForm("type"))
    url := c.PostForm("url")
    moduleId, err2 := models.Int(c.PostForm("module_id"))
    sort, err3 := models.Int(c.PostForm("sort"))
    status, err4 := models.Int(c.PostForm("status"))
    description := strings.Trim(c.PostForm("description"), " ")
    //判断err
    if err1 != nil || err2 != nil || err3 != nil || err4 != nil {
        con.Error(c, "传入参数错误", "/admin/access/add")
        return
    }
    //判断moduleName
    if moduleName == "" {
        con.Error(c, "模块名称不能为空", "/admin/access/add")
        return
    }
    //实例化access
    access := models.Access{
        ModuleName:  moduleName,
        ActionName:  actionName,
        Type:        accessType,
        Url:         url,
        ModuleId:    moduleId,
        Sort:        sort,
        Status:      status,
        Description: description,
    }
    err5 := models.DB.Create(&access).Error
    if err5 != nil {
        con.Error(c, "添加权限失败", "/admin/access/add")
        return
    }
    con.Success(c, "添加权限成功", "/admin/access")
}

//编辑
func (con AccessController) Edit(c *gin.Context) {
    //获取id
    id, err := models.Int(c.Query("id"))
    if err != nil {
        con.Error(c, "传入数据错误", "/admin/access")
        return
    }
    access := models.Access{Id: id}
    models.DB.Find(&access)

    //获取顶级模块
    accessList := []models.Access{}
    models.DB.Where("module_id = ?", 0).Find(&accessList)

    c.HTML(http.StatusOK, "admin/access/edit.html", gin.H{
        "access":     access,
        "accessList": accessList,
    })
}

//编辑:提交
func (con AccessController) DoEdit(c *gin.Context) {
    //获取提交的表单数据
    id, err := models.Int(c.PostForm("id"))
    if err != nil {
        con.Error(c, "传入数据错误", "/admin/access/edit?id"+models.String(id))
        return
    }
    //获取表单数据
    moduleName := strings.Trim(c.PostForm("module_name"), " ")
    actionName := strings.Trim(c.PostForm("action_name"), " ")
    accessType, err1 := models.Int(c.PostForm("type"))
    url := c.PostForm("url")
    moduleId, err2 := models.Int(c.PostForm("module_id"))
    sort, err3 := models.Int(c.PostForm("sort"))
    status, err4 := models.Int(c.PostForm("status"))
    description := strings.Trim(c.PostForm("description"), " ")
    //判断err
    if err1 != nil || err2 != nil || err3 != nil || err4 != nil {
        con.Error(c, "传入数据错误", "/admin/access/edit?id"+models.String(id))
        return
    }
    //判断moduleName
    if moduleName == "" {
        con.Error(c, "模块名称不能为空", "/admin/access/edit?id"+models.String(id))
        return
    }

    //获取要修改的数据
    access := models.Access{Id: id}
    models.DB.Find(&access)
    access.ModuleName = moduleName
    access.ActionName = actionName
    access.Type = accessType
    access.Url = url
    access.ModuleId = moduleId
    access.Sort = sort
    access.Status = status
    access.Description = description
    //保存
    err5 := models.DB.Save(&access).Error
    if err5 != nil {
        con.Error(c, "编辑权限失败", "/admin/access/edit?id"+models.String(id))
        return
    }
    con.Success(c, "编辑权限成功", "/admin/access")
}

//删除
func (con AccessController) Delete(c *gin.Context) {
    //获取提交的表单数据
    id, err := models.Int(c.Query("id"))
    if err != nil {
        con.Error(c, "传入数据错误", "/admin/access")
        return
    }

    //获取要删除的数据
    access := models.Access{Id: id}
    models.DB.Find(&access)
    if access.ModuleId == 0 { // 顶级模块
        accessList := []models.Access{}
        models.DB.Where("module_id = ? ", access.Id).Find(&accessList)
        if len(accessList) > 0 {
            con.Error(c, "当前模块下子菜单,请先删除子菜单后再来删除这个数据", "/admin/access")
            return
        }
    }
    // 操作 或者 菜单, 或者顶级模块下面没有子菜单, 可以直接删除
    err = models.DB.Delete(&access).Error
    if err != nil {
        con.Error(c, "删除数据失败", "/admin/access")
        return
    }
    con.Success(c, "删除数据成功", "/admin/access")
}
  1. 创建权限html

在templates/admin/access下创建权限相关html

index.html


{{ define "admin/access/index.html" }}
{{ template "admin/public/page_header.html" .}}
               
    
    
{{range $key,$value := .accessList}} {{range $k,$v := $value.AccessItem}} {{end}} {{end}}
模块名称 节点类型 操作名称 操作地址 排序 描述 操作
{{$value.ModuleName}} {{if eq $value.Type 1}} 模块 {{else if eq $value.Type 2}} 菜单 {{else}} 操作 {{end}} {{$value.ActionName}} {{$value.Url}} {{$value.Sort}} {{$value.Description}} 修改 删除
--{{$v.ModuleName}} {{if eq $v.Type 1}} 模块 {{else if eq $v.Type 2}} 菜单 {{else}} 操作 {{end}} {{$v.ActionName}} {{$v.Url}} {{$v.Sort}} {{$v.Description}} 修改 删除
{{end}}

add.html

{{ define "admin/access/add.html" }}
{{ template "admin/public/page_header.html" .}}

增加权限
  • 模块名称:
  • 节点类型:
  • 操作名称:
  • 操作地址:
  • 所属模块:
  • 排  序:
  • 描  述 :
  • 状  态:  

{{end}}

edit.html

{{ define "admin/access/edit.html" }}
{{ template "admin/public/page_header.html" .}}

修改权限
  • 模块名称:
  • 节点类型:
  • 操作名称:
  • 操作地址:
  • 所属模块:
  • 排  序:
  • 描  述 :
  • 状  态:  

{{end}}
  1. 配置路由

在routes/adminRouters.go下配置权限路由
package routers

import (
    "goshop/controllers/admin"
    "goshop/middlewares"
    "github.com/gin-gonic/gin"
)

//设置admin后台路由
func AdminRoutersInit(r *gin.Engine) {
    //路由分组: 配置全局中间件:middlewares.InitMiddleware
    adminRouters := r.Group("/admin", middlewares.InitAdminAuthMiddleware)
    {
        //后台首页
        adminRouters.GET("/", admin.MainController{}.Index)
        adminRouters.GET("/welcome", admin.MainController{}.Welcome)

        //登录页面
        adminRouters.GET("/login", admin.LoginController{}.Index) // 实例化控制器,并访问其中方法
        adminRouters.POST("/doLogin", admin.LoginController{}.DoIndex)
        adminRouters.GET("/loginOut", admin.LoginController{}.LoginOut)

        //验证码
        adminRouters.GET("/captcha", admin.LoginController{}.Captcha)

        //管理员路由
        adminRouters.GET("/manager", admin.ManagerController{}.Index)
        adminRouters.GET("/manager/add", admin.ManagerController{}.Add)
        adminRouters.POST("/manager/doAdd", admin.ManagerController{}.DoAdd)
        adminRouters.GET("/manager/edit", admin.ManagerController{}.Edit)
        adminRouters.POST("/manager/doEdit", admin.ManagerController{}.DoEdit)
        adminRouters.GET("/manager/delete", admin.ManagerController{}.Delete)

        //角色路由
        adminRouters.GET("/role", admin.RoleController{}.Index)
        adminRouters.GET("/role/add", admin.RoleController{}.Add)
        adminRouters.POST("/role/doAdd", admin.RoleController{}.DoAdd)
        adminRouters.GET("/role/edit", admin.RoleController{}.Edit)
        adminRouters.POST("/role/doEdit", admin.RoleController{}.DoEdit)
        adminRouters.GET("/role/delete", admin.RoleController{}.Delete)

        //权限路由
        adminRouters.GET("/access", admin.AccessController{}.Index)
        adminRouters.GET("/access/add", admin.AccessController{}.Add)
        adminRouters.POST("/access/doAdd", admin.AccessController{}.DoAdd)
        adminRouters.GET("/access/edit", admin.AccessController{}.Edit)
        adminRouters.POST("/access/doEdit", admin.AccessController{}.DoEdit)
        adminRouters.GET("/access/delete", admin.AccessController{}.Delete
    }
}
  1. 界面展示

列表

[golang gin框架] 14.Gin 商城项目-RBAC管理_第11张图片

增加

[golang gin框架] 14.Gin 商城项目-RBAC管理_第12张图片

编辑

[golang gin框架] 14.Gin 商城项目-RBAC管理_第13张图片

删除

[golang gin框架] 14.Gin 商城项目-RBAC管理_第14张图片

八.角色和权限关联,角色授权,已授权的权限选中

1.在角色列表中增加'授权'操作

[golang gin框架] 14.Gin 商城项目-RBAC管理_第15张图片
在角色index.html中增加授权连接代码,代码如下:
 
     授权
     修改
     删除

2.授权操作,以及已授权的权限选中

[golang gin框架] 14.Gin 商城项目-RBAC管理_第16张图片
在角色html模板文件下创建auth.html页面
{{ define "admin/role/auth.html" }}
    {{ template "admin/public/page_header.html" .}}
    
角色授权
{{range $key, $value := .accessList}} {{end}}
{{range $k,$v := $value.AccessItem}}       {{end}}
{{ end }}

3.增加授权方法

在角色控制器中增加授权方法

//授权
func (con RoleController) Auth(c *gin.Context) {
    //获取id
    id, err := models.Int(c.Query("id"))
    if err != nil {
        con.Error(c, "传入数据错误", "/admin/role")
        return
    }
    role := models.Role{Id: id}
    models.DB.Find(&role)

    //获取所有权限列表
    accessList := []models.Access{}
    models.DB.Where("module_id = ?", 0).Preload("AccessItem").Find(&accessList)

    //获取当前角色拥有的权限,并把权限id放在一个map对象中
    roleAccess := []models.RoleAccess{}
    models.DB.Where("role_id = ?", id).Find(&roleAccess)
    roleAccessMap := make(map[int]int)
    for _, v := range roleAccess {
        roleAccessMap[v.AccessId] = v.AccessId
    }

    //循环遍历所有权限数据,判断当前权限的id是否在角色权限的map对象中,如果是的话给当前数据加入checked属性
    for i := 0; i < len(accessList); i++ { //循环权限列表
        if _, ok := roleAccessMap[accessList[i].Id]; ok { // 判断当前权限是否在角色权限的map对象中
            accessList[i].Checked = true
        }
        for j := 0; j < len(accessList[i].AccessItem); j++ { // 判断当前权限的子栏位是否在角色权限的map中
            if _, ok := roleAccessMap[accessList[i].AccessItem[j].Id]; ok { // 判断当前权限是否在角色权限的map对象中
                accessList[i].AccessItem[j].Checked = true
            }
        }
    }
    c.HTML(http.StatusOK, "admin/role/auth.html", gin.H{
        "roleId":     id,
        "accessList": accessList,
    })
}

//授权提交
func (con RoleController) DoAuth(c *gin.Context) {
    //获取提交的表单数据
    roleId, err := models.Int(c.PostForm("role_id"))
    if err != nil {
        con.Error(c, "传入数据错误", "/admin/role")
        return
    }
    //获取表单提交的权限id切片
    accessIds := c.PostFormArray("access_node[]")
    //先删除当前角色对应的权限
    roleAccess := models.RoleAccess{}
    models.DB.Where("role_id = ?", roleId).Delete(&roleAccess)

    //循环遍历accessIds,增加当前角色对应的权限
    for _, v := range accessIds {
        roleAccess.RoleId = roleId
        accessId, _ := models.Int(v)
        roleAccess.AccessId = accessId
        models.DB.Create(&roleAccess)
    }

    con.Success(c, "角色授权成功", "/admin/role")
}

4.配置路由

在routes/adminRouters.go下配置授权路由
adminRouters.GET("/role/auth", admin.RoleController{}.Auth)
adminRouters.POST("/role/doAuth", admin.RoleController{}.DoAuth)

九.用户权限判断

登录后显示用户名称、根据用户的权限动态显示左侧菜单
判断当前登录用户的权限 、没有权限访问则拒绝

1.后台首页MainControllers.go修改

package admin

// 进入后台系统首页

import (
    "encoding/json"
    "github.com/gin-contrib/sessions"
    "github.com/gin-gonic/gin"
    "gorm.io/gorm"
    "goshop/models"
    "net/http"
)

type MainController struct {
}

func (con MainController) Index(c *gin.Context) {
    //获取Session里面保存的用户信息
    session := sessions.Default(c)
    userinfo := session.Get("userinfo")
    //判断Session中的用户信息是否存在,如果不存在跳转到登录页面(注意需要判断) 如果存在继续向下执行
    //session.Get获取返回的结果是一个空接口类型,所以需要进行类型断言: 判断userinfo是不是一个string
    userinfoStr, ok := userinfo.(string)
    if ok { // 说明是一个string
        //1.获取用户信息
        var userinfoStruct []models.Manager
        //把获取到的用户信息转换结构体
        json.Unmarshal([]byte(userinfoStr), &userinfoStruct)

        //获取所有权限列表
        accessList := []models.Access{}
        models.DB.Where("module_id = ?", 0).Preload("AccessItem", func(db *gorm.DB) *gorm.DB {
            return db.Order("access.sort DESC")
        }).Order("sort DESC").Find(&accessList)

        //获取当前角色拥有的权限,并把权限id放在一个map对象中
        roleAccess := []models.RoleAccess{}
        models.DB.Where("role_id = ?", userinfoStruct[0].RoleId).Find(&roleAccess)
        roleAccessMap := make(map[int]int)
        for _, v := range roleAccess {
            roleAccessMap[v.AccessId] = v.AccessId
        }

        //循环遍历所有权限数据,判断当前权限的id是否在角色权限的map对象中,如果是的话给当前数据加入checked属性
        for i := 0; i < len(accessList); i++ { //循环权限列表
            if _, ok := roleAccessMap[accessList[i].Id]; ok { // 判断当前权限是否在角色权限的map对象中
                accessList[i].Checked = true
            }
            for j := 0; j < len(accessList[i].AccessItem); j++ { // 判断当前权限的子栏位是否在角色权限的map中
                if _, ok := roleAccessMap[accessList[i].AccessItem[j].Id]; ok { // 判断当前权限是否在角色权限的map对象中
                    accessList[i].AccessItem[j].Checked = true
                }
            }
        }

        c.HTML(http.StatusOK, "admin/main/index.html", gin.H{
            "username": userinfoStruct[0].Username,
            "isSuper": userinfoStruct[0].IsSuper,  // 是否超级管理员, 超级管理员显示全部
            "accessList": accessList,
        })
    } else {
        c.Redirect(http.StatusFound, "/admin/login")
    }
}

func (con MainController) Welcome(c *gin.Context) {
    c.HTML(http.StatusOK, "admin/main/welcome.html", gin.H{})
}

2.后台首页templates/main.index.html展示

{{ define "admin/main/index.html" }}
{{ template "admin/public/page_header.html" .}}




    
    













{{ template "admin/public/page_aside.html" .}}
{{ end }}

3.admin/public/page_header.html


{{ define "admin/public/page_header.html" }}
    
    
    
        
        
    
    
    

    

    
    
    

    
{{end}}

4.左侧菜单栏admin/public/page_aside.html


{{ define "admin/public/page_aside.html" }}
    
    {{$isSuper := .isSuper}} {{range $key, $value := .accessList}} {{if eq $isSuper 1}}
  • {{$value.ModuleName}}

  • {{else}} {{if eq $value.Checked true}}
  • {{$value.ModuleName}}

      {{range $k,$v := $value.AccessItem}} {{if eq $v.Checked true}} {{if eq $v.Type 2}}
    • {{$v.ActionName}}
    • {{end}} {{end}} {{end}}
  • {{end}} {{end}} {{end}}
{{end}}

5.权限模型代码修改

package models

//权限模型

type Access struct {
    Id          int
    ModuleName  string //模块名称
    ActionName  string //操作名称
    Type        int    //节点类型 :  1、表示模块    2、表示菜单     3、操作
    Url         string //路由跳转地址
    ModuleId    int    //此module_id和当前模型的id关联       module_id= 0 表示模块
    Sort        int
    Description string
    Status      int
    AddTime     int
    AccessItem  []Access `gorm:"foreignKey:ModuleId;references:Id"`  // 表的自关联,获取该数据的子分类

    Checked     bool     `gorm:"-"` // 用户是否有该权, 忽略本字段,给struct加一个自定义属性,和数据库没有关系
}

func (Access) TableName() string {
    return "access"
}

6.中间件middlewares/adminAuth.go修改

package middlewares

//中间件: 作用: 在执行路由之前或者之后进行相关逻辑判断

import (
    "encoding/json"
    "fmt"
    "github.com/gin-contrib/sessions"
    "github.com/gin-gonic/gin"
    "gopkg.in/ini.v1"
    "goshop/models"
    "net/http"
    "os"
    "strings"
)

func InitAdminAuthMiddleware(c *gin.Context) {
    //权限判断: 没有登录的用户不能进入后台管理中心

    //1、获取Url访问的地址
    //当地址后面带参数时:,如: admin/captcha?t=0.8706946438889653,需要处理
    //strings.Split(c.Request.URL.String(), "?"): 把c.Request.URL.String()请求地址按照?分割成切片
    pathname := strings.Split(c.Request.URL.String(), "?")[0]

    //2、获取Session里面保存的用户信息
    session := sessions.Default(c)
    userinfo := session.Get("userinfo")
    //3、判断Session中的用户信息是否存在,如果不存在跳转到登录页面(注意需要判断) 如果存在继续向下执行
    //session.Get获取返回的结果是一个空接口类型,所以需要进行类型断言: 判断userinfo是不是一个string
    userinfoStr, ok := userinfo.(string)
    if ok { // 说明是一个string
        var userinfoStruct []models.Manager
        //把获取到的用户信息转换结构体
        err := json.Unmarshal([]byte(userinfoStr), &userinfoStruct)
        if err != nil || !(len(userinfoStruct) > 0 && userinfoStruct[0].Username != "") {
            if pathname != "/admin/login" && pathname != "/admin/dologin" && pathname != "/admin/captcha" {
                //跳转到登录页面
                c.Redirect(http.StatusFound, "/admin/login")
            }
        } else { //表示用户登录成功
            //获取当前访问的URL对应的权限id,判断权限id是否在角色对应的权限中
            // strings.Replace 字符串替换
            urlPath := strings.Replace(pathname, "/admin/", "", 1)
            //排除权限判断:不是超级管理员并且不在相关权限内
            if userinfoStruct[0].IsSuper == 0 && !excludeAuthPath("/" + urlPath){
                //判断用户权限:当前用户权限是否可以访问url地址
                //获取当前角色拥有的权限,并把权限id放在一个map对象中
                roleAccess := []models.RoleAccess{}
                models.DB.Where("role_id = ?", userinfoStruct[0].RoleId).Find(&roleAccess)
                roleAccessMap := make(map[int]int)
                for _, v := range roleAccess {
                    roleAccessMap[v.AccessId] = v.AccessId
                }
                //实例化access
                access := models.Access{}
                //查询权限id
                models.DB.Where("url = ? ", urlPath).Find(&access)
                //判断权限id是否在角色对应的权限中
                if _, ok := roleAccessMap[access.Id]; !ok {
                    c.String(http.StatusOK, "没有权限")
                    c.Abort() // 终止程序
                }
            }
        }
    } else {
        //4、如果Session不存在,判断当前访问的URl是否是login doLogin captcha,如果不是跳转到登录页面,如果是不行任何操作
        //说明用户没有登录
        //需要排除到不需要做权限判断的路由
        if pathname != "/admin/login" && pathname != "/admin/dologin" && pathname != "/admin/captcha" {
            //跳转到登录页面
            c.Redirect(http.StatusFound, "/admin/login")
        }
    }
}

//排除权限判断的方法
func excludeAuthPath(urlPath string) bool {
    //加载配置文件
    cfg, err := ini.Load("./conf/app.ini")
    if err != nil {
        fmt.Printf("Fail to read file: %v", err)
        os.Exit(1)
    }
    //获取需要排除的地址
    excludeAuthPath := cfg.Section("").Key("excludeAuthPath").String()
    //拆分字符串成为一个切片
    excludeAuthPathSlice := strings.Split(excludeAuthPath, ",")
    //判断传入的地址是否在排除地址内
    for _, v := range excludeAuthPathSlice {
        if v == urlPath {
            return true
        }
    }
    return false
}

[上一节][golang gin框架] 13.Gin 商城项目-配置公共基类实现公共的成功,失败提示页面 用户登录、退出登录、以及权限判断

[下一节][golang gin框架] 15.Gin 商城项目-封装上传图片方法,轮播图的增删改查以及异步修改状态,数量

你可能感兴趣的:(golang,#,gin框架开发,gin,rbac)