当前位置:   article > 正文

若依框架中权限管理的后端实现_若依后端框架

若依后端框架

RBAC模型介绍

  • 若依框架的权限管理功能是基于RBAC模型来实现的,即:系统中所有的权限,都是基于角色来控制的
  • 框架对权限的控制,不仅支持菜单的功能,还支持菜单中每一个按钮的权限控制

RBAC模型包含的表有下面五张:
1. 用户表
2. 角色表
3. 权限菜单表
4. 用户和角色的关联表
5. 角色和权限菜单的关联表
关系如下:

权限的分类

  • 菜单权限
    用户登录之后能看见哪些菜单
  • 按钮权限
    用户在页面上看见哪些按钮,如新增、删除、修改、导出等按钮
  • 接口权限
    用户能发送哪些请求给后端接口,与按钮一一对应
  • 数据权限
    用户有权限访问后端的某个接口后,能看到哪些数据,如权限大的用户能看到更多的数据

菜单权限和按钮权限的实现

具体代码如下:
controller层:SysLoginController下的getRouters()方法

    /**
     * 获取路由信息
     * 
     * @return 路由信息
     */
    @GetMapping("getRouters")
    public AjaxResult getRouters()
    {
        Long userId = SecurityUtils.getUserId();
        List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
        return AjaxResult.success(menuService.buildMenus(menus));
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

service层:SysMenuServiceImpl下的selectMenuTreeByUserId()方法

    /**
     * 根据用户ID查询菜单
     * 
     * @param userId 用户名称
     * @return 菜单列表
     */
    @Override
    public List<SysMenu> selectMenuTreeByUserId(Long userId)
    {
        List<SysMenu> menus = null;
        if (SecurityUtils.isAdmin(userId))
        {
            menus = menuMapper.selectMenuTreeAll();
        }
        else
        {
            menus = menuMapper.selectMenuTreeByUserId(userId);
        }
        return getChildPerms(menus, 0);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

先判断用户是否为超级管理员,若是,则返回所有权限菜单,若不是,则根据用户id返回菜单

mapper层:SysMenuMapper

超级管理员执行的sql

    /**
     * 根据用户ID查询菜单
     *
     * @return 菜单列表
     */
    public List<SysMenu> selectMenuTreeAll();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
	<select id="selectMenuTreeAll" resultMap="SysMenuResult">
		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, 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 where m.menu_type in ('M', 'C') and m.status = 0
		order by m.parent_id, m.order_num
	</select>
  • 1
  • 2
  • 3
  • 4
  • 5

其他用户执行的sql

    /**
     * 根据用户ID查询菜单
     *
     * @param userId 用户ID
     * @return 菜单列表
     */
    public List<SysMenu> selectMenuTreeByUserId(Long userId);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
	<select id="selectMenuListByUserId" parameterType="SysMenu" resultMap="SysMenuResult">
		select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, 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
		where ur.user_id = #{params.userId}
		<if test="menuName != null and menuName != ''">
            AND m.menu_name like concat('%', #{menuName}, '%')
		</if>
		<if test="visible != null and visible != ''">
            AND m.visible = #{visible}
		</if>
		<if test="status != null and status != ''">
            AND m.status = #{status}
		</if>
		order by m.parent_id, m.order_num
	</select>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

接口权限的实现

以获取用户列表的接口为例

    /**
     * 获取用户列表
     */
    @PreAuthorize("@ss.hasPermi('system:user:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysUser user)
    {
        startPage();
        List<SysUser> list = userService.selectUserList(user);
        return getDataTable(list);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

接口权限是基于SpringSecurity的注解@PreAuthorize实现的
若依框架在PermissionService中重新定义了hasPermi()方法

/**
 * RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母
 * 
 * @author ruoyi
 */
@Service("ss")
public class PermissionService
{
	.
	.
	.
	
    /**
     * 验证用户是否具备某权限
     * 
     * @param permission 权限字符串
     * @return 用户是否具备某权限
     */
    public boolean hasPermi(String permission)
    {
        if (StringUtils.isEmpty(permission))
        {
            return false;
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions()))
        {
            return false;
        }
        PermissionContextHolder.setContext(permission);
        return hasPermissions(loginUser.getPermissions(), permission);
    }
    .
    .
    .
}
  • 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

每次带有注解@PreAuthorize的请求过来,就会先执行hasPermi(),判断是否具备某权限,若返回false则没有权限,若返回true,则有权限,可以执行该接口

数据权限的实现

数据权限的核心就是使用aop切面,拼接sql语句

这里的dataScope参数通过实行切面传入
在service层里添加注解@DataScope,这是若依框架中的一个自定义注解

通过监听这个注解来执行切面,DataScopeAspect为定义切面的类
这里的基本思想就是把要拼接的sql语句传到基类中的params里,params是BaseEntity类中的一个map类型的属性

这里第一步是清空params里面的dataScope,然后再判断其是否具有权限,再把拼接后的sql语句放到params里的dataScope里面

    /**
     * 拼接权限sql前先清空params.dataScope参数防止注入
     */
    private void clearDataScope(final JoinPoint joinPoint)
    {
        Object params = joinPoint.getArgs()[0];
        if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
        {
            BaseEntity baseEntity = (BaseEntity) params;
            baseEntity.getParams().put(DATA_SCOPE, "");
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
    protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope)
    {
        // 获取当前的用户
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (StringUtils.isNotNull(loginUser))
        {
            SysUser currentUser = loginUser.getUser();
            // 如果是超级管理员,则不过滤数据
            if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
            {
                String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());
                dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
                        controllerDataScope.userAlias(), permission);
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

数据权限分为五种,分别是全部数据权限、自定数据权限、部门数据权限、部门及以下数据权限、仅本人数据权限,拼接对应的sql语句


最后将拼接好后的sql语句放到params.dataScope里面

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/48777
推荐阅读
相关标签
  

闽ICP备14008679号