java项目启动出现java多线程访问数据库库权限不够

一、首先分清楚系统中的岗位与角色的关系
& & & 在小的系统中,我们可以将岗位看作角色,根据岗位来进行权限的分配。但是在大公司这种方案就不适用了,岗位太多,所以我们将岗位提取出来,给具有共性的岗位赋予一个角色,然后给角色分配权限。
& &&权限角色是系统功能权限设置的基础,相当于用户分组,所有用户对应到相应权限角色,便具有该权限角色所赋予的所有功能权限。岗位是在组织架构下的精细岗位划分,是业务流程控制、业绩考核、预警体系的基础,不同的机构、部门下的同一职务是作为不同的岗位的。并没有把岗位直接作为系统功能权限设置的对象而是引入了权限角色概念,是因为岗位非常多,而很多不同的机构、部门下的同一职务拥有同样的功能权限,如果直接用岗位来设置将极大增加重复工作量。权限角色实际上有些相当于岗位权限分类的概念,即具有同样功能权限的岗位集合在一起,这样可以减少权限设置的工作量。
举例说明:
杂货部门饮料组主管和杂货部门粮油组主管是两个岗位,在业务流程控制、业绩考核、预警体系中是不同的,但其在系统中的功能模块权限是相同的,故都属于门店柜组主管这样一个权限角色。
二、权限分配的几种模板
我们给用户分配权限,不是直接给用户分配权限,因为用户太多了,这样分配效率太低。通常我们使用的是基于角色的权限模型,即用户,角色,权限。
三、如何实现权限的初始化数据:基于URL的权限管理初始化数据
首先我们看下权限树的结构:对比一下,下面两张表,就很清晰了为什么基于url来实现权限的分配,因为不同的url对应不同的功能
基于url的意思是:由于此项目比较小,我们就直接将岗位作为角色来进行权限的分配。而每个权限有相对应的url,即每个action,我们在网页上对每个特定的功能有特定的url访问链接。
看上面权限的数据库表,由于权限也有父子类关系,所以这里有parentId,同理,parentId为null的,则为顶级权限。
初始化数据采用了一个java类,挂载父子关系来实现的:
看到下面的初始化,我们可以很清楚的知道,权限管理是在所有的基本功能模块规划好之后进行一个设置:
由于只能初始化一次,所以这里我们单独使用了spring容器来初始化
package cn.itcast.oa.
import javax.annotation.R
import org.apache.catalina.core.ApplicationC
import org.apache.commons.codec.digest.DigestU
import org.hibernate.S
import org.hibernate.SessionF
import org.springframework.context.support.ClassPathXmlApplicationC
import org.springframework.stereotype.C
import org.springframework.transaction.annotation.T
import cn.itcast.oa.domain.P
import cn.itcast.oa.domain.U
//初始化权限数据,这里采用了创建一个java工具类,实现跨数据平台的权限数据的初始化
@Component
public class Installer {
private SessionFactory sessionF
//在这里实现权限数据的初始化
@Transactional
public void install(){
Session session=sessionFactory.getCurrentSession();
//设置超级管理员的信息
User user=new User();
user.setName("超级管理员");
user.setLoginName("admin");
user.setPassword(DigestUtils.md5Hex("admin"));
session.save(user);
//设置权限的各个数据,在数据库中,上下级权限只有有一个属性就可以将父子类绑定在一起
//系统管理下面有岗位管理,部门管理,用户管理,有个大类然后将子类挂在父类上,子类的下面还有孙子类,再讲孙子类挂在子类上面依次根据parent来挂载
Privilege menu,menu1,menu2,menu3,menu4,menu5;
menu=new Privilege("系统管理",null,null);
menu1=new Privilege("岗位管理","/role_list",menu);
menu2=new Privilege("部门管理","/department_list",menu);
menu3=new Privilege("用户管理","/user_list",menu);
session.save(menu);
session.save(menu1);
session.save(menu2);
session.save(menu3);
//将岗位管理和部门管理和用户管理的子类:列表,添加,修改,删除等添加到相对应的父类中
session.save(new Privilege("岗位列表","/role_list",menu1));
session.save(new Privilege("岗位删除","/role_delete",menu1));
session.save(new Privilege("岗位添加","/role_add",menu1));
session.save(new Privilege("岗位修改","/role_edit",menu1));
session.save(new Privilege("部门列表","/department_list",menu2));
session.save(new Privilege("部门删除","/department_delete",menu2));
session.save(new Privilege("部门添加","/department_add",menu2));
session.save(new Privilege("部门修改","/department_edit",menu2));
session.save(new Privilege("用户列表","/user_list",menu3));
session.save(new Privilege("用户删除","/user_delete",menu3));
session.save(new Privilege("用户添加","/user_add",menu3));
session.save(new Privilege("用户修改","/user_edit",menu3));
session.save(new Privilege("初始化密码","/user_initPassword",menu3));
//论坛和审批的数据
menu=new Privilege("网上交流",null,null);
menu1=new Privilege("论坛管理","/forumManage_list",menu);
menu2=new Privilege("论坛","/forum_list",menu);
session.save(menu);
session.save(menu1);
session.save(menu2);
menu = new Privilege("审批流转", null, null);
menu1 = new Privilege("审批流转管理", "/processDefinition_list", menu);
menu2 = new Privilege("申请模板管理", "/template_list", menu);
menu3 = new Privilege("起草审批", "/flow_templateList", menu);
menu4 = new Privilege("待我审批", "/flow_myTaskList", menu);
menu5= new Privilege("我的申请查询", "/flow_myApplicationList", menu);
session.save(menu);
session.save(menu1);
session.save(menu2);
session.save(menu3);
session.save(menu4);
session.save(menu5);
public static void main(String[] args) {
ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
Installer installer = (Installer) ac.getBean("installer");
//调用该类的方法,是类名.方法
installer.install();
  如果启动tomcat的时候我们通过一个java类就可以启动该程序,但是如果未启动tomcat,我们就采用spring和hibernate来实现数据的保存,因为这里面的方法中有了session,获取到spring容器后,我们就可以实现对数据库itcast_privilege的插入操作。
四、如何给用户分配权限:即当不同的用户登录到首页时,如何只显示该用户所具有的功能列表。而不是全部显示。(基于url的权限分配)
这里采用的逻辑如下:首先将此功能进行细分:第一步:将首页所有的权限功能显示出来。第二步:将用户具有权限的功能显示出来。第三步:补充后续新添加的逻辑
第一步:将所有的权限功能显示出来
这里重点考虑的是权限功能列表什么时候出现,是在用户进入了首页之后才显示还是更早些呢,因为当用户登录成功到首页后才显示可能会出现延迟加载的效果,所以这里考虑到了在项目启动的时候这些列表就应该加载起来,所以这里使用了servlet的监听器的功能:即系统启动时初始化信息。即系统启动时候这些权限列表就加载了。
所以在此监听器中我们应该获取到权限列表的顶级权限列表,然后通过懒加载来加载顶级权限的子孙权限列表。
public class InitListener implements ServletContextListener{
//初始化监听器
public void contextInitialized(ServletContextEvent sce) {
//servlet监听器如何获取spring容器的内容,采用的就是下面这个方法
ApplicationContext ac=(ApplicationContext) WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
PrivilegeService privilegeService= (PrivilegeService) ac.getBean("privilegeServiceImpl");
//左侧菜单栏的显示,显示顶级菜单
List&Privilege&
topPrivilegeList= privilegeService.findTopList();
sce.getServletContext().setAttribute("topPrivilegeList", topPrivilegeList);
  这里使用的知识点:servlet监听器是自己new的一个监听器实例,而通过注解的方式注入到spring的容器中的对象只能在spring容器内使用,即这两个容器是不同的容器,即如何在servlet的监听器中使用spring容器中的bean来实现业务逻辑的实现。(这个前面博文已经介绍了)
配置好之后,获取了顶级权限列表,我们将其放入最大的作用域application中,然后在jsp页面获取到:
sce.getServletContext().setAttribute("topPrivilegeList", topPrivilegeList);
循环遍历这个顶级列表,然后将lazy加载设为false,即默认立即显示子孙列表。
第二步:权限列表全部显示出来后,需要考虑用户登录后只看到自己所对应的权限列表功能的实现
角色(岗位) 用户 权限这三者如何串联起来呢:一个公司有很多用户,不可能一一为用户进行分配权限,而对于小公司而言,通过岗位为用户分配权限这个是可取的。
岗位与用户是多对多的关系,岗位与权限也是多对多的关系。用户与权限的关系就通过了岗位(角色)来关联起来。
如果实现业务逻辑和页面的展示呢:
& & &首先角色分为管理员和普通用户:管理员admin拥有所有的权限,而普通用户拥有特定权限。
在jsp页面添加一个根据用户判断所对应的角色,根据角色判断所对应的权限:
//判断用户是否具有该权限的方法,如果是管理员则拥有最高级权限,即全部权限,如果是普通用户则需要判断。返回结果是boolean类型
public boolean hasPrivilegeByName(String name){
if(isAdmin()){
//如果不是超级管理员,普通用户。普通用户则都是根据岗位来判断是否具有某种权限。
for (Role role : roles) {
for (Privilege
pri: role.getPrivileges()) {
if (pri.getName().equals(name)) {
* 判断本用户是否有指定URL的权限
* @param privUrl
public boolean hasPrivilegeByUrl(String privUrl) {
// 超级管理有所有的权限
if (isAdmin()) {
// && 去掉后面的参数
int pos = privUrl.indexOf("?");
if (pos & -1) {
privUrl = privUrl.substring(0, pos);
// && 去掉UI后缀
if (privUrl.endsWith("UI")) {
privUrl = privUrl.substring(0, privUrl.length() - 2);
// 如果本URL不需要控制,则登录用户就可以使用。这里分为两类:登录后需要验证的和不需要验证的
//从最大的作用域中获取权限所对应的url。这个是在监听器里面获取的,即初始化的时候就获取到了
Collection&String& allPrivilegeUrls = (Collection&String&) ActionContext.getContext().getApplication().get("allPrivilegeUrls");
if (!allPrivilegeUrls.contains(privUrl)) {
// 普通用户要判断是否含有这个权限
for (Role role : roles) {
for (Privilege priv : role.getPrivileges()) {
if (privUrl.equals(priv.getUrl())) {
//判断是否是超级管理员
private boolean isAdmin() {
return "admin".equals(loginName);
  具体的业务逻辑实现如上:当用户登录成功后,用户会保存到session对象中,而在user类这里面普通用户是否有这个权限,因为用户的权限是角色所控制的,所以在这里是遍历用户所对应的角色,然后遍历角色对应的权限,如果权限名称跟我们在jsp页面传递的name名称相同。
第三步:补充其他的。后面讲
五、如何设计权限树:(jsp页面权限树的设计,权限树中选中功能的各个实现,即父类选中,子类全选,子类的某个选中,父类图片也选中)
权限树的设计分三步:(1)treeview组件的js和css文件引入
& & & & & & & & & & & & & & (2)利用&ul&&li&&/li&&/ul&标签编写树结构
& & & & & & & & & & & & & & &(3)添加行为和动作。
&%@ page language="java" import="java.util.*" pageEncoding="utf-8"%&
&title&配置权限&/title&
&%@ include file="/WEB-INF/jsp/public/commons.jspf" %&
&script language="javascript" src="${pageContext.request.contextPath}/script/jquery_treeview/jquery.treeview.js"&&/script&
&link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/style/blue/file.css" /&
&link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/script/jquery_treeview/jquery.treeview.css" /&
//这是选中父类,子类全部被选中,选中子类,父类也被选中的结果。 &script type="text/javascript"&
$(function(){//文档加载完之后执行,这个函数是获取当前文档
// 指定事件处理函数
$("[name=privilegeIds]").click(function(){
// 当选中或取消一个权限时,也同时选中或取消所有的下级权限
$(this).siblings("ul").find("input").attr("checked", this.checked);
// 当选中一个权限时,也要选中所有的直接上级权限
if(this.checked == true){
$(this).parents("li").children("input").attr("checked", true);
&!-- 标题显示 --&
&div id="Title_bar"&
&div id="Title_bar_Head"&
&div id="Title_Head"&&/div&
&div id="Title"&&!--页面标题--&
&img border="0" width="13" height="13" src="${pageContext.request.contextPath}/style/images/title_arrow.gif"/& 配置权限
&div id="Title_End"&&/div&
&!--显示表单内容--&
&div id=MainArea&
&s:form action="role_setPrivilege"&
&s:hidden name="id"&&/s:hidden&
&div class="ItemBlock_Title1"&&!-- 信息说明 --&&div class="ItemBlock_Title1"&
&img border="0" width="4" height="7" src="${pageContext.request.contextPath}/style/blue/images/item_point.gif" /& 正在为【${name}】配置权限 &/div&
&!-- 表单内容显示 --&
&div class="ItemBlockBorder"&
&div class="ItemBlock"&
&table cellpadding="0" cellspacing="0" class="mainForm"&
&!--表头--&
&tr align="LEFT" valign="MIDDLE" id="TableTitle"&
&td width="300px" style="padding-left: 7"&
&!-- 如果把全选元素的id指定为selectAll,并且有函数selectAll(),就会有错。因为有一种用法:可以直接用id引用元素 --&
&input type="checkbox" id="cbSelectAll" onClick="$('[name=privilegeIds]').attr('checked', this.checked)"/&
&label for="cbSelectAll"&全选&/label&
&!--显示数据列表--&
&tbody id="TableData"&
&tr class="TableDetail1"&
&!-- 显示权限树 --&
&%-- &s:checkboxlist name="privilegeIds" list="#privilegeList" listKey="id" listValue="name"&&/s:checkboxlist&
&s:iterator value="#privilegeList"&
&!--回显数据--&
&input type="checkbox" name="privilegeIds" value="${id}" id="cb_${id}"
&s:property value="%{id in privilegeIds ? 'checked' : ''}"/&
&!--点击复选框,可以选择,对应的文字的时候也可以选中复选框,就是文字也是链接,也可以选中--&
&label for="cb_${id}"&${name}&/label&
&/s:iterator&
&!-- 显示树状结构内容 --&
&ul id="tree"&
&s:iterator value="#application.topPrivilegeList"&
&input type="checkbox" name="privilegeIds" value="${id}" id="cb_${id}" &s:property value="%{id in privilegeIds ? 'checked' : ''}"/& /&
&!-- label标签是用来进行点击文字复选框也可以被选中的效果 --&
&label for="cb_${id}"&&span class="folder"&${name}&/span&&/label&
&s:iterator value="children"&
&input type="checkbox" name="privilegeIds" value="${id}" id="cb_${id}" &s:property value="%{id in privilegeIds ? 'checked' : ''}"/& /&
&label for="cb_${id}"&&span class="folder"&${name}&/span&&/label&
&s:iterator value="children"&
&input type="checkbox" name="privilegeIds" value="${id}" id="cb_${id}" &s:property value="%{id in privilegeIds ? 'checked' : ''}"/& /&
&label for="cb_${id}"&&span class="folder"&${name}&/span&&/label&
&/s:iterator&
&/s:iterator&
&/s:iterator&
&script language="javascript"&
$("#tree").treeview();
&!-- 表单操作 --&
&div id="InputDetailBar"&
&input type="image" src="${pageContext.request.contextPath}/style/images/save.png"/&
&a href="javascript:history.go(-1);"&&img src="${pageContext.request.contextPath}/style/images/goBack.png"/&&/a&
&div class="Description"&
说明:&br /&
1,选中一个权限时:&br /&
&&&& a,应该选中他的所有直系上级。&br /&
&&&& b,应该选中他的所有直系下级。&br /&
2,取消选择一个权限时:&br /&
&&&& a,应该取消选择他的所有直系下级。&br /&
&&&& b,如果同级的权限都是未选择状态,就应该取消选中他的直接上级,并向上做这个操作。&br /&
3,全选/取消全选。&br /&
4,默认选中当前岗位已有的权限。&br /&
六、如何只显示有权限的链接,比如:用户管理模块,张三没有修改和删除的功能,则在张三的用户列表中不显示修改和删除的图标链接。
每个功能模块中可能有很多子功能,比如删除和修改,新增功能。当改用户没有这个权限的时候,我们这个时候图标链接是不需要展示的。这里利用了修改struts2的自定义标签实现。
第一步:找到&a&标签的源码文件,一般在struts2的核心jar包的,META_INF下面.tld文件中。
ctrl+shift+t
第二步:如何修改?
struts的jar包源码文件是只读的,我们无法修改,可以通过两种方式:第一种拷贝出来修改完了,再覆盖。第二种:在src下面建立一个同名的包,建立一个同名的类,将类中的内容拷贝进去,然后修改。
第三步:修改:重新struts标签其实就是修改struts的doStartTag()和doEndTag()方法。一般最常用的后者。这是标签前和标签后。即&a&表示标签前,&/a&表示标签后。
doEndTag()的作用就是控制标签体的内容是显示还是不显示,在这里加上我们的逻辑就可以实现该功能。
此处是基于url来判断的,因为便签内部action其实就 url的扩展,将其修改为跟数据库一样的url就可以实现判断比较了。
//Struts2自定义标签类重写主要就是重写doStartTag()和doEndTag()方法。这两个方法指的是标签的开始和结束。(开始)&a&标签体&/a&(结束)一般我们使用doEndTag标签,因为我们需要使用到标签体的内容。doStartTag只能得到标签属性的内容,得不得标签体的内容。
public int doEndTag() throws JspException {
// 当前登录用户,自定义标签是在jsp页面用的,所以这里页面获取的是pageContext
User user = (User) pageContext.getSession().getAttribute("user");
// 当前准备显示的链接对应的权限URL,jsp页面的url是含有参数和后缀的url,跟数据库中存储的url是有区别的,所以要进行处理操作。
// && 在开头加上'/'
String privUrl = "/" +
/*这些代码写在user类中,即去掉页面的url的后缀等东西,与数据库的url对应。这里主要是用到了String的类的方法
int pos = privUrl.indexOf("?");
if (pos & -1) {
privUrl = privUrl.substring(0, pos);
// && 去掉UI后缀
if (privUrl.endsWith("UI")) {
privUrl = privUrl.substring(0, privUrl.length() - 2);
if (user.hasPrivilegeByUrl(privUrl)) {
return super.doEndTag(); //显示页面中的超链接代码,只要是页面有链接的都可以控制,即就是控制页面的链接是否存在的标签。比如说修改、删除的超链接,如果有权限就显示,如果没有权限就不显示,然后继续执行jsp页面中其他的代码
return EVAL_PAGE; // 不生成与显示超链接 标签,只是继续执行页面中后面的代码,即页面中的超链接没有了,继续执行jsp页面中其他剩余的代码
七、如何处理每个请求对应的权限分配:即当不同的用户登录的时候所拥有的权限是不同的,除了给予用户的特定权限外,用户还有一些基本的权限(比如注销功能,退出功能等这些普通的权限),所以这些权限就分为:(1)用户未登录时的,用户可以使用登录功能的权限。(2)登录成功后全部的公司用户所普遍具有的一些普通权限,即用户默认都有的权限(3)登录成功后赋予特定权限的一些特定权限.
此功能实现的逻辑是这样子的:
目标是将登陆的权限和每个action的拦截,共同拥有的权限结合起来,共同实现对每个action的拦截。
对action的拦击,其实就的对url的拦截。
拦截器一般都是用来实现拦截某个状态,比如用户未登录,在SSO系统中也是利用拦截器来实现子系统的拦截。
第一步:建立拦截器类
(1)建立拦截器类,继承Interceptor类
(2)在struts.xml中配置拦截器
&!-- 权限管理模块,拦击每个action的,配置拦截器,先声明拦截器 --&
&interceptors&
&interceptor name="checkPrivilege" class="cn.itcast.oa.util.CheckPrivilegeInterceptor"&&/interceptor&
&!-- 重新定义默认的拦截器栈 --&
&interceptor-stack name="defaultStack"&
&interceptor-ref name="checkPrivilege"&&/interceptor-ref&
&interceptor-ref name="defaultStack"&&/interceptor-ref&
&/interceptor-stack&
&/interceptors&
第二步:分析逻辑
(1)用户是否登录:未登录提示登录,登录了验证权限(权限存在则显示页面,不存在则返回错误页面)
(2)如何显示普通的共有的权限:获取到所有权限的url,如果此url不包含所选权限的url,则剩下的就是共有的
第三步:填充此类
注意这里的
package cn.itcast.oa.
import org.xml.sax.SAXE
import com.opensymphony.xwork2.ActionC
import com.opensymphony.xwork2.ActionI
import com.opensymphony.xwork2.interceptor.AbstractI
import com.sun.xml.internal.bind.v2.runtime.unmarshaller.I
import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.S
import cn.itcast.oa.domain.U
//验证用户登录的拦截器,
public class CheckPrivilegeInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation) throws Exception {
/*System.out.println("之前拦截。。。。。。。。。。。。。");
String result=invocation.invoke();
System.out.println("之后拦截。。。。。。。。。。。。。");
/*判断用户是否登录,如果登录则放行,如果未登录则提示登录。未登录又分两种情况,一种是用户正在前往登录页面,这种情况也需要放行,一种是用户未登录。
* 如果未登录则提示用户登录。如果登录成功后,则验证用户是否具有权限,如果有权限,则放行,如果没有权限则提示错误页面。
User user=(User) ActionContext.getContext().getSession().get("user");
String nameSpace=invocation.getProxy().getNamespace();
String actionName=invocation.getProxy().getActionName();
String privUrl=nameSpace+actionN//对应权限的url
if (user == null) {
// 如果是在登录的路上,即用户前往登录的界面,则放行,如果不是登录,则转到登录页面."user_loginUI""user_login"
if (privUrl.startsWith("/user_login")) {
return invocation.invoke();
return "loginUI";
if (user.hasPrivilegeByUrl(privUrl)) {
return invocation.invoke();
return "noPrivilegeError";
* 判断本用户是否有指定URL的权限
* @param privUrl
public boolean hasPrivilegeByUrl(String privUrl) {
// 超级管理有所有的权限
if (isAdmin()) {
// && 去掉后面的参数
int pos = privUrl.indexOf("?");
if (pos & -1) {
privUrl = privUrl.substring(0, pos);
// && 去掉UI后缀
if (privUrl.endsWith("UI")) {
privUrl = privUrl.substring(0, privUrl.length() - 2);
// 如果本URL不需要控制,则登录用户就可以使用。这里分为两类:登录后需要验证的和不需要验证的
//从最大的作用域中获取权限所对应的url。这个是在监听器里面获取的,即初始化的时候就获取到了
Collection&String& allPrivilegeUrls = (Collection&String&) ActionContext.getContext().getApplication().get("allPrivilegeUrls");
if (!allPrivilegeUrls.contains(privUrl)) {
// 普通用户要判断是否含有这个权限
for (Role role : roles) {
for (Privilege priv : role.getPrivileges()) {
if (privUrl.equals(priv.getUrl())) {
//判断是否是超级管理员
private boolean isAdmin() {
return "admin".equals(loginName);
八、服务器关闭后,在session还未过期的情况下,用户的状态仍然显示在页面。
这个请参考前面博文写的session的对象序列化。
九、页面嵌套页面的处理工作
这个是jsp页面的一个刷新操作即可完成:这是在loginUI.jsp中设置的刷新:即每次点击的时候刷新:
&后期补充:
基于url的权限管理:主要分两步:第一步认证阶段。第二步:授权阶段。
当访问一个系统的时候,我们在页面输入该项目的名称,项目会自动到WEB-ROOT下面寻找默认的index.jsp页面,这个时候发现index.jsp页面里面是跳转到项目首页的first.action。因为拦截的过程是对应的每个mapping,所以这个时候进入了登录的拦截器,判断first.action是否是可以匿名访问的,查阅到匿名的配置文件,发现不是可以匿名访问的,所以这个时候不允许放行。那么用户是否已经登录过了呢,判断session中的用户,发现也没有登录,这个时候跳转到登录页面进行登录。这个过程就是认证的过程。
问题:为什么需要进行匿名访问的放行呢?
拦截器拦截所有的url,如果匿名访问的不能放行,那么用户登录的login.action也不能执行,都拦截了,用户也不能进行校验用户名和密码了。
基于url的拦截是:分为匿名的访问地址、认证通过后公共的访问地址、具有相应权限的访问地址
阅读(...) 评论()

我要回帖

更多关于 javabean访问数据库 的文章

 

随机推荐