通用用户权限模型实现

末鹿安然 提交于 2020-08-18 21:15:56

1. RBAC模型

现有业务系统都要用到用户权限功能,其中的理论基础就是RBAC(Role-Based Access Control)即:基于角色的权限控制。通过角色关联用户,角色关联权限的方式间接赋予用户权限。

RBAC模型可以分为:RBAC0、RBAC1、RBAC2、RBAC3 四种。其中RBAC0是基础,也是最简单的,相当于底层逻辑,RBAC1、RBAC2、RBAC3都是以RBAC0为基础的升级。

  • RBAC0 :最简单的用户、角色、权限模型 ; 
  • RBAC1: 相对于RBAC0模型,增加了子角色,引入了继承概念,即子角色可以继承父角色的所有权限
  • RBAC2: 基于RBAC0模型,增加了对角色的一些限制:角色互斥、基数约束、先决条件角色等。
  • RBAC3: 就是前面几种情况的合集,一般只有在非常复杂的系统中才会用到。

2. 常见权限数据库模型

  • 其中用户与角色是多对多,角色与菜单权限是多对多。
  • 角色与部门是多对多,主要是为了按此角色可以访问哪些部门的数据权限,通过此动态注入相关 sql,到达动态过滤效果。

比较复杂是的数据权限的实现,具体实现过程如下:

 a.定义数据过滤注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataFilter {
    /**  表的别名 */
    String tableAlias() default "";

    /**  true:没有本部门数据权限,也能查询本人数据 */
    boolean user() default true;

    /**  true:拥有子部门数据权限 */
    boolean subDept() default false;

    /**  部门ID */
    String deptId() default "dept_id";

    /**  用户ID */
    String userId() default "user_id";
}

b.动态SQL拦截注入

@Aspect
@Component
public class DataFilterAspect {
    @Autowired
    private SysDeptService sysDeptService;
    @Autowired
    private SysUserRoleService sysUserRoleService;
    @Autowired
    private SysRoleDeptService sysRoleDeptService;

    @Pointcut("@annotation(com.xb.common.annotation.DataFilter)")
    public void dataFilterCut() {

    }

    @Before("dataFilterCut()")
    public void dataFilter(JoinPoint point) throws Throwable {
        Object params = point.getArgs()[0];
        if(params != null && params instanceof Map){
            SysUserEntity user = ShiroUtils.getUserEntity();

            //如果不是超级管理员,则进行数据过滤
            if(user.getUserId() != Constant.SUPER_ADMIN){
                Map map = (Map)params;
                map.put(Constant.SQL_FILTER, getSQLFilter(user, point));
            }

            return ;
        }

        throw new RRException("数据权限接口,只能是Map类型参数,且不能为NULL");
    }

    /**
     * 获取数据过滤的SQL
     */
    private String getSQLFilter(SysUserEntity user, JoinPoint point){
        MethodSignature signature = (MethodSignature) point.getSignature();
        DataFilter dataFilter = signature.getMethod().getAnnotation(DataFilter.class);
        //获取表的别名
        String tableAlias = dataFilter.tableAlias();
        if(StringUtils.isNotBlank(tableAlias)){
            tableAlias +=  ".";
        }

        //部门ID列表
        Set<Long> deptIdList = new HashSet<>();

        //用户角色对应的部门ID列表
        List<Long> roleIdList = sysUserRoleService.queryRoleIdList(user.getUserId());
        if(roleIdList.size() > 0){
            List<Long> userDeptIdList = sysRoleDeptService.queryDeptIdList(roleIdList.toArray(new Long[roleIdList.size()]));
            deptIdList.addAll(userDeptIdList);
        }

        //用户子部门ID列表
        if(dataFilter.subDept()){
            List<Long> subDeptIdList = sysDeptService.getSubDeptIdList(user.getDeptId());
            deptIdList.addAll(subDeptIdList);
        }

        StringBuilder sqlFilter = new StringBuilder();
        sqlFilter.append(" (");

        if(deptIdList.size() > 0){
            sqlFilter.append(tableAlias).append(dataFilter.deptId()).append(" in(").append(StringUtils.join(deptIdList, ",")).append(")");
        }

        //没有本部门数据权限,也能查询本人数据
        if(dataFilter.user()){
            if(deptIdList.size() > 0){
                sqlFilter.append(" or ");
            }
            sqlFilter.append(tableAlias).append(dataFilter.userId()).append("=").append(user.getUserId());
        }

        sqlFilter.append(")");

        if(sqlFilter.toString().trim().equals("()")){
            return null;
        }

        return sqlFilter.toString();
    }
}

c.service方法使用示例

    @DataFilter(subDept = true, user = false, tableAlias = "t1")
    public List<SysDeptEntity> queryList(Map<String, Object> params){
        return baseMapper.queryList(params);
    }

d.动态加入sql_fliter

	<select id="queryList" resultType="com.xb.modules.sys.entity.SysDeptEntity">
		select t1.*,(select t2.name from sys_dept t2 where t2.dept_id=t1.parent_id)parentName from sys_dept t1 where 
			t1.del_flag = 0
		<if test="sql_filter != null">
			and ${sql_filter}
		</if>
	</select>

3. shiro 介绍

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

  • Authentication(认证):用户身份识别,通常被称为用户“登录”
  • Authorization(授权):访问控制。比如某个用户是否具有某个操作的使用权限。
  • Session Management(会话管理):特定于用户的会话管理,甚至在非web 或 EJB 应用程序。
  • Cryptography(加密):在对数据源使用加密算法加密的同时,保证易于使用。

Shiro 架构包含三个主要的理念:Subject,SecurityManager和 Realm。下面的图展示了这些组件如何相互作用,我们将在下面依次对其进行描述。

4. shiro 权限控制

Shiro 进行权限控制

四种主要方式 :

a、 在程序中 通过 Subject 编程方式进行权限控制,不常用。

b、 配置 Filter 实现 URL 级别粗粒度权限控制

  • xxx.html* = anon (未登录可以访问)
  • xxx.html* =authc (必须登录才能访问 )
  • xxx.html* = perms[权限] (需要特定权限才能访问)
  • xxx.html* = roles[角色] (需要特定角色才能访问 )

c、 配置代理,基于注解实现细粒度权限控制

  • @RequiresPermissions(权限)  需要特定权限才能访问
  • @RequiresRoles(角色)  需要特定角色才能访问
  • @RequiresAuthentication 需要认证才能访问

d、 在页面中使用 shiro 自定义标签实现 页面显示权限控制

  • <shiro:authenticated> 登录后才能访问
  • <shiro:hasPermission name="abc"> 需要特定权限才能访问
  • <shiro:hasRole name="abc"> 需要特定角色才能访问

 

参考: https://gitee.com/pengchua/webframework_merge/tree/master/web-framework/xb-admin

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!