美文网首页
RBAC学习day-70:RBAC的实现

RBAC学习day-70:RBAC的实现

作者: 开源oo柒 | 来源:发表于2019-10-29 22:16 被阅读0次

    一、RBAC模型

    1.什么是RBAC?

    RBAC(Role-BasedAccessControl )基于角色的访问控制。
    RBAC 认为权限的过程可以抽象概括为: 判断【Who 是否可以对 What 进行 How 的访问操作(Operator)】
    Who:权限的拥用者或主体 ;
    What:权限针对的对象或资源 ;
    How:具体的权限 ;
    Operator:操作。表明对 What 的 How 操作。也就是 Privilege+Resource; Role:角色,一定数量的权限的集合。 权限分配的单位与载体,目的是隔离User与Privilege 的逻辑关系。

    2.RBAC模型分类:

    RBAC96 模型家族,其中包括了 RBAC0~RBAC3 四个概念模型。

    模型图
    • RBAC0:

    定义了能构成一个 RBAC 控制系统的最小的元素集合。
    在 RBAC 之中,包含用户 users(USERS)、角色 roles(ROLES)、目标 objects(OBS)、操作 operations(OPS)、许可权 permissions(PRMS)五个基本数据元素,权限被赋予角色,而不是用 户,当一个角色被指定给一个用户时,此用户就拥有了该角色所包含的权限。会话 sessions 是用户与激活的角色集合之间的映射。
    RBAC0 与传统访问控制的差别在于增加一层间接性 带来了灵活性,RBAC1、 RBAC2、RBAC3 都是先后在 RBAC0 上的扩展。

    3.RBAC0:

    详情图
    • RBAC1:

    角色间的继承关系可分为一般继承关系和受限继承关系。一般继承关系仅要求角色继承 关系是一个绝对偏序关系,允许角色间的多继承。而受限继承关系则进一步要求角色继承关 系是一个树结构。

    详情图
    • RBAC2:

    RBAC2 的约束规定了权限被赋予角色时,或角色被赋予用户时,以及当用户在某一时刻 激活一个角色时所应遵循的强制性规则。责任分离包括静态责任分离和动态责任分离。约束 与用户-角色-权限关系一起决定了 RBAC2 模型中用户的访问许可。

    详情
    • RBAC3:

    RBAC3 包含了 RBAC1 和 RBAC2 既提供了角色间的继承关系,又提供了责任分离关系。

    详情

    二、RBAC项目分析

    1.需求:

    (1)实现用户登录功能;
    (2)使用 RBAC0 模型管理系统权限;
    (3)对系统的菜单以及菜单中的链接进行管理;
    (4)用户登录后首页根据用户角色显示该角色所对应的菜单;
    (5)禁止用户越级访问。

    2.数据库设计:

    一个角色对多个用户;多个角色对多个菜单,创建中间表;一个菜单对多个功能操作。

    示例图
    • 创建表:
      (1)用户表:
    CREATE TABLE `users` (
      `userid` int(11) NOT NULL AUTO_INCREMENT,
      `username` varchar(20) DEFAULT NULL,
      `userpwd` varchar(30) DEFAULT NULL,
      `role_id` int(11) DEFAULT NULL,
      PRIMARY KEY (`userid`),
      KEY `role_id_pk` (`role_id`) USING BTREE,
      CONSTRAINT `users_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `roles` (`roleid`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
    

    (2)角色表:

    CREATE TABLE `roles` (
      `roleid` int(11) NOT NULL,
      `rolename` varchar(50) DEFAULT NULL,
      PRIMARY KEY (`roleid`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    

    (3)菜单表:

    CREATE TABLE `menus` (
      `menuid` int(11) NOT NULL AUTO_INCREMENT,
      `menuname` varchar(40) DEFAULT NULL,
      `menuurl` varchar(40) DEFAULT NULL,
      `fatherid` int(11) DEFAULT NULL,
      PRIMARY KEY (`menuid`)
    ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
    
    

    (4)菜单和角色的中间表:

    CREATE TABLE `roles_menus` (
      `roles_id` int(11) NOT NULL,
      `menus_id` int(11) NOT NULL,
      PRIMARY KEY (`roles_id`,`menus_id`),
      KEY `roles_menus_fk_id` (`menus_id`),
      CONSTRAINT `roles_menus_fk` FOREIGN KEY (`roles_id`) REFERENCES `roles` (`roleid`),
      CONSTRAINT `roles_menus_fk_id` FOREIGN KEY (`menus_id`) REFERENCES `menus` (`menuid`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    

    (5)功能表:

    CREATE TABLE `funs` (
      `funid` int(11) NOT NULL AUTO_INCREMENT,
      `funname` varchar(50) DEFAULT NULL,
      `funurl` varchar(50) DEFAULT NULL,
      `menu_id` int(11) DEFAULT NULL,
      PRIMARY KEY (`funid`),
      KEY `menu_id_fk` (`menu_id`),
      CONSTRAINT `menu_id_fk` FOREIGN KEY (`menu_id`) REFERENCES `menus` (`menuid`)
    ) ENGINE=InnoDB AUTO_INCREMENT=209 DEFAULT CHARSET=utf8;
    
    

    3.搭建ssm项目框架:

    (1).导包:


    jar文件

    (2)配置web.xml文件:

    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://java.sun.com/xml/ns/javaee"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
        id="WebApp_ID" version="3.0">
        <display-name>rbacDemo</display-name>
    
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc.xml</param-value>
            </init-param>
        </servlet>
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
        <!-- 字符编码 -->
        <filter>
            <filter-name>encod</filter-name>
            <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    
            <init-param>
                <param-name>encoding</param-name>
                <param-value>utf-8</param-value>
            </init-param>
        </filter>
    
        <filter-mapping>
            <filter-name>encod</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext-*.xml</param-value>
        </context-param>
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    </web-app>
    

    (3)配置springmvc.xml:

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:mvc="http://www.springframework.org/schema/mvc" 
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd 
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd">
        <!-- 扫描@Controller -->
        <context:component-scan base-package="com.zlw.controller"></context:component-scan>
        <!-- @RequestMapping -->
        <mvc:annotation-driven></mvc:annotation-driven>
        <!-- 对静态资源放行 -->
        <mvc:default-servlet-handler />
        <!-- 视图解析器 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/"></property>
            <property name="suffix" value=".jsp"></property>
        </bean>
        
        <!-- 配置静态资源映射 -->
        <mvc:resources location="/WEB-INF/css/" mapping="/css/**" />
        <mvc:resources location="/WEB-INF/js/" mapping="/js/**" />
        <mvc:resources location="/WEB-INF/images/" mapping="/images/**" />
    </beans>       
    

    (4)配置applicationContext-*.xml:

    <bean id="dataSource"
            class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
            <property name="url" value="jdbc:mysql://localhost:3306/rbac"></property>
            <property name="username" value="root"></property>
            <property name="password" value="root"></property>
        </bean>
        <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"></property>
            <property name="typeAliasesPackage" value="com.zlw.pojo"></property>
        </bean>
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="factory"></property>
            <property name="basePackage" value="com.zlw.mapper"></property>
        </bean>
        
        <!-- 扫描@Service -->
        <context:component-scan base-package="com.zlw.service"></context:component-scan>
    

    (5)创建项目需要的包:


    package

    4.创建实体类:

    (1)Users类:

        private int userid;//用户ID
        private String username;//用户名
        private String userpwd;//密码
        private Roles roles;//角色信息
        private List<Menus> menus = new ArrayList<Menus>();//菜单信息
        private List<Funs> funs = new ArrayList<Funs>();//功能信息
    

    (2)Roles类:

        private int roleid;//角色ID
        private String rolename;//角色名称
        private List<Menus> menus = new ArrayList<Menus>();//功能信息
    

    (3)Menus类:

        private int menuid;//菜单ID
        private String menuname;//菜单名称
        private String menuurl;//菜单url
        private int fatherid;//父级ID
        private List<Funs> funs = new ArrayList<Funs>();//功能信息
    

    (4)Funs类:

        private int funid;
        private String funname;
        private String funurl;
    
    • mapper数据访问层:
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    <mapper namespace="com.zlw.mapper.UsersMapper">
        <resultMap type="com.zlw.pojo.Users" id="userMapper">
            <id property="username" column="username" />
            <result property="userpwd" column="userpwd" />
            <!-- 配置关联对象Roles -->
            <association property="roles" javaType="com.zlw.pojo.Roles">
                <id property="roleid" column="roleid" />
                <result property="rolename" column="rolename" />
            </association>
            <!-- 配置关联对象Menus -->
            <collection property="menus" ofType="com.zlw.pojo.Menus">
                <id property="menuid" column="menuid" />
                <result property="menuname" column="menuname" />
                <result property="menuurl" column="menuurl" />
                <result property="fatherid" column="fatherid" />
            </collection>
            <!-- 配置关联对象Funs -->
            <collection property="funs" ofType="com.zlw.pojo.Funs">
                <id property="funid" column="funid" />
                <result property="funname" column="funname" />
                <result property="funurl" column="funurl" />
            </collection>
        </resultMap>
        <select id="findByName" resultMap="userMapper"
            parameterType="java.lang.String">
            select *from users u,roles r,roles_menus rm,menus m
            LEFT JOIN funs f ON m.menuid=f.menu_id
            where u.role_id=r.roleid
            and
            r.roleid=rm.roles_id
            and rm.menus_id=m.menuid
            and u.username=#{0}
        </select>
    </mapper>
    
    • Service业务层:
    @Service("userService")
    public class UserServiceImpl implements UserService {
    
        @Autowired
        private UsersMapper usersMapper;
        @Override
        public Users userLogin(String username, String pwd) {
            Users users = usersMapper.findByName(username);
            if(users ==null) {
                //用户不存在
                throw new UsersException("用户不存在!");
            }else if(!users.getUserpwd().equals(pwd)){
                //密码有误
                throw new UsersException("密码错误!");
            }
            return users;
        }
    }
    
    • Controller控制层:
    @Controller
    @RequestMapping("user")
    public class UserController {
        
        @Autowired
        private UserService userService;
        
        @RequestMapping("login")
        public String login(Users users,Model model,HttpSession session,HttpServletRequest request) {
            try {
                Users us= userService.userLogin(users.getUsername(), users.getUserpwd());
                System.out.println(us);
                session.setAttribute("user", us);
            } catch (Exception e) {
                e.printStackTrace();
                model.addAttribute("msg",e.getMessage());
                return "login";
            }
            
            return "redirect:/index.jsp";
        }
    }
    
    • jsp页面:
    <body>
        <h3 >用户登录</h3>
        <span style="color: red">${requestScope.msg }</span>
        <form action="user/login" method="post">
            <p>
                用户名:<input type="text" name="username"/>
            </p>
            <p>
                密码:<input type="password" name="userpwd"/>
            </p>
            <p>
                <input type="submit" value="登录"/>
            </p>
        </form>
    </body>
    
    • 自定义异常:
    package com.zlw.exception;
    /**
     * 自定义异常
     * @author zhang
     *
     */
    public class UsersException extends RuntimeException{
    
        public UsersException() {
            super();
        }
    
        public UsersException(String message, Throwable cause) {
            super(message, cause);
        }
    
        public UsersException(String message) {
            super(message);
        }
    }
    
    • 实现基本效果:


      登录界面
      自定义异常

    5.使用Dtree组件实现菜单的自动生成:

    • 使用过滤器限制除登录页面的访问:

    继承Filter接口:import javax.servlet.Filter;Filter中的方法。在没有通过登录页面访问其他页面都会被拒绝,并跳转到登录界面。

    public class UserLoginFilter implements Filter {
    
        @Override
        public void destroy() {
    
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse respose, FilterChain chain)
                throws IOException, ServletException {
            //获取用户访问的uri
            HttpServletRequest req = (HttpServletRequest) request;
            String uri = req.getRequestURI();
            //判断当前访问的uri是否是登录资源,如果是放行
            if(uri.indexOf("login")!=-1) {
                chain.doFilter(req, respose);
            }else {
                //用户是否登录的判断
                HttpSession session = req.getSession();
                Users users = (Users) session.getAttribute("user");
                if(users!=null&&users.getUsername().length()>0) {
                    chain.doFilter(req, respose);
                }else {
                    req.setAttribute("msg","请先进行登录!");
                    req.getRequestDispatcher("login.jsp").forward(req, respose);
                }
            }
        }
        @Override
        public void init(FilterConfig arg0) throws ServletException {
    
        }
    }
    

    (2)在web.xml中配置过滤器:

        <filter>
            <filter-name>UserLoginFilter</filter-name>
            <filter-class>com.zlw.filter.UserLoginFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>UserLoginFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
    • 实现菜单的生成:

    在项目中使用dtree的组件;并在主界面中搭建主框架;
    (1)主界面:

    <frameset rows="15%,*,10%" border="1">
        <frame src="head.jsp" scrolling="no" name="head.jsp">
        <frameset cols="20%,*">
            <frame src="menu.jsp" scrolling="auto" name="menu">
            <frame src="body.jsp" scrolling="auto" name="body">
        </frameset>
        <frame src="foot2.jsp" scrolling="auto" name="foot2.jsp">
    

    (2)菜单展现的jsp:

    <html>
    <head>
    
    <title>Insert title here</title>
        <SCRIPT language=javascript src="js/dtree/dtree.js"></SCRIPT>
        <script type="text/javascript" src="js/java-like.util.js"></script>
        <link rel="stylesheet" href="js/dtree/dtree.css" type="text/css">
    </head>
    <body>
        <form action="" name="form1" method=POST>
        <table width="100%" border="0" cellspacing="0" cellpadding="0">
            <tr>
                <td rowspan="5" width="1" bgcolor="CCCCCC"></td>
                <td bgcolor="CCCCCC" height="1"></td>
                <td rowspan="4" bgcolor="CCCCCC" width="1"></td>
            </tr>
    
            <tr>
                <td bgcolor="E3E7FF" align="center" height="5"></td>
            </tr>
    
            <tr>
                <td bgcolor="CCCCCC" height="1"></td>
            </tr>
    
            <tr>
                <td bgcolor="F9F9F9" align="center" valign="top">
                    <table width="90%" border="0" align="center" cellpadding="1" cellspacing="1" bgcolor="F5F5F5">
                        <tr bgcolor="F3F9FF">
                            <td bgcolor="F5F5F5">
                                <SCRIPT LANGUAGE="JavaScript">
                                    d = new dTree('d');
                                    d.config.target = "body";
                                    d.config.imageDir = 'js/dtree/img';
                                    d.reSetImagePath();
                                    d.config.folderLinks = false;
                                    d.config.closeSameLevel =true;
                                    var isOpen ;
                                    //根节点
                                    <%
                                        Users user = (Users)session.getAttribute("user");
                                        List menus = user.getMenus();
                                        for(int i=0;i<menus.size();i++){
                                        Menus menu = (Menus)menus.get(i);
                                    %>
                                     d.add(<%=menu.getMenuid() %>, <%=menu.getFatherid() %>, '<%=menu.getMenuname() %>', '<%=menu.getMenuurl() %>', '', 'body');
                                    <%}%>
                                    document.write(d);
                                </script>
                            </td>
                        </tr>
                    </table>
                </td>
            </tr>
    
            <tr>
                <td background="images/jao1.gif" colspan="2" align="right"><img
                        src="images/jao.gif" width="8" height="8"></td>
            </tr>
    
        </table>
    </form>
    </body>
    </html>
    
    • 实现的效果:


      实现菜单效果

    6.权限的管理:

    主要解决越级访问问题;所谓的越级访问就是使用低级别的角色访问高级别的资源。

    • 将数据录入资源管理数据库中:


      资源管理基础数据
    • 创建权限过滤器:

    继承Filter接口:import javax.servlet.Filter;实现Filter中的方法;先对静态资源和用户登录放行;通过判断前访问的 URI 是否在功能数据中包
    含;决定是否赋予访问的权限。

    public class SafeFilter implements Filter {
    
        @Override
        public void destroy() {
    
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse resp = (HttpServletResponse) response;
            String uri = req.getRequestURI();
            //对静态页面做放行处理
            if(uri.endsWith(".js")||uri.endsWith(".css")||uri.endsWith(".gif")) {
                chain.doFilter(request, response);      
            }else {
                //对用户的登录放行
                if(uri.indexOf("login")!=-1) {
                    chain.doFilter(request, response);  
                }else {
                    HttpSession session = req.getSession();
                    Users users = (Users) session.getAttribute("user");
                    List<Funs> funs = users.getFuns();
                    //定义一个开关控制
                    boolean flag = false;
                    for(Funs f:funs) {
                        System.out.println(uri);
                        //判断当前访问的URI是否在功能数据中包含
                        if(uri.indexOf(f.getFunurl())!=-1) {
                            flag = true;
                            break;
                        }
                    }
                    System.out.println(flag);
                    //根据开关的值进行跳转
                    if(flag) {
                        System.out.println(flag);
                        chain.doFilter(request, response);              
                    }else {
                        System.out.println("ok!");
                        resp.sendRedirect("roleerror.jsp");
                    }
                }
            }
            
        }
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            
        }
    }
    

    (2)配置web.xml中的过滤器:

        <filter>
            <filter-name>SafeFilter</filter-name>
            <filter-class>com.zlw.filter.SafeFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>SafeFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
    • 实现的效果:


    相关文章

      网友评论

          本文标题:RBAC学习day-70:RBAC的实现

          本文链接:https://www.haomeiwen.com/subject/nnfnvctx.html