若依Vue系统中的权限管理部分的功能都集中在了系统管理菜单模块中,如下图所示。其中权限部分主要涉及到了用户管理、角色管理、菜单管理、部门管理这四个部分。
一、若依Vue系统中的权限分类 根据观察,若依Vue系统中的权限分为以下几类
菜单权限:用户登录系统之后能看到哪些菜单
按钮权限:用户在一个页面上能看到哪些按钮,比如新增、删除等按钮
接口权限:用户带着认证信息请求后端接口,是否有权限访问,该接口和前端页面上的按钮一一对应
数据权限:用户有权限访问后端某个接口,但是不同的用户相同的接口相同的入参,根据权限大小不同,返回的结果应当不一样——权限大的能够看到的数据更多。
1.菜单权限 这个比较好理解,拥有不同权限的用户登录系统之后看到的菜单是不一样的,从新建菜单到给一个用户分配菜单权限,上一篇文章已经讲过,不赘述。
用户登录之后会请求后端的com.ruoyi.web.controller.system.SysLoginController#getRouters
接口获取登录用户的菜单数据:
1 2 3 4 5 6 7 8 select distinct m.menu_id, m.parent_id, m.menu_name, m.path , m.component, m.visible, m.status, ifnull (m.perms,'' ) as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time from sys_menu m left join sys_role_menu rm on m.menu_id = rm.menu_id left join sys_user_role ur on rm.role_id = ur.role_id left join sys_role ro on ur.role_id = ro.role_id left join sys_user u on ur.user_id = u.user_id where u.user_id = #{userId} and m.menu_type in ('M' , 'C' ) and m.status = 0 AND ro.status = 0 order by m.parent_id, m.order_num
菜单类型(M目录 C菜单 F按钮);菜单状态(0显示 1隐藏)
这是典型的用户-角色-菜单模型。
前端会根据该接口返回的数据渲染出不同的菜单。
2.按钮权限 新增按钮权限和新增菜单差不多,下图是我在新闻列表页面上新增了一个按钮叫做新闻新增
,该按钮的权限分配和菜单的权限分配方法是一样的。
](https://img2020.cnblogs.com/blog/516671/202102/516671-20210220092911897-871865201.png; charset=UTF-8 )
](https://img2020.cnblogs.com/blog/516671/202102/516671-20210220092912311-49634302.png; charset=UTF-8 )
3.接口权限 每一个按钮基本上都会对应着一个后端的接口,前端会根据权限标志显示或者隐藏按钮,但是如果用户不点击按钮,直接通过http请求工具请求后端咋办?所以接口权限也是要有的,该权限和按钮上权限完全一致。
若依系统使用了SpringSecurity框架,接口权限都是基于注解@PreAuthorize
实现的,比如,用户管理页面中的修改用户按钮对应的后端接口长这个样子
1 2 3 4 5 6 7 8 ```@PreAuthorize ("@ss.hasPermi('system:user:edit')" )@Log (title = "用户管理" , businessType = BusinessType.UPDATE )@PutMapping public AjaxResult edit (@Validated @RequestBody SysUser user) { ... }
1 2 3 4 5 6 和其对应的前端按钮权限标志一样 [![image-20210218165254074 ](https://img2020.cnblogs.com/blog/516671/202102/516671-20210220092912565 -1420307202 .png;%20charset=UTF-8 )](https://img2020.cnblogs.com/blog/516671/202102/516671-20210220092912565 -1420307202 .png;%20charset=UTF-8 ) 如果没有权限访问接口,则会返回类似如下信息:
{ “msg”: “请求访问:/system/user/list,认证失败,无法访问系统资源”, “code”: 401 }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 用户有权限访问后端某个接口,但是不同的用户相同的接口相同的入参,根据权限大小不同,返回的结果应当不一样——权限大的能够看到的数据更多。 体现在若依Vue系统中,举个例子,现在若以系统中有两个用户,一个是超级管理员admin,一个是普通用户kdyzm [![image-20210218173156066 ](https://img 2020.cnblogs.com/blog/ 516671 /202102/ 516671 -20210220092912780 -849185298 .png;%20 charset=UTF-8 )](https://img 2020.cnblogs.com/blog/ 516671 /202102/ 516671 -20210220092912780 -849185298 .png;%20 charset=UTF-8 ) 它们两者均有用户管理、菜单管理、角色管理权限,所以它们能够看到相应的菜单并作出相应的操作,比如删除、新增、修改等。这里有个问题,kdyzm应当只能看到一部分数据,而超级管理员应当能够看到所有数据,在若依系统中,是通过对角色`数据权限`修改进行控制的。 [![image-20210218173632200 ](https://img 2020.cnblogs.com/blog/ 516671 /202102/ 516671 -20210220092913044 -2087596525 .png;%20 charset=UTF-8 )](https://img 2020.cnblogs.com/blog/ 516671 /202102/ 516671 -20210220092913044 -2087596525 .png;%20 charset=UTF-8 ) [![image-20210218173725363 ](https://img 2020.cnblogs.com/blog/ 516671 /202102/ 516671 -20210220092913260 -1489587122 .png;%20 charset=UTF-8 )](https://img 2020.cnblogs.com/blog/ 516671 /202102/ 516671 -20210220092913260 -1489587122 .png;%20 charset=UTF-8 ) 所以,相同的权限,超级管理员能够看到的用户数量和普通用户kdyzm能够看到的用户数量是不一样的。 超级管理员看到的用户管理页面: [![image-20210218174025420 ](https://img 2020.cnblogs.com/blog/ 516671 /202102/ 516671 -20210220092913461 -95587307 .png;%20 charset=UTF-8 )](https://img 2020.cnblogs.com/blog/ 516671 /202102/ 516671 -20210220092913461 -95587307 .png;%20 charset=UTF-8 ) 普通用户kdyzm看到的用户管理页面: [![image-20210218174122328 ](https://img 2020.cnblogs.com/blog/ 516671 /202102/ 516671 -20210220092913711 -1306359143 .png;%20 charset=UTF-8 )](https://img 2020.cnblogs.com/blog/ 516671 /202102/ 516671 -20210220092913711 -1306359143 .png;%20 charset=UTF-8 ) 菜单权限很简单,实际上就是简单的用户-角色-菜单模型,那么菜单是什么时候加载的呢?`ruoyi-ui\src\permission.js`,加载的逻辑在这个文件中。 `permission.js`文件中设置了[导航守卫](https:// router.vuejs.org/zh/gui de/advanced/ navigation-guards.html),每次路由发生变化的时候就会触发router.beforeEach的回调函数。
router.beforeEach((to, from, next) => { NProgress.start() if (getToken()) { /* has token*/ if (to.path === ‘/login’) { next({ path: ‘/‘ }) NProgress.done() } else { if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息 store.dispatch(‘GetInfo’).then(res => { // 拉取user_info const roles = res.roles store.dispatch(‘GenerateRoutes’, { roles }).then(accessRoutes => { // 根据roles权限生成可访问的路由表 router.addRoutes(accessRoutes) // 动态添加可访问路由表 next({ …to, replace: true }) // hack方法 确保addRoutes已完成 }) }).catch(err => { store.dispatch(‘LogOut’).then(() => { Message.error(err) next({ path: ‘/‘ }) }) }) } else { next() } } } else { // 没有token if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入 next() } else { next(/login?redirect=${to.fullPath}
) // 否则全部重定向到登录页 NProgress.done() } } })
1 2 3 4 5 6 7 8 注意`if (store.getters.roles.length === 0 ) {`这段逻辑,可以看出,如果不刷新当前页面,就算给用户添加了新的菜单权限,用户也看不到新的菜单。 ### 2. 按钮权限 按钮权限设置上和菜单权限基本上是一样的,是附着于页面中的细粒度权限。按钮权限体现在如果用户没有相应的权限,则看不到相关的按钮。这个是咋实现的呢? 先看下系统管理下的菜单管理中的修改、新增和删除按钮前端vue代码
```
el-button上有个属性v-hasPermi
,这实际上是vue的自定义指令,属性值就是创建按钮的时候定义的那个权限标志
。其定义在src/directive/permission/index.js
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import hasRole from './hasRole' import hasPermi from './hasPermi' const install = function (Vue ) { Vue .directive ('hasRole' , hasRole) Vue .directive ('hasPermi' , hasPermi) }if (window .Vue ) { window ['hasRole' ] = hasRole window ['hasPermi' ] = hasPermi Vue .use (install); }export default install
其具体实现逻辑就在同目录的hasPermi.js
文件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import store from '@/store' export default { inserted (el, binding, vnode ) { const { value } = binding const all_permission = "*:*:*" ; const permissions = store.getters && store.getters .permissions if (value && value instanceof Array && value.length > 0 ) { const permissionFlag = value const hasPermissions = permissions.some (permission => { return all_permission === permission || permissionFlag.includes (permission) }) if (!hasPermissions) { el.parentNode && el.parentNode .removeChild (el) } } else { throw new Error (`请设置操作权限标签值` ) } } }
注意代码 el.parentNode && el.parentNode.removeChild(el)
,可以看到,如果没有按钮权限,则会将按钮本身从dom中移除。
3.接口权限 接口权限和前端的按钮权限一一对应。为的是防止用户绕过按钮直接请求后端接口获取数据。在若依Vue系统中,是使用SpringSecurity的注解@PreAuthorize
实现的。
虽然只是一个注解,但是它是SpringSecurity+JWT集成的结晶~这个之后再细谈。
4.数据权限 数据权限实现的关键在于com.ruoyi.framework.aspectj.DataScopeAspect
类。该类是一个切面类,凡是加上com.ruoyi.common.annotation.DataScope
注解的方法,在执行的时候都会被它拦截。
该切面定义了五种权限范围
name
code
desc
DATA_SCOPE_ALL
1
全部数据权限
DATA_SCOPE_CUSTOM
2
自定数据权限
DATA_SCOPE_DEPT
3
部门数据权限
DATA_SCOPE_DEPT_AND_CHILD
4
部门及以下数据权限
DATA_SCOPE_SELF
5
仅本人数据权限
该切面的核心逻辑是“拼SQL”,方法执行之前,会给参数的一个params属性添加一个dataScope键值对,key为”dataScope”,值为AND (" + sqlString.substring(4) + ")"
样式的一段SQL,这段SQL会根据当前用户所在的部门以及当前用户角色的权限范围发生变化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 StringBuilder sqlString = new StringBuilder(); for (SysRole role : user .getRoles()) { String dataScope = role .getDataScope(); if (DATA_SCOPE_ALL.equals(dataScope)) { sqlString = new StringBuilder(); break; } else if (DATA_SCOPE_CUSTOM.equals(dataScope)) { sqlString.append(StringUtils.format( " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role .getRoleId())); } else if (DATA_SCOPE_DEPT.equals(dataScope)) { sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user .getDeptId())); } else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) { sqlString.append(StringUtils.format( " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user .getDeptId(), user .getDeptId())); } else if (DATA_SCOPE_SELF.equals(dataScope)) { if (StringUtils.isNotBlank(userAlias)) { sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user .getUserId())); } else { // 数据权限为仅本人且没有userAlias别名不查询任何数据 sqlString.append(" OR 1=0 "); } } }
简单来说,这段代码的逻辑就是用户所在的部门权限越高,数据权限范围越大,查出来的结果集将会越大。
DataScope注解分别加到了部门列表查询、角色列表查询、用户列表查询的接口上,很明显,这几个接口需要根据不同的人查出不同的结果。
以用户列表查询为例,执行sql为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ``` <select id ="selectUserList" parameterType ="SysUser" resultMap ="SysUserResult" > select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, d.dept_name, d.leader from sys_user u left join sys_dept d on u.dept_id = d.dept_id where u.del_flag = '0' <if test ="userName != null and userName != ''" > AND u.user_name like concat('%', # {userName} , '%') </if > <if test ="status != null and status != ''" > AND u.status = # {status} </if > <if test ="phonenumber != null and phonenumber != ''" > AND u.phonenumber like concat('%', # {phonenumber} , '%') </if > <if test ="params.beginTime != null and params.beginTime != ''" > AND date_format(u.create_time,'%y%m%d') >= date_format(# {params.beginTime} ,'%y%m%d') </if > <if test ="params.endTime != null and params.endTime != ''" > AND date_format(u.create_time,'%y%m%d') <= date_format(# {params.endTime} ,'%y%m%d') </if > <if test ="deptId != null and deptId != 0" > AND (u.dept_id = # {deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(# {deptId} , ancestors) )) </if > $ {params.dataScope} </select >
${params.dataScope}
1 2 3 4 5 6 7 8 9 10 11 12 实际上`DataScopeAspect`切面就只干了填充params的dataScope属性这么一件事情。 ## 三、若依Vue系统SpringSecurity+JWT 若依Vue系统中从用户登录到后端接口权限校验,都是基于SpringSecurity+JWT实现的,其中,SpringSecurity是核心,jwt只是为了保证token合法性的一种手段(签名防止篡改)。spring security集成的相关代码在`ruoyi-framework`模块的`com.ruoyi.framework.security`包以及`com.ruoyi.framework.config.SecurityConfig`类中。 `SecurityConfig`是核心配置类,所有的配置均在该类中。 ### 1 .用户登录 用户登录的逻辑在方法`com.ruoyi.web.controller.system.SysLoginController#login`中,一个典型的登录请求体如下所示
{ “username”: “admin”, “password”: “admin123”, “code”: “0”, “uuid”: “a9fdbcbcb28748b796b5b77ad71bbb97” }
1 2 username和password 分别是用户名和密码,code为验证码,uuid 为验证码的唯一标识。登录成功之后会返回前端一个jwt令牌
{ “msg”: “操作成功”, “code”: 200, “token”: “eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjIzZjRhNjJjLTY5NzMtNDcxZS04ZmU4LWJmYWQ4YzllNWFkMiJ9.9d3iIaNq62CkjTXlxFOQgdDMOAZiu5tAsEn0cEuV23opT6PAqu_CiaN7kQY8_XhlQrHX5RgZ2bH7LpsiKLLcSw” }
1 2 3 4 5 6 7 8 9 在登录方法中,做了以下几件事情- 根据uuid获取redis中的验证码并对请求的验证码做验证- 如果验证码没问题,则对用户名和密码进行校验- 如果用户名和密码校验成功,则使用token作为key将用户信息保存到redis- 使用jwt对token签名并返回前端 在整个过程中,会抛出一些自定义异常,比如
throw new CaptchaExpireException(); throw new CaptchaException(); throw new UserPasswordNotMatchException(); throw new CustomException(e.getMessage());
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 这些异常最终会被全局异常处理器处理掉:`com.ruoyi.framework.web.exception.GlobalExceptionHandler` ### 2 .接口权限校验 前端请求完成登录接口之后会将token存储到cookie,key为Admin-Token,value是jwt令牌。登录逻辑:`user.actions.Login`。 之后,每次请求后端接口的时候都会带上Authentication Header <img width="900" height="236" src=":/ecf0a0b25f7743fa9565506bbcda4935" />](https://img2020.cnblogs.com/blog/516671 /202102 /516671 -20210220092914108 -1243552764 .png 这实际上是通过axios的请求拦截器实现的:详情可见`src/utils/request.js`文件 带着Authentication Header的请求打到后端的时候会经过过滤器`com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter`,该过滤器做了以下几件事情 - 从请求头中取出jwt令牌,并对其进行jwt验签,验签若是成功,则取出原始token - 根据token从redis中取出用户数据 - 将用户信息封装成`UsernamePasswordAuthenticationToken`对象,并将该对象填充到Spring Security上下文中 填充到SpringSecurity上下文才能让Controller接口上的`@PreAuthorize`注解发挥作用(**存疑,这里若依作者并非使用原生的SpringSecurity提供的spel表达式,也没有用authorities,而是使用了PermissionService类**)。 接着,Controller接口正式执行之前会进入`com.ruoyi.framework.web.service.PermissionService#hasPermi`方法判定权限,这里重新从redis中取出用户数据并进行权限校验,权限校验失败则不再执行接口中逻辑(**存疑,这里并没有使用SpringSecurity上下文中的用户数据,那么`JwtAuthenticationTokenFilter`中的用户信息填充上下文中的代码是干啥用的**)。 ## 四、实战 上一篇文章讲解了如何创建一个菜单并创建页面,但是是个空页面[![新闻列表](:/87d48c64a84a4b6e8b25ed9bdddb6785)](https: //img2020.cnblogs.com/blog/516671 /202102 /516671 -20210220092914285 -1553601342 .png 这篇文章将会讲解如何实现增删查改功能。 一切开始之前,新建表news,建表SQL如下
CREATE TABLE news
( id
int(11) NOT NULL AUTO_INCREMENT COMMENT ‘主键’, title
varchar(128) NOT NULL COMMENT ‘新闻标题’, brief
varchar(256) DEFAULT NULL COMMENT ‘新闻概述’, content
text COMMENT ‘新闻正文’, create_time
datetime DEFAULT NULL, create_by
varchar(64) DEFAULT NULL, update_time
datetime DEFAULT NULL, update_by
varchar(64) DEFAULT NULL, delete_flag
tinyint(1) DEFAULT NULL, PRIMARY KEY (id
) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 可以仿照角色管理的页面写,直接将角色管理页面的代码直接拷贝到news文件中,效果如下 <img width="900" height="430" src=":/97fe3e71d0f0446a9c5b0442c381529b" />](https:/ /img2020.cnblogs.com/ blog/516671/ 202102 /516671 -20210220092914489 -244471663 .png;%20 charset=UTF-8 ) 没错,新闻列表的标题,角色管理的页面。。。 [![还有这种操作?](:/015813bc80094600a9a1e8fa5a4ef0f3)](https:/ /img2020.cnblogs.com/ blog/516671/ 202102 /516671 -20210220092914694 -893380236 .png;%20 charset=UTF-8 ) 之后对页面中元素进行修改,使其和上面创建的表结构一致,修改后的页面样子 <img width="900" height="388" src=":/8e8b45da2bb145b68c3d6fedeee5a74a" />](https:/ /img2020.cnblogs.com/ blog/516671/ 202102 /516671 -20210220092914948 -2028729040 .png;%20 charset=UTF-8 ) 这是预期中的样子,但是内容还是角色管理页面的内容。 上一步已经完成了页面外观的改造,接下来需要修改页面内容了,首先需要把按钮权限给加上 <img width="900" height="594" src=":/caa72bc567a2407ca5995cd0cdcb7bc3" />](https:/ /img2020.cnblogs.com/ blog/516671/ 202102 /516671 -20210220092915138 -1025251050 .png;%20 charset=UTF-8 ) 按照这个样子添加按钮权限,之后把权限标志分配到前端页面中 <img width="900" height="502" src=":/48b1876708d542ff9723844b8b1acc1c" />](https:/ /img2020.cnblogs.com/ blog/516671/ 202102 /516671 -20210220092915331 -1670317335 .png;%20 charset=UTF-8 ) 在系统工具-代码生成页面中生成news表对应的相关实体类、mapper、xml对象等,可以极大的简化开发过程。 <img width="900" height="373" src=":/15190851985649ca9bfa8b8369eea775" />](https:/ /img2020.cnblogs.com/ blog/516671/ 202102 /516671 -20210220092915606 -1157802840 .png;%20 charset=UTF-8 ) 将上一步代码生成器生成的`NewsController`拿过来改一改就行,修改后的代码如下所示:
package com.ruoyi.web.controller.business;
import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.system.domain.News; import com.ruoyi.system.mapper.NewsMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*;
import java.util.Date; import java.util.List;
/**
@author kdyzm */ @RestController @RequestMapping(“/business/news”) public class NewsController extends BaseController {
@Autowired private NewsMapper newsMapper;
/**
获取新闻列表 */ @PreAuthorize(“@ss.hasPermi(‘business:news:list’)”) @GetMapping(“/list”) public TableDataInfo list(News post) { startPage(); List list = newsMapper.selectNewsList(post); return getDataTable(list); }
@Log(title = “新闻管理”, businessType = BusinessType.EXPORT) @PreAuthorize(“@ss.hasPermi(‘business:news:export’)”) @GetMapping(“/export”) public AjaxResult export(News post) { List list = newsMapper.selectNewsList(post); ExcelUtil util = new ExcelUtil<>(News.class); return util.exportExcel(list, “新闻数据”); }
/**
根据新闻编号获取详细信息 */ @PreAuthorize(“@ss.hasPermi(‘business:news:query’)”) @GetMapping(value = “/{postId}”) public AjaxResult getInfo(@PathVariable Long postId) { return AjaxResult.success(newsMapper.selectNewsById(postId)); }
/**
新增新闻 */ @PreAuthorize(“@ss.hasPermi(‘business:news:add’)”) @Log(title = “新闻管理”, businessType = BusinessType.INSERT) @PostMapping public AjaxResult add(@Validated @RequestBody News post) { post.setCreateBy(SecurityUtils.getUsername()); post.setCreateTime(new Date()); return toAjax(newsMapper.insertNews(post)); }
/**
修改新闻 */ @PreAuthorize(“@ss.hasPermi(‘business:news:update’)”) @Log(title = “新闻管理”, businessType = BusinessType.UPDATE) @PutMapping public AjaxResult edit(@Validated @RequestBody News post) { post.setUpdateBy(SecurityUtils.getUsername()); return toAjax(newsMapper.updateNews(post)); }
/**
删除新闻 */ @PreAuthorize(“@ss.hasPermi(‘business:news:delete’)”) @Log(title = “新闻管理”, businessType = BusinessType.DELETE) @DeleteMapping(“/{postIds}”) public AjaxResult remove(@PathVariable Long[] postIds) { return toAjax(newsMapper.deleteNewsByIds(postIds)); } }
1 2 3 4 ### 5. 修改前端页面请求地址 将生成的代码中的news.js文件放到api目录,并修改其中的接口路径与后端接口地址一一对应。
import request from ‘@/utils/request’
// 查询角色列表 export function listNews(query) { return request({ url: ‘/business/news/list’, method: ‘get’, params: query }) }
// 查询角色详细 export function getNews(roleId) { return request({ url: ‘/business/news/‘ + roleId, method: ‘get’ }) }
// 新增角色 export function addNews(data) { return request({ url: ‘/business/news’, method: ‘post’, data: data }) }
// 修改角色 export function updateNews(data) { return request({ url: ‘/business/news’, method: ‘put’, data: data }) }
// 删除角色 export function delNews(roleId) { return request({ url: ‘/business/news/‘ + roleId, method: ‘delete’ }) }
// 导出角色 export function exportNews(query) { return request({ url: ‘/business/news/export’, method: ‘get’, params: query }) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 然后修改页面中的请求地址使用这里的地址。 超级管理员拥有最大权限,所有权限校验都会跳过对超级管理员的权限校验。这里先使用超级管理员进行测试可以规避权限问题,大体上先看看能否跑的通。 下面是演示超级管理员的CRUD操作。 <img width="900" height="486" src=":/8c8fefe4b4034d35a79dc9fc3dd9e23d" />](https:/ /img2020.cnblogs.com/ blog/516671/ 202102 /516671 -20210220092915844 -1904245767 .gif;%20 charset=UTF-8 ) 这里用用户kdyzm进行测试,在测试之前,先看下kdyzm的角色 [![image-20210220090846893 ](:/4c5fe14d38aa4d3bb05aa7a3afd993c6)](https:/ /img2020.cnblogs.com/ blog/516671/ 202102 /516671 -20210220092918588 -303247302 .png;%20 charset=UTF-8 ) 可以看到该用户是运营角色,那么修改角色权限,只给查询、修改、新增权限,不给导出和删除权限,如下所示 [![image-20210220091032435 ](:/05b812b6b510428cbe943ea07976f59a)](https:/ /img2020.cnblogs.com/ blog/516671/ 202102 /516671 -20210220092918795 -1100979659 .png;%20 charset=UTF-8 ) 这时候切换登录用户为kdyzm,看看新闻列表页面 <img width="900" height="250" src=":/0750a05996764ad8a1646013ad54e3c4" />](https:/ /img2020.cnblogs.com/ blog/516671/ 202102 /516671 -20210220092919018 -468519097 .png;%20 charset=UTF-8 ) 可以看到,kdyzm在新闻列表页面中,看不到导出导出按钮和删除按钮,符合预期设想。