一、RBAC模型
1.什么是RBAC葬荷?
RBAC(Role-BasedAccessControl )基于角色的訪問控制。
RBAC 認(rèn)為權(quán)限的過程可以抽象概括為: 判斷【W(wǎng)ho 是否可以對 What 進行 How 的訪問操作(Operator)】
Who:權(quán)限的擁用者或主體 ;
What:權(quán)限針對的對象或資源 ;
How:具體的權(quán)限 ;
Operator:操作纽帖。表明對 What 的 How 操作宠漩。也就是 Privilege+Resource; Role:角色,一定數(shù)量的權(quán)限的集合懊直。 權(quán)限分配的單位與載體,目的是隔離User與Privilege 的邏輯關(guān)系扒吁。
2.RBAC模型分類:
RBAC96 模型家族,其中包括了 RBAC0~RBAC3 四個概念模型。
- RBAC0:
定義了能構(gòu)成一個 RBAC 控制系統(tǒng)的最小的元素集合室囊。
在 RBAC 之中,包含用戶 users(USERS)雕崩、角色 roles(ROLES)、目標(biāo) objects(OBS)融撞、操作 operations(OPS)盼铁、許可權(quán) permissions(PRMS)五個基本數(shù)據(jù)元素,權(quán)限被賦予角色,而不是用 戶尝偎,當(dāng)一個角色被指定給一個用戶時饶火,此用戶就擁有了該角色所包含的權(quán)限。會話 sessions 是用戶與激活的角色集合之間的映射致扯。
RBAC0 與傳統(tǒng)訪問控制的差別在于增加一層間接性 帶來了靈活性肤寝,RBAC1、 RBAC2抖僵、RBAC3 都是先后在 RBAC0 上的擴展鲤看。
3.RBAC0:
- RBAC1:
角色間的繼承關(guān)系可分為一般繼承關(guān)系和受限繼承關(guān)系。一般繼承關(guān)系僅要求角色繼承 關(guān)系是一個絕對偏序關(guān)系耍群,允許角色間的多繼承义桂。而受限繼承關(guān)系則進一步要求角色繼承關(guān) 系是一個樹結(jié)構(gòu)找筝。
- RBAC2:
RBAC2 的約束規(guī)定了權(quán)限被賦予角色時,或角色被賦予用戶時,以及當(dāng)用戶在某一時刻 激活一個角色時所應(yīng)遵循的強制性規(guī)則。責(zé)任分離包括靜態(tài)責(zé)任分離和動態(tài)責(zé)任分離慷吊。約束 與用戶-角色-權(quán)限關(guān)系一起決定了 RBAC2 模型中用戶的訪問許可呻征。
- RBAC3:
RBAC3 包含了 RBAC1 和 RBAC2 既提供了角色間的繼承關(guān)系,又提供了責(zé)任分離關(guān)系罢浇。
二陆赋、RBAC項目分析
1.需求:
(1)實現(xiàn)用戶登錄功能;
(2)使用 RBAC0 模型管理系統(tǒng)權(quán)限;
(3)對系統(tǒng)的菜單以及菜單中的鏈接進行管理;
(4)用戶登錄后首頁根據(jù)用戶角色顯示該角色所對應(yīng)的菜單;
(5)禁止用戶越級訪問。
2.數(shù)據(jù)庫設(shè)計:
一個角色對多個用戶嚷闭;多個角色對多個菜單攒岛,創(chuàng)建中間表;一個菜單對多個功能操作胞锰。
- 創(chuàng)建表:
(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).導(dǎo)包:
(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>
<!-- 對靜態(tài)資源放行 -->
<mvc:default-servlet-handler />
<!-- 視圖解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 配置靜態(tài)資源映射 -->
<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)創(chuàng)建項目需要的包:
4.創(chuàng)建實體類:
(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數(shù)據(jù)訪問層:
<!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" />
<!-- 配置關(guān)聯(lián)對象Roles -->
<association property="roles" javaType="com.zlw.pojo.Roles">
<id property="roleid" column="roleid" />
<result property="rolename" column="rolename" />
</association>
<!-- 配置關(guān)聯(lián)對象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>
<!-- 配置關(guān)聯(lián)對象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業(yè)務(wù)層:
@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);
}
}
-
實現(xiàn)基本效果:
5.使用Dtree組件實現(xiàn)菜單的自動生成:
- 使用過濾器限制除登錄頁面的訪問:
繼承Filter接口:import javax.servlet.Filter嗅榕;Filter中的方法顺饮。在沒有通過登錄頁面訪問其他頁面都會被拒絕,并跳轉(zhuǎn)到登錄界面凌那。
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();
//判斷當(dāng)前訪問的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>
- 實現(xiàn)菜單的生成:
在項目中使用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)菜單展現(xiàn)的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 ;
//根節(jié)點
<%
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>
-
實現(xiàn)的效果:
6.權(quán)限的管理:
主要解決越級訪問問題;所謂的越級訪問就是使用低級別的角色訪問高級別的資源励稳。
-
將數(shù)據(jù)錄入資源管理數(shù)據(jù)庫中:
- 創(chuàng)建權(quán)限過濾器:
繼承Filter接口:import javax.servlet.Filter佃乘;實現(xiàn)Filter中的方法;先對靜態(tài)資源和用戶登錄放行驹尼;通過判斷前訪問的 URI 是否在功能數(shù)據(jù)中包
含趣避;決定是否賦予訪問的權(quán)限。
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();
//對靜態(tài)頁面做放行處理
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();
//定義一個開關(guān)控制
boolean flag = false;
for(Funs f:funs) {
System.out.println(uri);
//判斷當(dāng)前訪問的URI是否在功能數(shù)據(jù)中包含
if(uri.indexOf(f.getFunurl())!=-1) {
flag = true;
break;
}
}
System.out.println(flag);
//根據(jù)開關(guān)的值進行跳轉(zhuǎn)
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>
-
實現(xiàn)的效果: