Quellcode durchsuchen

权限管理完成

pixel vor 5 Jahren
Ursprung
Commit
d9a100be97

+ 21 - 4
QMPlusServer/controller/api/api.go

@@ -57,7 +57,7 @@ func DeleteApi(c *gin.Context) {
 
 type AuthAndPathIn struct {
 	AuthorityId string        `json:"authorityId"`
-	Apis        []dbModel.Api     `json:"apis"`
+	ApiIds        []uint     `json:"apiIds"`
 }
 
 // @Tags Api
@@ -71,7 +71,7 @@ type AuthAndPathIn struct {
 func SetAuthAndApi(c *gin.Context) {
 	var authAndPathIn AuthAndPathIn
 	_ = c.BindJSON(&authAndPathIn)
-	err := new(dbModel.ApiAuthority).SetAuthAndApi(authAndPathIn.AuthorityId, authAndPathIn.Apis)
+	err := new(dbModel.ApiAuthority).SetAuthAndApi(authAndPathIn.AuthorityId, authAndPathIn.ApiIds)
 	if err != nil {
 		servers.ReportFormat(c, false, fmt.Sprintf("添加失败:%v", err), gin.H{})
 	} else {
@@ -136,7 +136,6 @@ func GetApiById(c *gin.Context) {
 // @Param data body api.CreateApiParams true "创建api"
 // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
 // @Router /api/updataApi [post]
-
  func UpdataApi(c *gin.Context) {
 	 var api dbModel.Api
 	 _ = c.BindJSON(&api)
@@ -146,4 +145,22 @@ func GetApiById(c *gin.Context) {
 	 } else {
 		 servers.ReportFormat(c, true, "修改数据成功", gin.H{})
 	 }
- }
+ }
+
+// @Tags Api
+// @Summary 获取所有的Api 不分页
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
+// @Router /api/getAllApis [post]
+func GetAllApis(c *gin.Context){
+	err,apis := new(dbModel.Api).GetAllApis()
+	if err != nil {
+		servers.ReportFormat(c, false, fmt.Sprintf("获取数据失败,%v", err), gin.H{})
+	} else {
+		servers.ReportFormat(c, true, "获取数据成功", gin.H{
+			"apis":     apis,
+		})
+	}
+}

+ 26 - 0
QMPlusServer/controller/api/authority.go

@@ -82,4 +82,30 @@ func GetAuthorityList(c *gin.Context){
 			"pageSize": pageInfo.PageSize,
 		})
 	}
+}
+
+
+type GetAuthorityId struct {
+	AuthorityId string `json:"authorityId"`
+}
+
+// @Tags authority
+// @Summary 获取本角色所有有权限的apiId
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body api.GetAuthorityId true "获取本角色所有有权限的apiId"
+// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
+// @Router /authority/getAuthAndApi [post]
+func GetAuthAndApi(c *gin.Context){
+	var idInfo GetAuthorityId
+	_ = c.BindJSON(&idInfo)
+	err,apis := new(dbModel.ApiAuthority).GetAuthAndApi(idInfo.AuthorityId)
+	if err != nil {
+		servers.ReportFormat(c, false, fmt.Sprintf("获取数据失败,%v", err), gin.H{})
+	} else {
+		servers.ReportFormat(c, true, "获取数据成功", gin.H{
+			"apis": apis,
+		})
+	}
 }

+ 3 - 3
QMPlusServer/controller/api/menu.go

@@ -138,7 +138,7 @@ type IdInfo struct {
 }
 
 // @Tags menu
-// @Summary 获取指定角色menu
+// @Summary 删除菜单
 // @Security ApiKeyAuth
 // @accept application/json
 // @Produce application/json
@@ -157,11 +157,11 @@ func DeleteBaseMenu(c *gin.Context) {
 }
 
 // @Tags menu
-// @Summary 新菜单
+// @Summary 新菜单
 // @Security ApiKeyAuth
 // @accept application/json
 // @Produce application/json
-// @Param data body dbModel.BaseMenu true "新菜单"
+// @Param data body dbModel.BaseMenu true "新菜单"
 // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
 // @Router /menu/updataBaseMen [post]
 func UpdataBaseMenu(c *gin.Context) {

+ 17 - 0
QMPlusServer/middleware/jwt.go

@@ -2,13 +2,22 @@ package middleware
 
 import (
 	"errors"
+	"fmt"
 	"github.com/dgrijalva/jwt-go"
 	"github.com/gin-gonic/gin"
 	uuid "github.com/satori/go.uuid"
 	"main/controller/servers"
+	"main/init/qmsql"
 	"time"
 )
 
+type SqlRes struct {
+	Path string
+	AuthorityId string
+	ApiId  uint
+	Id    uint
+}
+
 func JWTAuth() gin.HandlerFunc {
 	return func(c *gin.Context) {
 		// 我们这里jwt鉴权取头部信息 x-token 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localSstorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录
@@ -31,6 +40,14 @@ func JWTAuth() gin.HandlerFunc {
 			c.Abort()
 			return
 		}
+		var sqlRes SqlRes
+		 row:=qmsql.DEFAULTDB.Raw("SELECT apis.path,api_authorities.authority_id,api_authorities.api_id,apis.id FROM apis INNER JOIN api_authorities ON api_authorities.api_id = apis.id 	WHERE apis.path = ? AND	api_authorities.authority_id = ?",c.Request.RequestURI,claims.AuthorityId)
+		err=row.Scan(&sqlRes).Error
+		if(fmt.Sprintf("%v",err) == "record not found"){
+			servers.ReportFormat(c, false, "没有Api操作权限", gin.H{})
+			c.Abort()
+			return
+		}
 		c.Set("claims", claims)
 	}
 }

+ 8 - 1
QMPlusServer/model/dbModel/api.go

@@ -12,6 +12,7 @@ type Api struct {
 	gorm.Model
 	Path        string `json:"path"`
 	Description string `json:"description"`
+	Group       string `json:"group"`
 }
 
 func (a *Api) CreateApi() (err error) {
@@ -38,6 +39,12 @@ func (a *Api) GetApiById(id float64)(err error,api Api){
 	err = qmsql.DEFAULTDB.Where("id = ?",id).First(&api).Error
 	return
 }
+// 获取所有api信息
+func (a *Api)GetAllApis()(err error,apis []Api){
+	err = qmsql.DEFAULTDB.Find(&apis).Error
+	return
+}
+
 
 // 分页获取数据  需要分页实现这个接口即可
 func (a *Api) GetInfoList(info modelInterface.PageInfo) (err error, list interface{}, total int) {
@@ -47,7 +54,7 @@ func (a *Api) GetInfoList(info modelInterface.PageInfo) (err error, list interfa
 		return
 	} else {
 		var apiList []Api
-		err = db.Find(&apiList).Error
+		err = db.Order("group").Find(&apiList).Error
 		return err, apiList, total
 	}
 }

+ 14 - 4
QMPlusServer/model/dbModel/api_authority.go

@@ -14,13 +14,23 @@ type ApiAuthority struct {
 }
 
 //创建角色api关联关系
-func (a *ApiAuthority) SetAuthAndApi(authId string, apis []Api) (err error) {
-	err = qmsql.DEFAULTDB.Where("authority_id = ?", authId).Delete(&ApiAuthority{}).Error
-	for _, v := range apis {
-		err = qmsql.DEFAULTDB.Create(&ApiAuthority{AuthorityId: authId, ApiId: v.ID}).Error
+func (a *ApiAuthority) SetAuthAndApi(authId string, apisid []uint) (err error) {
+	err = qmsql.DEFAULTDB.Where("authority_id = ?", authId).Unscoped().Delete(&ApiAuthority{}).Error
+	for _, v := range apisid {
+		err = qmsql.DEFAULTDB.Create(&ApiAuthority{AuthorityId: authId, ApiId: v}).Error
 		if err != nil {
 			return err
 		}
 	}
 	return nil
 }
+
+// 获取角色api关联关系
+func (a *ApiAuthority) GetAuthAndApi(authId string) (err error,apiIds []uint) {
+	var apis []ApiAuthority
+	err = qmsql.DEFAULTDB.Where("authority_id = ?", authId).Find(&apis).Error
+	for _, v := range apis {
+		apiIds = append(apiIds,v.ApiId)
+	}
+	return nil,apiIds
+}

+ 3 - 1
QMPlusServer/router/api.go

@@ -11,9 +11,11 @@ func InitApiRouter(Router *gin.Engine) {
 	{
 		ApiRouter.POST("createApi", api.CreateApi)  //创建Api
 		ApiRouter.POST("deleteApi", api.DeleteApi)  //删除Api
-		ApiRouter.POST("setAuthAndPath",api.SetAuthAndApi) // 设置api和角色关系
+		ApiRouter.POST("setAuthAndApi",api.SetAuthAndApi) // 设置api和角色关系
 		ApiRouter.POST("getApiList",api.GetApiList)  //获取Api列表
 		ApiRouter.POST("getApiById",api.GetApiById)  //获取单条Api消息
 		ApiRouter.POST("updataApi",api.UpdataApi)   //更新api
+		ApiRouter.POST("getAllApis",api.GetAllApis) // 获取所有api
+		ApiRouter.POST("getAuthAndApi",api.GetAuthAndApi) // 获取api和auth关系
 	}
 }

+ 3 - 3
QMPlusServer/router/authority.go

@@ -9,8 +9,8 @@ import (
 func InitAuthorityRouter(Router *gin.Engine) {
 	AuthorityRouter := Router.Group("authority").Use(middleware.JWTAuth())
 	{
-		AuthorityRouter.POST("createAuthority", api.CreateAuthority)
-		AuthorityRouter.POST("deleteAuthority", api.DeleteAuthority)
-		AuthorityRouter.POST("getAuthorityList",api.GetAuthorityList)
+		AuthorityRouter.POST("createAuthority", api.CreateAuthority) //创建角色
+		AuthorityRouter.POST("deleteAuthority", api.DeleteAuthority) //删除角色
+		AuthorityRouter.POST("getAuthorityList",api.GetAuthorityList) //获取角色列表
 	}
 }

+ 9 - 9
QMPlusServer/router/menu.go

@@ -9,14 +9,14 @@ import (
 func InitMenuRouter(Router *gin.Engine) {
 	MenuRouter := Router.Group("menu").Use(middleware.JWTAuth())
 	{
-		MenuRouter.POST("getMenu", api.GetMenu)
-		MenuRouter.POST("getMenuList", api.GetMenuList)
-		MenuRouter.POST("addBaseMenu", api.AddBaseMenu)
-		MenuRouter.POST("getBaseMenuTree", api.GetBaseMenuTree)
-		MenuRouter.POST("addMenuAuthority", api.AddMenuAuthority)
-		MenuRouter.POST("getMenuAuthority", api.GetMenuAuthority)
-		MenuRouter.POST("deleteBaseMenu", api.DeleteBaseMenu)
-		MenuRouter.POST("updataBaseMenu", api.UpdataBaseMenu)
-		MenuRouter.POST("getBaseMenuById", api.GetBaseMenuById)
+		MenuRouter.POST("getMenu", api.GetMenu) //获取菜单树
+		MenuRouter.POST("getMenuList", api.GetMenuList) // 分页获取基础menu列表
+		MenuRouter.POST("addBaseMenu", api.AddBaseMenu) // 新增菜单
+		MenuRouter.POST("getBaseMenuTree", api.GetBaseMenuTree) // 获取用户动态路由
+		MenuRouter.POST("addMenuAuthority", api.AddMenuAuthority) //	增加menu和角色关联关系
+		MenuRouter.POST("getMenuAuthority", api.GetMenuAuthority) // 获取指定角色menu
+		MenuRouter.POST("deleteBaseMenu", api.DeleteBaseMenu) // 删除菜单
+		MenuRouter.POST("updataBaseMenu", api.UpdataBaseMenu) // 更新菜单
+		MenuRouter.POST("getBaseMenuById", api.GetBaseMenuById) //根据id获取菜单
 	}
 }

+ 3 - 3
QMPlusServer/router/user.go

@@ -9,8 +9,8 @@ import (
 func InitUserRouter(Router *gin.Engine) {
 	UserRouter := Router.Group("user").Use(middleware.JWTAuth())
 	{
-		UserRouter.POST("changePassword", api.ChangePassword)
-		UserRouter.POST("uploadHeaderImg", api.UploadHeaderImg)
-		UserRouter.POST("getInfoList", api.GetInfoList)
+		UserRouter.POST("changePassword", api.ChangePassword) // 修改密码
+		UserRouter.POST("uploadHeaderImg", api.UploadHeaderImg) //上传头像
+		UserRouter.POST("getInfoList", api.GetInfoList) // 分页获取用户列表
 	}
 }

+ 48 - 2
QMPlusVuePage/src/api/api.js

@@ -70,8 +70,6 @@ export const updataApi = (data) => {
     })
 }
 
-
-
 // @Tags Api
 // @Summary 更新api
 // @Security ApiKeyAuth
@@ -86,4 +84,52 @@ export const setAuthApi = (data) => {
         method: 'post',
         data
     })
+}
+
+// @Tags Api
+// @Summary 获取所有的Api 不分页
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
+// @Router /api/getAllApis [post]
+export const getAllApis = (data) => {
+    return service({
+        url: "/api/getAllApis",
+        method: 'post',
+        data
+    })
+}
+
+// @Tags authority
+// @Summary 获取本角色所有有权限的apiId
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body api.GetAuthorityId true "获取本角色所有有权限的apiId"
+// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
+// @Router /authority/getAuthAndApi [post]
+export const getAuthAndApi = (data) => {
+    return service({
+        url: "/api/getAuthAndApi",
+        method: 'post',
+        data
+    })
+}
+
+
+// @Tags Api
+// @Summary 创建api和角色关系
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body api.AuthAndPathIn true "创建api和角色关系"
+// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
+// @Router /api/setAuthAndApi [post]
+export const setAuthAndApi = (data) => {
+    return service({
+        url: "/api/setAuthAndApi",
+        method: 'post',
+        data
+    })
 }

+ 4 - 4
QMPlusVuePage/src/view/layout/aside/index.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <div class="menu-total">
-        <i class="el-icon-arrow-right"></i>
+      <i class="el-icon-arrow-right"></i>
     </div>
     <vue-scroll :ops="ops">
       <el-menu
@@ -31,14 +31,14 @@ export default {
   data() {
     return {
       active: '',
-      isCollapse: true,
+      isCollapse: false,
       ops: {
-        bar: {disable:true}
+        bar: { disable: true }
       }
     }
   },
   methods: {
-    selectMenuItem(index,indexList) {
+    selectMenuItem(index) {
       if (index === this.$route.name) return
       this.$router.push({ name: index })
     }

+ 12 - 2
QMPlusVuePage/src/view/superAdmin/api/api.vue

@@ -6,6 +6,7 @@
     <el-table :data="tableData" border stripe>
       <el-table-column label="id" min-width="60" prop="ID"></el-table-column>
       <el-table-column label="api路径" min-width="150" prop="path"></el-table-column>
+      <el-table-column label="api分组" min-width="150" prop="group"></el-table-column>
       <el-table-column label="api简介" min-width="150" prop="description"></el-table-column>
       <el-table-column fixed="right" label="操作" width="200">
         <template slot-scope="scope">
@@ -29,9 +30,12 @@
     <el-dialog :visible.sync="dialogFormVisible" title="新增Api">
       <el-form :inline="true" :model="form" label-width="80px">
         <el-form-item label="路径">
-          <el-input autocomplete="off" v-model="form.path"></el-input>
+          <el-input autocomplete="off" @blur="autoGroup" v-model="form.path"></el-input>
         </el-form-item>
-        <el-form-item label="说明">
+        <el-form-item label="api分组">
+          <el-input autocomplete="off" v-model="form.group"></el-input>
+        </el-form-item>
+        <el-form-item label="api简介">
           <el-input autocomplete="off" v-model="form.description"></el-input>
         </el-form-item>
       </el-form>
@@ -60,15 +64,21 @@ export default {
       dialogFormVisible: false,
       form: {
         path: '',
+        group: '',
         description: ''
       },
       type: ''
     }
   },
   methods: {
+    // 自动设置api分组
+    autoGroup(){
+      this.form.group = this.form.path.split('/')[1]
+    },
     initForm() {
       this.form = {
         path: '',
+        group: '',
         description: ''
       }
     },

+ 94 - 15
QMPlusVuePage/src/view/superAdmin/authority/authority.vue

@@ -45,13 +45,14 @@
     <!-- 关联menu弹窗 -->
     <el-dialog :visible.sync="menuDialogFlag" title="关联菜单">
       <el-tree
-        :data="treeData"
-        :default-checked-keys="treeIds"
-        :props="defaultProps"
+        v-if="menuDialogFlag"
+        :data="menuTreeData"
+        :default-checked-keys="menuTreeIds"
+        :props="menuDefaultProps"
         default-expand-all
         highlight-current
         node-key="ID"
-        ref="tree"
+        ref="menuTree"
         show-checkbox
       ></el-tree>
       <div class="dialog-footer" slot="footer">
@@ -59,6 +60,27 @@
         <el-button @click="relation" type="primary">确 定</el-button>
       </div>
     </el-dialog>
+
+
+    
+    <!-- 关联api弹窗 -->
+    <el-dialog :visible.sync="apiDialogFlag" title="关联api">
+      <el-tree
+        v-if="apiDialogFlag"
+        :data="apiTreeData"
+        :default-checked-keys="apiTreeIds"
+        :props="apiDefaultProps"
+        default-expand-all
+        highlight-current
+        node-key="ID"
+        ref="apiTree"
+        show-checkbox
+      ></el-tree>
+      <div class="dialog-footer" slot="footer">
+        <el-button @click="closeDialog">取 消</el-button>
+        <el-button @click="authApiEnter" type="primary">确 定</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
@@ -71,6 +93,11 @@ import {
   createAuthority
 } from '@/api/authority'
 import { getBaseMenuTree, addMenuAuthority, getMenuAuthority } from '@/api/menu'
+import {
+  getAllApis,
+  getAuthAndApi,
+  setAuthAndApi
+} from '@/api/api'
 import infoList from '@/view/superAdmin/mixins/infoList'
 export default {
   name: 'Authority',
@@ -80,14 +107,22 @@ export default {
       listApi: getAuthorityList,
       listKey:'list',
       activeUserId: 0,
-      treeData: [],
-      treeIds: [],
-      defaultProps: {
+      menuTreeData: [],
+      menuTreeIds: [],
+      menuDefaultProps: {
         children: 'children',
         label: 'nickName'
       },
+
+      apiTreeData: [],
+      apiTreeIds: [],
+      apiDefaultProps: {
+        children: 'children',
+        label: 'description'
+      },
       dialogFormVisible: false,
       menuDialogFlag: false,
+      apiDialogFlag: false,
       form: {
         authorityId: '',
         authorityName: ''
@@ -130,8 +165,10 @@ export default {
       this.initForm()
       this.dialogFormVisible = false
       this.menuDialogFlag = false
+      this.apiDialogFlag = false
     },
     // 确定弹窗
+
     async enterDialog() {
       const res = await createAuthority(this.form)
       if (res.success) {
@@ -161,17 +198,14 @@ export default {
           arr.push(Number(item.menuId))
         }
       })
-      this.treeIds = arr
-      const res2 = await getBaseMenuTree()
-      this.treeData = res2.data.menus
+      this.menuTreeIds = arr
       this.activeUserId = row.authorityId
       this.menuDialogFlag = true
     },
     // 关联树 确认方法
     async relation() {
-      const checkArr = this.$refs.tree
-        .getCheckedNodes()
-        .concat(this.$refs.tree.getHalfCheckedNodes())
+      const checkArr = this.$refs.menuTree
+        .getCheckedNodes(false,true)
       const res = await addMenuAuthority({
         menus: checkArr,
         authorityId: this.activeUserId
@@ -183,11 +217,56 @@ export default {
         })
       }
       this.closeDialog()
+    },
+    // 创建api树方法
+    buildApiTree(apis){
+      const apiObj = new Object
+      apis&&apis.map(item=>{
+        if(apiObj.hasOwnProperty(item.group)){
+          apiObj[item.group].push(item)
+        }else{
+          Object.assign(apiObj,{[item.group]:[item]})
+        }
+      })
+      const apiTree = []
+      for(const key in apiObj){
+        const treeNode = {
+          ID:key,
+          description:key+"组",
+          children:apiObj[key]
+        }
+        apiTree.push(treeNode)
+      }
+      return apiTree
+    },
+    // 关联用户api关系
+    async addAuthApi(row){
+      const res = await getAuthAndApi({authorityId:row.authorityId})
+      this.activeUserId = row.authorityId
+      this.apiTreeIds = res.data.apis||[]
+      this.apiDialogFlag = true
+    },
+    async authApiEnter(){
+      const checkArr = this.$refs.apiTree.getCheckedKeys(true)
+       const res = await setAuthAndApi({authorityId:this.activeUserId,apiIds:checkArr})
+      if (res.success) {
+        this.$message({
+          type: 'success',
+          message: '添加成功!'
+        })
+      }
+      this.closeDialog()
     }
-    // 获取基础menu树
   },
-  created() {
+  async created() {
     this.getTableData()
+    // 获取所有菜单树
+    const res = await getBaseMenuTree()
+    this.menuTreeData = res.data.menus
+    // 获取api并整理成树结构
+    const res2 = await getAllApis()
+    const apis = res2.data.apis
+    this.apiTreeData = this.buildApiTree(apis)
   }
 }
 </script>

+ 7 - 14
QMPlusVuePage/vue.config.js

@@ -6,13 +6,7 @@ function resolve(dir) {
     return path.join(__dirname, dir)
 }
 module.exports = {
-    /**
-     * You will need to set publicPath if you plan to deploy your site under a sub path,
-     * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,
-     * then publicPath should be set to "/bar/".
-     * In most cases please use '/' !!!
-     * Detail: https://cli.vuejs.org/config/#publicpath
-     */
+    // 基础配置 详情看文档
     publicPath: '/',
     outputDir: 'dist',
     assetsDir: 'static',
@@ -26,20 +20,19 @@ module.exports = {
             errors: true
         },
         proxy: {
-            // change xxx-api/login => mock/login
+            // 把key的路径代理到target位置
             // detail: https://cli.vuejs.org/config/#devserver-proxy
-            [process.env.VUE_APP_BASE_API]: {
-                target: `http://127.0.0.1:8888`,
+            [process.env.VUE_APP_BASE_API]: { //需要代理的路径   例如 '/api'
+                target: `http://127.0.0.1:8888`, //代理到 目标路径
                 changeOrigin: true,
-                pathRewrite: {
-                    ['^' + process.env.VUE_APP_BASE_API]: ''
+                pathRewrite: { // 修改路径数据
+                    ['^' + process.env.VUE_APP_BASE_API]: '' // 举例 '^/api:""' 把路径中的/api字符串删除
                 }
             }
         },
     },
     configureWebpack: {
-        // provide the app's title in webpack's name field, so that
-        // it can be accessed in index.html to inject the correct title.
+        //    @路径走src文件夹
         resolve: {
             alias: {
                 '@': resolve('src')