基于角色的访问控制(RBAC)

一、基于角色的访问控制(RBAC)介绍

什么是基于角色的访问控制(RBAC)

Role-Based Access Control
基于角色的访问控制(RBAC)百度百科

其基本思想是,对系统操作的各种权限不是直接授予具体的用户,而是在用户集合与权限集合之间建立一个角色集合。
每一种角色对应一组相应的权限。
一旦用户被分配了适当的角色后,该用户就拥有此角色的所有操作权限。
这样做的好处是,不必在每次创建用户时都进行分配权限的操作,只要分配用户相应的角色即可,而且角色的权限变更比用户的权限变更要少得多,这样将简化用户的权限管理,减少系统的开销。


为什么要用RBAC

正如介绍中所说,不同的人拥有不同的权限,使得系统更加合理,分级治之!!!

二、RBAC的数据库设计

RBAC系统表设计思路

数据库表设计思路图

在这里插入图片描述


简单介绍下:

李四是这个系统的一个用户,他的角色是超级管理员admin,超级管理员的权限就是全部权限。

上述的五个表,只有三个表(用户表,角色表,菜单表)是真正的表,
用户角色表是将用户表和角色表通过主外键的关系相连接的,同样权限表就是将角色表和菜单表相互连接的表


数据库结构文件(用powerdesign打开)


用户表介绍

在这里插入图片描述


比较简单

角色表介绍

在这里插入图片描述


用户角色表

在这里插入图片描述

将用户表和角色表通过外键连接起来


菜单表介绍

在这里插入图片描述


这个表比较复杂,解释下

举个例子:

  • 商品管理(一级菜单):
    • 商品分类管理(二级菜单)
    • 商品信息(二级菜单)
菜单编号(主键)菜单名称父级菜单ID父级菜单名称菜单类型(几级菜单)菜单URL菜单编码菜单图标菜单排序
4商品管理01icon-product2
5商品分类4商品管理2crServelt?op=listcr:listcon-cr-list1
6商品信息4商品管理2productServelt?op=listproduct:listicon-product-list2

这里的菜单编码如cr:list意思就是 分类 列表,无非就是为了看明白该数据是实现那个业务逻辑,没什么特殊用意!!!
菜单排序则是商品管理是所有一级菜单中的第二个,商品分类是商品管理的第一个二级菜单,商品信息是商品管理的第二个二级菜单。

数据库表和数据SQL源码

源码链接

三、RBAC的功能实现

登录功能实现

login.jsp(不做重点介绍)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录页</title>
</head>
<body>
<form method="post" action="/ebuy/systemUserServlet?op=login">

    用户名:<input type="text" name="userName"><br>
    密码:<input type="password" name="userPwd"><br>
    <input type="submit" value="提交">
</form>
</body>
</html>


看代码看得出登录页面设置十分简单,只有一个提交表单(后期完善吧)

form表单请求的是/ebuy/systemUserServlet?op=login,一个servlet,并且传入参数op方便请求同一个servlet的不同方法。

当然登录是要访问数据库查询数据的,(代码后面留下来了,不重点说了!),根据用户名和密码来查询数据,dao层代码如下:

/**
 * 具有权限控制的用户数据访问实现类
 */
public class SystemUserDaoImpl implements SystemUserDao {
    @Override
    public SystemUser login(SystemUser systemUser) {
        Connection connection=null;
        PreparedStatement ps=null;
        ResultSet rs=null;
        try {


            String sql = "select * from system_userinfo where USERINFO_LOGINID=? and USERINFO_PASSWORD=? ";
            connection = JDBCUtils.getConnection();
            ps = connection.prepareStatement(sql);

            ps.setString(1, systemUser.getUserinfo_loginid());
            ps.setString(2,systemUser.getUserinfo_password());
            rs = ps.executeQuery();

            SystemUser systemUser1=createSystemUserByResultSet(rs);
            if (systemUser1.getUserinfo_loginid()==null){
                return null;
            }else {

                return systemUser1;
            }


        }catch (Exception e){
            e.printStackTrace();
        }

        try {
            rs.close();
            ps.close();
            connection.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }

    private SystemUser createSystemUserByResultSet(ResultSet rs) throws SQLException {
        SystemUser systemUser = new SystemUser();
        while (rs.next()){
            systemUser.setUserinfo_uid(rs.getString("userinfo_uid"));
            systemUser.setUserinfo_loginid(rs.getString("userinfo_loginid"));
            systemUser.setUserinfo_name(rs.getString("userinfo_name"));
            systemUser.setUserinfo_password(rs.getString("userinfo_password"));
            systemUser.setUserinfo_sex(rs.getString("userinfo_sex"));
            systemUser.setUserinfo_email(rs.getString("userinfo_email"));
            systemUser.setUserinfo_mobile(rs.getString("userinfo_mobile"));
            systemUser.setUserinfo_status(rs.getInt("userinfo_status"));
        }

        return systemUser;
    }
}


在这里插入图片描述


查询菜单功能

在登录成功后,我们根据登录用户的id来查询用户角色表中对应的角色id,然后通过角色菜单(权限表)来查询该用户的菜单项。

sql语句如下:

                String sql = "select p.* from system_role_permission rp ,system_permission p where 
                rp.permission_id=p.permission_id and rp.role_id=( select r.role_id from system_userrole ur,system_userinfo 
                u,system_role r where ur.userinfo_id=u.userinfo_uid and ur.role_id=r.role_id and u.userinfo_loginid=?) ";

然后将查询到的菜单项打包成集合,返回至index.jsp进行遍历

在这里插入图片描述


index页面通过include指令指令包含了两个jsp,分别是admin-header.jsp和admin-left.jsp。

在这里插入图片描述

admin-header.jsp
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

%>
<base href="<%=basePath%>">
<head>
    <link type="text/css" rel="stylesheet" href="css/style.css" />
    <script type="text/javascript" src="scripts/function-manage.js"></script>
</head>
<div id="header" class="wrap">
    <div id="logo"><img src="images/logo.gif" /></div>
    <div class="help"><a href="index.jsp">返回前台页面</a></div>
    <div class="navbar">
        <ul class="clearfix">
            <li class="current"><a href="manage/index.jsp">首页</a></li>
            <li><a href="manage/user.html">用户</a></li>
            <li><a href="manage/product.html">商品</a></li>
            <li><a href="manage/order.html">订单</a></li>
            <li><a href="manage/guestbook.html">留言</a></li>
            <li><a href="manage/news.html">新闻</a></li>
        </ul>
    </div>
</div>
<div id="childNav">
    <div class="welcome wrap">
        管理员&nbsp;&nbsp;${sessionScope.USER.userinfo_name}&nbsp;&nbsp;您好,今天是<%Date date=new Date();out.print(date);%>,欢迎回到管理后台。
    </div>
</div>
<div id="position" class="wrap">
    您现在的位置:<a href="index.jsp">易买网</a> &gt; 管理后台
</div>



该页面就只有两个重点,一个是basepath,一个是显示用户名。

  • basepath
    就是方便网页之间请求资源url的,不明白可以看这个

  • 显示用户名
    之前我们在登录请求servlet时,将查询到的用户表信息保存至session,通过jsp的session就可以获取到。


admin-left.jsp

代码源码:


<%@ page contentType="text/html;charset=UTF-8" language="java"   pageEncoding="UTF-8"%>
<html>
<head>
    <title>Title</title>
</head>
<div id="menu-mng" class="lefter">
    <div class="box">
        <dl>
            <c:forEach items="${sessionScope.systemPermissionList}" var="p1">

                <c:if test="${p1.parent_id==0}">

                    <dt>${p1.permission_name}</dt>

                    <c:forEach items="${sessionScope.systemPermissionList}" var="p2">

                        <c:if test="${p2.parent_id!=0&&p2.parent_id==p1.permission_id}">
                            <dd><em><a href="productClass-add.html">新增</a></em><a href="${p2.permission_url}">${p2.permission_name}</a></dd>
                        </c:if>
                    </c:forEach>
                </c:if>
        </c:forEach>
        </dl>
    </div>
</div>


上述的代码适用于二级菜单(多级不适用,多级也会使得页面过于乱)
在这里插入图片描述

在标题的url上我们是根据数据库查询到的
在这里插入图片描述


实现角色管理

上述我们已经说了角色管理等标题的url是数据库中查询出来的,然后我们可以看到该角色管理的url:systemRoleServlet?op=list
可以看出是请求systemRoleServlet这个servlet,然后请求的方法是list,就是把数据库的role角色信息全部查询出来


dao层数据访问层直接查询全部的角色信息,保存至role的集合中,然后返回至roleList页面。

在这里插入图片描述


roleList和index页面一样,同样包含header页面和left页面。

在这里插入图片描述


授权按钮

每个角色信息后面都有一个授权按钮,点击之后会根据不同的角色,显示当前角色具备的功能并“打钩”,其他功能则不“打钩”,效果如下:

当是超级管理员时:

在这里插入图片描述


商品管理员时:
在这里插入图片描述


那么是如何实现的呢???

显示vtree树

上述的菜单树其实是一个图层,

<div id="bg">
    </div>
    <div id="show">
        <span id="roleName"></span><br>
        <input id="btnclose" type="button" value="close" onclick="hidediv();"/>
        <ul id="treeDemo" class="ztree"></ul>
        <input id="btnOK" type="button" value="OK" onclick="savePermission();"/>
    </div>
</div>

当我们点击授权时,<a href="javascript:void(0);" onclick="showdiv('${role.role_id}','${role.role_name}')">授权</a>,调用showdiv()方法,

shoudiv()方法:


        var roleIds='';
function showdiv(roleId,roleName) {
            roleIds=roleId;

            //每点击一次进行一次刷新
            document.getElementById("bg").style.display = "block";
            document.getElementById("show").style.display = "block";
            $("#roleName").html(roleName+"拥有的权限");
            //ajax请求
            $.ajax({
                url:'systemPermissionServlet',
                type:'post',
                async:false,
                data:{
                    op:'getPermissionTreeByRoleId',
                    roleId:roleId
                },
                dataType:'json',
                success:function (data){
                    $.fn.zTree.init($("#treeDemo"), setting, data);

                }


            })



        }

该方法首先就是使得图层显现出来,然后执行ajax请求,然后ajax请求成功之后就会调用success函数,初始化树,从而将该被勾选的数据进行勾选。

授权角色并保存至数据库

点击图层的ok按钮,触发savePermission()方法,

function savePermission() {

            var zTree = $.fn.zTree.getZTreeObj("treeDemo");
            var nodes = zTree.getCheckedNodes();

            var permissionIds='';
            for (var i = 0; i < nodes.length; i++) {
                if (i==nodes.length-1){
                    //最后一个不加逗号
                    permissionIds+=nodes[i].id;
                    //跳出循环
                    break;
                }
                permissionIds+=nodes[i].id+",";

            };
            $.ajax({
                url:'systemPermissionServlet',
                type:'post',
                async:false,
                data:{
                    op:'updatePermission',
                    /*全局变量,在点击授权时,在*showdiv方法中已经赋值*/
                    roleId:roleIds,
                    permissionIdArray:permissionIds
                },
                dataType:'json',
                success:function (data){
                    alert(data.msg);

                }

            });
            alert("授权完成");
            hidediv();
        }

同上面的显示vtree。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐