美文网首页
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