1.1 权限管理介绍
什么权限
l 哪些功能可以被哪些用户使用
l 用户可以使用哪些功能
图解权限:
用户管理 userAction_manager.action
查看日志 logAction_query.action
添加商品 goodsAction_save.action
权限解决方案:
l 如何知道用户有哪些权限: 把用户拥有权限的URL存储起来,根据URL判断就知道它是否有权限
l 用户的权限是根据项目的需求动态变化的,所以权限的本身也需要维护,权限要动态给用户分配、动态的回收
权限解决方案A:
权限模型B:
重点回顾:
权限就是控制功能的使用(功能对应着URL)。
对功能的控制就是对URL的访问控制。
在我们的程序中,一个功能对应一个或两个URL:
l 例如列表或删除功能,只对应一个URL.
l 例如添加或修改功能,对应两个URL:..add, ..addUI
权限的实现步骤:
1. 初始化权限数据:项目做好后,把需要控制的权限URL存储到权限表中
2. 超级管理员:此管理员. 默认拥有所有权限, 以后就通过此管理员给用户授权
分配权限
1.2 给角色分配权限,项目中创建若干个角色, 给每个角色授权
1.3 给用户分配角色, 用户的权限就是角色权限的集合
使用权限
l 登录、注销、主页面。
l 左侧的菜单是根据权限显示的。(没有权限的菜单不可见)
l 右侧页面中的链接是根据权限显示的。(没有权限的删除和更新不可见)
l 权限分类:有些权限默认所有用户都拥有. 例如注销、登录…….
创建角色与权限的表结构
(他们是多对多的关系,创建完毕之后有3张表)
/*------权限表用来存储权限名称和权限URL-------*/ drop table if exists role_privilege; drop table if exists privilege; drop table if exists role;
create table privilege ( pid int not null auto_increment, /* 权限名称 */ pname varchar(200), /* 权限URL地址*/ purl varchar(200), /* 权限菜单所属的父菜单ID */ parentId int, /* 此权限功能是否要显示在左边*/ isleft boolean, primary key(pid) );
create table role ( rid int not null auto_increment, /* 角色名称 */ rname varchar(200), /* 角色介绍 */ detail varchar(200), primary key(rid) );
create table role_privilege ( rid int, pid int, primary key(rid,pid) );
alter table privilegeadd constraint foreign key (parentId) references privilege (pid);
alter table role_privilegeadd constraint foreign key (rid) references role (rid);
alter table role_privilegeadd constraint foreign key (pid) references privilege (pid);
SELECT * FROM privilege; SELECT * FROM role; SELECT * FROM role_privilege;
INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('商品管理','goodsAction_all',null,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('类别管理','categoryAction_all',null,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('订单管理','forderAction_all',null,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('会员管理','usersAction_all',null,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('角色管理','roleAction_all',null,true);
INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('商品添加','goodsAction_saveUI',1,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('商品添加实现','goodsAction_save',1,false); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('商品查询','goodsAction_queryUI',1,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('商品查询实现','goodsAction_query',1,false); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('商品更新','goodsAction_updateUI',1,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('商品更新实现','goodsAction_update',1,false); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('商品删除','goodsAction_delete',1,true);
INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('类别添加','categoryAction_saveUI',2,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('类别添加实现','categoryAction_save',2,false); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('类别查询','categoryAction_queryUI',2,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('类别查询实现','categoryAction_query',2,false); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('类别更新','categoryAction_updateUI',2,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('类别更新实现','categoryAction_update',2,false); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('类别删除','categoryAction_delete',2,true);
INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('订单查询','forderAction_queryUI',3,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('订单查询实现','forderAction_query',3,false); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('订单修改','forderAction_updateUI',3,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('订单修改实现','forderAction_update',3,false); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('订单删除','forderAction_delete',3,true);
INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('会员查询','usersAction_queryUI',4,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('会员查询实现','usersAction_query',4,false); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('会员修改','usersAction_updateUI',4,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('会员修改实现','usersAction_update',4,false); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('会员删除','usersAction_delete',4,true);
INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('角色添加','roleAction_saveUI',5,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('角色添加实现','roleAction_save',5,false); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('角色查询','roleAction_queryUI',5,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('角色查询实现','roleAction_query',5,false); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('角色修改','roleAction_updateUI',5,true); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('角色修改实现','roleAction_update',5,false); INSERT INTO privilege (pname,purl,parentId,isleft)VALUES ('角色删除','roleAction_delete',5,true);
SELECT * FROM privilege; SELECT * FROM role; SELECT * FROM role_privilege; INSERT INTO role (rname,detail)VALUES ('客服','拥有基本的权限'); INSERT INTO role_privilege (rid,pid)values (1,1); INSERT INTO role_privilege (rid,pid)values (1,23); INSERT INTO role_privilege (rid,pid)values (1,13); INSERT INTO role_privilege (rid,pid)values (1,11);
|
查询某一个角色的权限信息SQL语句:
/*查询角色的权限信息*/ SELECT r.*,p.* FROM role r INNER JOIN role_privilege rpON r.rid=rp.rid INNER JOIN privilege pON rp.pid=p.pid; |
权限的实体类为:
public class Privilegeimplements java.io.Serializable {
private static final long serialVersionUID = -3096838392822714616L;
private Integer pid; private String pname; private String purl; private Boolean isleft; /* 如果此菜单拥有父菜单,则父菜单存储到此属性中*/ private Privilege privilege; /* 如果此菜单有子菜单,则子菜单可以存储到此属性中*/ private Set<Privilege>childrens = new HashSet<Privilege>(0); public Integer getPid() { return pid; } public void setPid(Integer pid) { this.pid = pid; } public String getPname() { return pname; } public void setPname(String pname) { this.pname = pname; } public String getPurl() { return purl; } public void setPurl(String purl) { this.purl = purl; } public Boolean getIsleft() { return isleft; } public void setIsleft(Boolean isleft) { this.isleft = isleft; } public Privilege getPrivilege() { return privilege; } public void setPrivilege(Privilege privilege) { this.privilege = privilege; } public Set<Privilege> getChildrens() { return childrens; } public void setChildrens(Set<Privilege> childrens) { this.childrens = childrens; } } |
角色的实体为:
public class Roleimplements java.io.Serializable {
private static final long serialVersionUID = -7851427548653281059L;
private Integer rid; private String rname; private String detail; private Set<Privilege>privileges = new HashSet<Privilege>(0);
public Set<Privilege> getPrivileges() { return privileges; } public void setPrivileges(Set<Privilege> privileges) { this.privileges = privileges; } public Role() { } // Property accessors public Integer getRid() { return this.rid; } public void setRid(Integer rid) { this.rid = rid; } public String getRname() { return this.rname; }
public void setRname(String rname) { this.rname = rname; } public String getDetail() { return this.detail; } public void setDetail(String detail) { this.detail = detail; } } |
角色的映射文件为:
<hibernate-mapping> <class name="cn.itcast.shop.pojo.Role" table="role"> <id name="rid" type="java.lang.Integer"> <column name="rid" /> <generator class="native" /> </id>
<property name="rname" type="java.lang.String"> <column name="rname" length="100" /> </property>
<property name="detail" type="java.lang.String"> <column name="detail" length="200" /> </property>
<set name="privileges" table="role_privilege" catalog="shop"> <key> <!-- 外键 rid --> <column name="rid" not-null="true" /> </key> <!-- rid 匹配的 pid 通过pid 去privilege加载具体的privilege实体 --> <many-to-many entity-name="cn.itcast.shop.pojo.Privilege"> <column name="pid" not-null="true" /> </many-to-many> </set> </class> </hibernate-mapping> |
权限的映射文件为:
<hibernate-mapping> <class name="cn.itcast.shop.pojo.Privilege" table="privilege"> <id name="pid" type="java.lang.Integer"> <column name="pid" /> <generator class="native"></generator> </id> <property name="pname" type="java.lang.String"> <column name="pname" length="100" /> </property> <property name="purl" type="java.lang.String"> <column name="purl" length="200" /> </property> <property name="isleft" type="java.lang.Boolean"> <column name="isleft" /> </property>
<many-to-one name="privilege" class="cn.itcast.shop.pojo.Privilege" fetch="select"> <column name="parentId" /> </many-to-one>
<set name="childrens" inverse="true"> <key> <column name="parentId" /> </key> <one-to-many class="cn.itcast.shop.pojo.Privilege" /> </set> </class> </hibernate-mapping> |
完成角色的 CRUD操作
由于权限是要先赋值给角色的,所以先完成角色的CRUD操作
给角色授权, 如果此角色已经有权限则回显
Action核心代码如下:
public String updateUI() { // 获取当前角色信息,其中包括,权限信息,为了方便回显 Role role = roleService.get(model.getRid()); session.put("role", role); // 获取当前用户的权限pid用于回显 String pids = privilegeService.getPrivilegePid(role.getPrivileges()); request.put("pids", pids); // 查询系统所有权限,因为此功能常见,而且权限不会改变,所以可以存储到app中 request.put("privileges",privilegeService.query()); return "updateUI"; }
public String update() { Set<Privilege> privileges = new HashSet<Privilege>(); for (int pid :pids) { Privilege privilege = new Privilege(); privilege.setPid(pid); privileges.add(privilege); } Role role = (Role) session.get("role"); role.setPrivileges(privileges); roleService.update(role); return "queryUI"; } |
采用contains标签来回显示:
<function> <description> Tests if an input string contains the specified substring. </description> <name>contains</name> <function-class>org.apache.taglibs.standard.functions.Functions</function-class> <function-signature>boolean contains(java.lang.String, java.lang.String)</function-signature> <example> <c:if test="${fn:contains(name, searchString)}"> </example> </function> |
回显示页面代码如下:
<body> 您正在更新的角色为:[${role.rname}]: <form action="${pageContext.request.contextPath}/roleAction_update.action" method="post"> 所属权限为:<br/> <c:forEach items="${privileges}"var="privilege"> ${privilege.pid} <input type="checkbox" value="${privilege.pid}" name="pids" ${fn:contains(requestScope.pids,privilege.pid)?"checked":""}/>${privilege.pname}/${privilege.purl}<br/> </c:forEach> <input type="submit" value="更新" /> </form> </body> |
注意:采用此有错误.只支持字符串 如果选择了12 那么回显的时候1 和2 都会被选中.如果JSTL支持字符数组则不会出现此问题 但是JSTL没有字符数组函数,没有可以自定义
自定义JSTL函数回显
自定义函数库:
1、定义类和方法(方法必须是public static)
2、编写自定义tld文件,并且将此文件放到WEB-INF或WEB-INF任意子目录下
3、在jsp中采用taglib指令引入自定义函数库
4、采用 前缀+冒号(:)+函数名 调用即可
自定义类为:
public class MyJSTLFunction { public static boolean contains(String[] strArr, String searchString) { for (String temp : strArr) { if (temp.equals(searchString)) { return true; } } return false; } } |
MyJSTL.tld文件为:
<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0">
<description>JSTL 1.1 functions library</description> <display-name>JSTL functions</display-name> <tlib-version>1.1</tlib-version> <short-name>my</short-name> <uri>http://cn.itcast.shop.jstl/myjstl</uri>
<function> <name>contains</name> <function-class>cn.itcast.shop.jstl.MyJSTLFunction</function-class> <function-signature>boolean contains(java.lang.String[], java.lang.String)</function-signature> </function> </taglib> |
页面调用如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%> <%@ taglib uri="http://cn.itcast.shop.jstl/myjstl" prefix="my" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'updateUI.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css"href="styles.css"> -->
</head>
<body> 您正在更新的角色为:[${role.rname}]: <form action="${pageContext.request.contextPath}/roleAction_update.action" method="post"> 所属权限为:<br/> <c:forEach items="${privileges}"var="privilege"> ${privilege.pid} <input type="checkbox" value="${privilege.pid}" name="pids" ${my:contains(requestScope.pids,privilege.pid)?"checked":""}/>${privilege.pname}/${privilege.purl}<br/> </c:forEach> <input type="submit" value="更新" /> </form> </body> </html>
|
完成基于树状的权限查询
@SuppressWarnings("unchecked") public List<Privilege> queryPrivilegeTree() { return hibernateTemplate.find("FROM Privilege p WHERE p.privilege IS NULL"); } |
添加树形菜单的功能:
前面已经完成了对权限的授权操作, 但是界面显示的奇丑无比, 如何制作好看的显示外观呢.最典型的做法就是,采用树形菜单显示层次结构,在讲解树型菜单插件的时候我们先回顾一下采用ul li的方式如何来显示菜单操作
<script type="text/javascript" src="jquery-1.7.min.js"></script> <script type="text/javascript"> $(function(){ $("span").click(function(){ $(this).next().toggle(); }); }); </script>
<body> <ul> <li><span>系统管理</span> <ul> <li><span>用户管理</span> <ul> <li>用户添加</li> <li>用户查询</li> </ul> </li> <li>订单管理</li> <li>类别管理</li> </ul> </li> <ul> </body> |
页面显示代码如下:
您正在更新的角色为:[${role.rname}]: <form action="${pageContext.request.contextPath}/roleAction_update.action" method="post"> 所属权限为: <br /> <ul id="root" class="filetree"> <c:forEach items="${privileges}"var="privilege"> <li> <input id="chk_${privilege.pid}" type="checkbox" value="${privilege.pid}" name="pids" ${my:contains(requestScope.pids,privilege.pid)?"checked":""}/> <label for="chk_${privilege.pid}">${privilege.pname}</label> <ul> <c:forEach items="${privilege.childrens}"var="children"> <li> <input id="chk_${children.pid}" type="checkbox" value="${children.pid}" name="pids" ${my:contains(requestScope.pids,children.pid)?"checked":""}/> <label for="chk_${children.pid}">${children.pname}</label> </li> </c:forEach> </ul> </li> </c:forEach> </ul> <input type="submit" value="更新" /> </form> |
讲解树状结构的Jquery插件
|
Jquery完成父子关系权限按钮
$("[type=checkbox]").click(function(){ // 选中取消的时候同时选中取消下级权限 $(this).siblings("ul").find("input").attr("checked",this.checked); // 当选中某一个权限的时候,要选中上级的所有权限,取消的时候,则不取消 if(this.checked){ $(this).parents("li").children("input").attr("checked",true); } }); |