feat: 增加权限设置

This commit is contained in:
fit2cloud-chenyw 2021-03-05 16:07:44 +08:00
parent a60a243114
commit a647c66b8a
20 changed files with 151 additions and 24 deletions

View File

@ -12,4 +12,6 @@ import java.util.List;
public class CurrentUserDto extends SysUserEntity implements Serializable {
private List<CurrentRoleDto> roles;
private List<String> permissions;
}

View File

@ -23,6 +23,8 @@ public class DynamicMenuDto implements Serializable {
private Long id;
private String permission;
private List<DynamicMenuDto> children;
}

View File

@ -14,7 +14,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -59,7 +58,9 @@ public class AuthServer implements AuthApi {
SysUserEntity user = authUserService.getUser(username);
CurrentUserDto currentUserDto = BeanUtils.copyBean(new CurrentUserDto(), user);
List<CurrentRoleDto> currentRoleDtos = authUserService.roleInfos(user.getUserId());
List<String> permissions = authUserService.permissions(user.getUserId());
currentUserDto.setRoles(currentRoleDtos);
currentUserDto.setPermissions(permissions);
return currentUserDto;
}

View File

@ -4,11 +4,13 @@ import io.dataease.auth.api.dto.CurrentRoleDto;
import io.dataease.auth.entity.SysUserEntity;
import io.dataease.base.mapper.ext.AuthMapper;
import io.dataease.auth.service.AuthUserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class AuthUserServiceImpl implements AuthUserService {
@ -27,7 +29,8 @@ public class AuthUserServiceImpl implements AuthUserService {
}
@Override
public List<String> permissions(Long userId){
return authMapper.permissions(userId);
List<String> permissions = authMapper.permissions(userId);
return permissions.stream().filter(StringUtils::isNotEmpty).collect(Collectors.toList());
}
@Override

View File

@ -21,6 +21,7 @@ public class DynamicMenuServiceImpl implements DynamicMenuService {
@Override
public List<DynamicMenuDto> load(String userId) {
SysMenuExample sysMenuExample = new SysMenuExample();
sysMenuExample.createCriteria().andTypeLessThanOrEqualTo(1);
sysMenuExample.setOrderByClause(" menu_sort ");
List<SysMenu> sysMenus = sysMenuMapper.selectByExample(sysMenuExample);
List<DynamicMenuDto> dynamicMenuDtos = sysMenus.stream().map(this::convert).collect(Collectors.toList());
@ -40,6 +41,7 @@ public class DynamicMenuServiceImpl implements DynamicMenuService {
menuMeta.setTitle(sysMenu.getTitle());
menuMeta.setIcon(sysMenu.getIcon());
dynamicMenuDto.setMeta(menuMeta);
dynamicMenuDto.setPermission(sysMenu.getPermission());
return dynamicMenuDto;
}

View File

@ -0,0 +1,29 @@
import store from '@/store'
function checkPermission(el, binding) {
const { value } = binding
// 我们是基于资源授鉴权 不用角色 因为后期可能有对部门授权 对 人员授权
const permissions = store.getters && store.getters.permissions
if (value && value instanceof Array) {
const needPermissions = value
// 满足指令中的每个权限才可放行 而不是 满足任意一个即可
const hasPermission = needPermissions.every(needP => {
const result = permissions.includes(needP)
return result
})
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`使用方式: v-permission="['user:read']"`)
}
}
export default {
inserted(el, binding) {
checkPermission(el, binding)
},
update(el, binding) {
checkPermission(el, binding)
}
}

View File

@ -0,0 +1,8 @@
import permission from '@/directive/Permission'
export default {
install(Vue) {
Vue.directive('permission', permission)
}
}

View File

@ -14,11 +14,12 @@ import router from './router'
import '@/icons' // icon
import '@/permission' // permission control
import api from '@/api/index.js'
import filter from '@/filter/filter'
import message from '@/metersphere/common/js/message'
import { left2RightDrag, bottom2TopDrag, right2LeftDrag } from '@/metersphere/common/js/directive'
import directives from './directive'
Vue.prototype.$api = api
import * as echarts from 'echarts'
@ -50,11 +51,13 @@ Vue.use(Fit2CloudUI, {
})
Vue.use(filter)
Vue.use(message)
Vue.use(directives)
Vue.config.productionTip = false
// 支持左右拖拽
Vue.directive('left-to-right-drag', left2RightDrag)
Vue.directive('right-to-left-drag', right2LeftDrag)
Vue.directive('bottom-to-top-drag', bottom2TopDrag)
new Vue({
el: '#app',
router,

View File

@ -10,6 +10,7 @@
<span class="operate-button">
<ms-table-button
v-if="showCreate"
v-permission="permission.add"
:is-tester-permission="isTesterPermission"
icon="el-icon-circle-plus-outline"
:content="createTip"
@ -67,7 +68,8 @@ export default {
default: false
},
condition: {
type: Object
type: Object,
default: null
},
createTip: {
type: String,
@ -76,7 +78,8 @@ export default {
}
},
runTip: {
type: String
type: String,
default: null
},
@ -85,10 +88,18 @@ export default {
default: false
},
tip: {
String,
type: String,
default() {
return this.$t('commons.search_by_name')
}
},
permission: {
type: Object,
default() {
return {
add: []
}
}
}
},
computed: {

View File

@ -1,9 +1,9 @@
<template>
<span>
<slot name="front" />
<ms-table-operator-button :is-tester-permission="isTesterPermission" :tip="tip1" icon="el-icon-edit" @exec="editClick" @click.stop="editClickStop" />
<ms-table-operator-button v-permission="permission.edit" :is-tester-permission="isTesterPermission" :tip="tip1" icon="el-icon-edit" @exec="editClick" @click.stop="editClickStop" />
<slot name="middle" />
<ms-table-operator-button :is-tester-permission="isTesterPermission" :tip="tip2" icon="el-icon-delete" type="danger" @exec="deleteClick" @click.stop="deleteClickStop" />
<ms-table-operator-button v-permission="permission.del" :is-tester-permission="isTesterPermission" :tip="tip2" icon="el-icon-delete" type="danger" @exec="deleteClick" @click.stop="deleteClickStop" />
<slot name="behind" />
</span>
@ -30,6 +30,16 @@ export default {
isTesterPermission: {
type: Boolean,
default: false
},
permission: {
type: Object,
default() {
return {
edit: [],
del: []
}
}
}
},
methods: {

View File

@ -66,7 +66,8 @@ router.beforeEach(async(to, from, next) => {
})
export const loadMenus = (next, to) => {
buildMenus().then(res => {
const asyncRouter = filterAsyncRouter(res.data)
const filterDatas = filterRouter(res.data)
const asyncRouter = filterAsyncRouter(filterDatas)
asyncRouter.push({ path: '*', redirect: '/404', hidden: true })
store.dispatch('permission/GenerateRoutes', asyncRouter).then(() => { // 存储路由
router.addRoutes(asyncRouter)
@ -74,6 +75,30 @@ export const loadMenus = (next, to) => {
})
})
}
// 根据权限过滤菜单
const filterRouter = routers => {
const user_permissions = store.getters.permissions
if (!user_permissions || user_permissions.length === 0) {
return routers
}
const tempResults = routers.filter(router => hasPermission(router, user_permissions))
// 如果是一级菜单(目录) 没有字菜单 那就移除
return tempResults.filter(item => item.children && item.children.length)
}
const hasPermission = (router, user_permissions) => {
// 菜单要求权限 但是当前用户权限没有包含菜单权限
if (router.permission && !user_permissions.includes(router.permission)) {
return false
}
// 如果有字菜单 则 判断是否满足 ‘任意一个子菜单有权限’
if (router.children && router.children.length) {
const permissionChilds = router.children.filter(item => hasPermission(item, user_permissions))
router.children = permissionChilds
return router.children.length > 0
}
return true
}
router.afterEach(() => {
// finish progress bar
NProgress.done()

View File

@ -16,6 +16,7 @@ const getters = {
sceneData: state => state.dataset.sceneData,
table: state => state.dataset.table,
loadingMap: state => state.request.loadingMap,
currentPath: state => state.permission.currentPath
currentPath: state => state.permission.currentPath,
permissions: state => state.user.permissions
}
export default getters

View File

@ -47,7 +47,7 @@ export const filterAsyncRouter = (routers) => { // 遍历后台传来的路由
}).map(router => {
router.hasOwnProperty('id') && delete router.id
router.hasOwnProperty('pid') && delete router.pid
router.hasOwnProperty('children') && !router['children'] && delete router.children
router.hasOwnProperty('children') && (!router['children'] || !router['children'].length) && delete router.children
router.hasOwnProperty('redirect') && !router['redirect'] && delete router.redirect
return router
})

View File

@ -10,7 +10,9 @@ const getDefaultState = () => {
roles: [],
avatar: '',
// 第一次加载菜单时用到
loadMenus: false
loadMenus: false,
// 当前用户拥有哪些资源权限
permissions: []
}
}
@ -37,6 +39,9 @@ const mutations = {
},
SET_LOAD_MENUS: (state, loadMenus) => {
state.loadMenus = loadMenus
},
SET_PERMISSIONS: (state, permissions) => {
state.permissions = permissions
}
}
@ -68,12 +73,13 @@ const actions = {
const currentUser = data
commit('SET_USER', currentUser)
const roles = data.roles
const { roles, nickName, permissions } = data
commit('SET_ROLES', roles)
const { nickName } = data
commit('SET_NAME', nickName)
// commit('SET_AVATAR', avatar)
commit('SET_PERMISSIONS', permissions)
resolve(data)
}).catch(error => {
reject(error)

View File

@ -15,6 +15,6 @@ export function isExternal(path) {
* @returns {Boolean}
*/
export function validUsername(str) {
const valid_map = ['admin', 'editor']
const valid_map = ['admin', 'cyw']
return valid_map.indexOf(str.trim()) >= 0
}

View File

@ -4,6 +4,7 @@
<el-card class="table-card">
<template v-slot:header>
<ms-table-header
:permission="permission"
:condition.sync="condition"
:create-tip="$t('organization.create')"
:title="$t('commons.organization')"
@ -45,7 +46,7 @@
<el-table-column :label="$t('commons.operating')">
<template v-slot:default="scope">
<ms-table-operator @editClick="edit(scope.row)" @deleteClick="handleDelete(scope.row)" />
<ms-table-operator :permission="permission" @editClick="edit(scope.row)" @deleteClick="handleDelete(scope.row)" />
</template>
</el-table-column> -->
</el-table>
@ -167,6 +168,11 @@ export default {
description: [
{ max: 50, message: this.$t('commons.input_limit', [0, 50]), trigger: 'blur' }
]
},
permission: {
add: ['dept:add'],
edit: ['dept:edit'],
del: ['dept:del']
}
}

View File

@ -4,6 +4,7 @@
<el-card class="table-card">
<template v-slot:header>
<ms-table-header
:permission="permission"
:condition.sync="condition"
:create-tip="$t('menu.create')"
:title="$t('commons.menu')"
@ -58,9 +59,9 @@
<el-table-column :label="$t('commons.operating')">
<template v-slot:default="scope">
<ms-table-operator @editClick="edit(scope.row)" @deleteClick="handleDelete(scope.row)" />
<ms-table-operator :permission="permission" @editClick="edit(scope.row)" @deleteClick="handleDelete(scope.row)" />
</template>
</el-table-column> -->
</el-table-column>
</el-table>
</el-card>
@ -210,6 +211,11 @@ export default {
description: [
{ max: 50, message: this.$t('commons.input_limit', [0, 50]), trigger: 'blur' }
]
},
permission: {
add: ['menu:add'],
edit: ['menu:edit'],
del: ['menu:del']
}
}
@ -220,6 +226,7 @@ export default {
},
methods: {
create() {
this.form = Object.assign({}, this.defaultForm)
this.dialogVisible = true
this.formType = 'add'
listenGoBack(this.closeFunc)

View File

@ -4,7 +4,7 @@
<el-aside width="70%" style="border: 1px solid #eee">
<el-card class="table-card">
<template v-slot:header>
<ms-table-header :condition.sync="condition" :create-tip="$t('role.add')" :title="$t('commons.role')" @search="search" @create="create" />
<ms-table-header :permission="permission" :condition.sync="condition" :create-tip="$t('role.add')" :title="$t('commons.role')" @search="search" @create="create" />
</template>
<el-table border highlight-current-row class="adjust-table" :data="tableData" style="width: 100%;" @row-click="rowClick">
@ -16,7 +16,7 @@
</el-table-column>
<el-table-column :label="$t('commons.operating')">
<template v-slot:default="scope">
<ms-table-operator @editClick="edit(scope.row)" @deleteClick="handleDelete(scope.row)" />
<ms-table-operator :permission="permission" @editClick="edit(scope.row)" @deleteClick="handleDelete(scope.row)" />
</template>
</el-table-column>
</el-table>
@ -121,7 +121,12 @@ export default {
{ required: true, message: '请输入名称', trigger: 'blur' }
]
},
currentRow: null
currentRow: null,
permission: {
add: ['role:add'],
edit: ['role:edit'],
del: ['role:del']
}
}
},
watch: {

View File

@ -4,6 +4,7 @@
<el-card class="table-card">
<template v-slot:header>
<ms-table-header
:permission="permission"
:condition.sync="condition"
:create-tip="$t('user.create')"
:title="$t('commons.user')"
@ -38,7 +39,7 @@
<!-- <el-table-column prop="source" :label="$t('user.source')"/> -->
<el-table-column :label="$t('commons.operating')" min-width="120px">
<template v-slot:default="scope">
<ms-table-operator @editClick="edit(scope.row)" @deleteClick="del(scope.row)">
<ms-table-operator :permission="permission" @editClick="edit(scope.row)" @deleteClick="del(scope.row)">
<template v-slot:behind>
<ms-table-operator-button
v-if="scope.row.isLocalUser"
@ -274,7 +275,12 @@ export default {
roles: [],
roleDatas: [],
userRoles: [],
formType: 'add'
formType: 'add',
permission: {
add: ['user:add'],
edit: ['user:edit'],
del: ['user:del']
}
}
},

View File

@ -18,7 +18,7 @@
{{ scope.row.title }}
</template>
</el-table-column>
<el-table-column label="Author" width="110" align="center">
<el-table-column v-permission="['menu:del']" label="Author" width="110" align="center">
<template slot-scope="scope">
<span>{{ scope.row.author }}</span>
</template>