1.1 权限管理介绍

什么权限

l 哪些功能可以被哪些用户使用

l 用户可以使用哪些功能

图解权限:





用户管理

查看日志

添加商品

权限解决方案:

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
drop table if exists
drop table if exists
 
create table
(
int not null
/* 权限名称  */
varchar(200),
/* 权限URL地址*/
varchar(200),
/* 权限菜单所属的父菜单ID */
int,
/* 此权限功能是否要显示在左边*/
boolean,
primary key(pid)
);
 
create table
(
rid int not null
/* 角色名称 */
rname varchar(200),
/* 角色介绍 */
detail varchar(200),
primary key(rid)
);
 
create table
(
rid int,
pid int,
primary key(rid,pid)
);
 
alter table privilegeadd constraint foreign key
references
 
alter table role_privilegeadd constraint foreign key
references
 
alter table role_privilegeadd constraint foreign key
references
 
SELECT *  FROM
SELECT *  FROM
SELECT *  FROM
 
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
SELECT *  FROM
SELECT *  FROM
INSERT INTO role (rname,detail)VALUES ('客服','拥有基本的权限');
INSERT INTO role_privilege (rid,pid)values
INSERT INTO role_privilege (rid,pid)values
INSERT INTO role_privilege (rid,pid)values
INSERT INTO role_privilege (rid,pid)values

查询某一个角色的权限信息SQL语句:

/*查询角色的权限信息*/
SELECT r.*,p.*  FROM role r INNER JOIN role_privilege rpON
INNER JOIN privilege pON

权限的实体类为:

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
}
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname
}
public String getPurl() {
return purl;
}
public void setPurl(String purl) {
this.purl
}
public Boolean getIsleft() {
return isleft;
}
public void setIsleft(Boolean isleft) {
this.isleft
}
public Privilege getPrivilege() {
return privilege;
}
public void setPrivilege(Privilege privilege) {
this.privilege
}
public Set<Privilege> getChildrens() {
return childrens;
}
public void setChildrens(Set<Privilege> childrens) {
this.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
}
public Role() {
}
// Property accessors
public Integer getRid() {
return this.rid;
}
public void setRid(Integer rid) {
this.rid
}
public String getRname() {
return this.rname;
}
 
public void setRname(String rname) {
this.rname
}
public String getDetail() {
return this.detail;
}
public void setDetail(String detail) {
this.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 通过pidprivilege加载具体的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-INFWEB-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>
系统管理</span>
<ul>
<li><span>用户管理</span>
<ul>
用户添加</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);
}
});