`
fengfeng925
  • 浏览: 105027 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

基于Struts2拦截器的权限控制

阅读更多
    最近闲来无事,自己做了个通过struts2拦截器的权限控制的小例子。主要是通过struts2的核心拦截器机制,不依赖于容器,实现一个可以精确到方法上的细粒度的权限控制。

     该小例子借鉴了网上一些前辈们,还有学界同行们的项目经验,我取其优秀的部分,稍加加工,改造而成,还非常不完善,欢迎大家批评指正。该小例子只供学习交流,无意于商业化中使用,也无意于挑起任何争端。

    下面是一些类的代码,这里可能需要大家对注解比较熟悉,不过不熟悉也不影响,和xml配置文件起的是一样的作用,都是将属性映射成为表的字段。

首先呢,建立一个注解类,名字叫Permission。这个注解类的代码如下
package com.zxyg.web.action.privilege;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 权限配置 注解类
 * @author Lan
 *
 */
@Retention(RetentionPolicy.RUNTIME) //注解的存活时间,整个运行时都存活
@Target(ElementType.METHOD) //此注解表示只能在方法上面有效
public @interface Permission {
	/** 模块名 **/
	String model();
	/** 权限值 **/
	String privilegeValue();
}

这个类,就是将来要在方法上打标记的。

接下来要建立几个跟权限相关的实体类了。
首先是权限实体类,类的代码如下
package com.zxyg.bean.privilege;

import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Table;

/**
 * 系统权限实体
 * @author Lan
 * 
 */
@Entity
@Table(name="t_systemprivilege")
public class SystemPrivilege {
	/** 权限值**/
	private SystemPrivilegePK id;
	/** 权限名称**/
	private String name;
	
	public SystemPrivilege() {}

	public SystemPrivilege(SystemPrivilegePK id) {
		this.id = id;
	}

	public SystemPrivilege(String model, String privilegeValue, String name) {
		this.id = new SystemPrivilegePK(model, privilegeValue);
		this.name = name;
	}

	@EmbeddedId //复合主键的标注
	public SystemPrivilegePK getId() {
		return id;
	}

	public void setId(SystemPrivilegePK id) {
		this.id = id;
	}

	@Column(length=20, nullable=false)
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

这个类主要包含两个属性,一个是权限值,另外一个是权限的名称,权限值呢,实际也是一个类,这里采用的是复合主键的形式,权限值的实体类代码如下
package com.zxyg.bean.privilege;

import javax.persistence.Column;
import javax.persistence.Embeddable;

/**
 * 系统权限复合主键类
 * @author Lan
 * 
 */
@Embeddable
public class SystemPrivilegePK {
	/** 模块名 **/
	private String model;
	/** 权限值 **/
	private String privilegeValue;
	
	public SystemPrivilegePK() {}

	public SystemPrivilegePK(String model, String privilegeValue) {
		this.model = model;
		this.privilegeValue = privilegeValue;
	}

	@Column(length=25, name="model")
	public String getModel() {
		return model;
	}

	public void setModel(String model) {
		this.model = model;
	}

	@Column(length=20, name="privilegeValue")
	public String getPrivilegeValue() {
		return privilegeValue;
	}

	public void setPrivilegeValue(String privilegeValue) {
		this.privilegeValue = privilegeValue;
	}
}


这个类有两个属性,一个是模块的名称,一个是模块对应的值。其实呢,这里在数据库里面的权限表里存储的就是对某个资源的什么操作,描述名称是什么,比如
role delete 角色删除
意思就是对role这个模块的删除操作,这个呢,就是一个具体的权限。
最后一个实体类就比较简单了,是角色类,类代码如下
package com.zxyg.bean.privilege;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

import com.zxyg.bean.organization.Employee;


/**
 * 角色实体
 * @author Lan
 * 
 */
@Entity
@Table(name="t_role")
public class Role {
	/** 主键ID* */
	private Integer id;
	/** 角色名称* */
	private String name;
	/** 存放权限的集合,角色和权限是多对多的关系 * */
	private Set<SystemPrivilege> privileges = new HashSet<SystemPrivilege>();
	
	public Role() {}
	
	public Role(Integer id) {
		this.id = id;
	}

	@Id @GeneratedValue
	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	@Column(length=20, nullable=false)
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@ManyToMany(cascade=CascadeType.REFRESH, fetch=FetchType.EAGER)
	@JoinTable(name="t_role_privilege", joinColumns=@JoinColumn(name="roleid"),
			inverseJoinColumns={@JoinColumn(name="model", referencedColumnName="model"),
		                        @JoinColumn(name="privilegeValue", referencedColumnName="privilegeValue")}) //中间表,来存放角色、权限的关系
	public Set<SystemPrivilege> getPrivileges() {
		return privileges;
	}

	public void setPrivileges(Set<SystemPrivilege> privileges) {
		this.privileges = privileges;
	}
	
	//定义此方法,方便添加权限
	public void addPrivilege(SystemPrivilege privilege) {
		this.privileges.add(privilege);
	}
}


这个类就比较简单了,具体看一下其中的关系即可,这里就不多做赘述了。
当你有一个Action类的时候,需要在方法上加标注描述了。举例如下
package com.zxyg.web.action.privilege;

import java.io.Serializable;

import javax.annotation.Resource;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;


import com.opensymphony.xwork2.ActionContext;
import com.zxyg.bean.privilege.Role;
import com.zxyg.bean.privilege.SystemPrivilege;
import com.zxyg.bean.privilege.SystemPrivilegePK;
import com.zxyg.service.privilege.RoleService;
import com.zxyg.service.privilege.SystemPrivilegeService;
import com.zxyg.utils.SiteUrl;
import com.zxyg.web.action.base.BaseSupport;

@Controller @Scope("prototype")
public class RoleManageAction extends BaseSupport {
	private static final long serialVersionUID = 1L;
	@Resource private RoleService roleService;
	@Resource private SystemPrivilegeService privilegeService;
	private Role role;
	
	//接收前端传过来的权限数组,如"角色添加"、"角色删除"等
	private SystemPrivilegePK[] privileges;
	
	//接收前端传过来的roleid参数
	private Integer roleid;
	
	public Integer getRoleid() {
		return roleid;
	}

	public void setRoleid(Integer roleid) {
		this.roleid = roleid;
	}

	public SystemPrivilegePK[] getPrivileges() {
		return privileges;
	}

	public void setPrivileges(SystemPrivilegePK[] privileges) {
		this.privileges = privileges;
	}

	public Role getRole() {
		return role;
	}

	public void setRole(Role role) {
		this.role = role;
	}


	/**
	 * 显示角色添加页面
	 * @return
	 */
	@Permission(model="role", privilegeValue="insert")
	public String addUI() {
		ActionContext.getContext().put("privileges", privilegeService.getScrollData().getResultlist());
		return "add";
	}
	
	/**
	 * 添加角色
	 * @return
	 */
	@Permission(model="role", privilegeValue="insert")
	public String add() {
		for(SystemPrivilegePK id : privileges) {
			role.addPrivilege(new SystemPrivilege(id));
		}
		roleService.save(role);
		ActionContext context = ActionContext.getContext();
		context.put("message", "添加角色成功");
		context.put("urladdress", SiteUrl.readUrl("control.role.list"));
		return "message";
	}
	
	/**
	 * 显示角色修改页面
	 * @return
	 */
	@Permission(model="role", privilegeValue="update")
	public String updateUI() {
		role = roleService.find((Serializable)roleid);
		ActionContext context = ActionContext.getContext();
		context.put("role", role);
		context.put("privileges", privilegeService.getScrollData().getResultlist());
		context.put("selectprivileges", role.getPrivileges());
		return "edit";
	}
	
	/**
	 * 修改角色
	 * @return
	 */
	@Permission(model="role", privilegeValue="update")
	public String update() {
		Role old_role = roleService.find((Serializable)roleid);
		old_role.setName(role.getName());
		old_role.getPrivileges().clear();
		for(SystemPrivilegePK id : privileges) {
			old_role.addPrivilege(new SystemPrivilege(id));
		}
		roleService.update(old_role);
		ActionContext context = ActionContext.getContext();
		context.put("message", "修改角色成功");
		context.put("urladdress", SiteUrl.readUrl("control.role.list"));
		return "message";
	}
	
	/**
	 * 删除角色
	 * @return
	 */
	@Permission(model="role", privilegeValue="delete")
	public String delete() {
		roleService.delete((Serializable)roleid);
		ActionContext context = ActionContext.getContext();
		context.put("message", "删除角色成功");
		context.put("urladdress", SiteUrl.readUrl("control.role.list"));
		return "message";
	}
}

看到这些方法上面的描述了吧,其实最终是要取得这些标注描述再来判断权限的。呵呵,接下来我们就写个拦截器了,这里面就是具体描述,当用户登录后如何和这些方法上的标注相互比对进行权限控制的,拦截器代码如下:
package com.zxyg.web.action.privilege;

import java.lang.reflect.Method;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.zxyg.bean.organization.Employee;
import com.zxyg.bean.privilege.Role;
import com.zxyg.bean.privilege.SystemPrivilege;
import com.zxyg.bean.privilege.SystemPrivilegePK;
import com.zxyg.utils.SiteUrl;

/**
 * 使用拦截器进行系统权限的拦截
 * @author Lan
 * 
 */
public class PermissionInterceptor implements Interceptor {
	private static final long serialVersionUID = 1L;

	public void destroy() {}

	public void init() {}

	/**
	 * 处理权限拦截
	 */
	public String intercept(ActionInvocation invocation) throws Exception {
		Employee employee = (Employee) ServletActionContext.getRequest().getSession().getAttribute("employee"); //取得当前登录的用户
		if (validate(employee, invocation)) {
			return invocation.invoke();
		}
		ActionContext context = ActionContext.getContext();
		context.put("message", "你没有权限执行该操作");
		context.put("urladdress", SiteUrl.readUrl("control.center.right"));
		return "message";
	}

	/**
	 * 校验控制在方法上的拦截
	 */
	public boolean validate(Employee employee, ActionInvocation invocation) {
		String methodName= "execute"; //定义默认的访问方法
		Method currentMethod = null;
		methodName = invocation.getProxy().getMethod(); //通过actionProxy,得到当前正在执行的方法名
		try {
			currentMethod = invocation.getProxy().getAction().getClass().getMethod(methodName, new Class[] {}); //利用反射,通过方法名称得到具体的方法
			/*Method[] methods = invocation.getProxy().getAction().getClass().getMethods(); //如果不确定方法的具体参数,也可以迭代比较,速度较慢
			for(Method method : methods ) {
				if(method.getName().equals(methodName)) {
					currentMethod = method;
					break;
				}
			}*/
		} catch (Exception e) {
		}
		if (currentMethod != null && currentMethod.isAnnotationPresent(Permission.class)) {
			Permission permission = currentMethod.getAnnotation(Permission.class); //得到方法上的Permission注解
			SystemPrivilege privilege = new SystemPrivilege(new SystemPrivilegePK(permission.model(), permission.privilegeValue())); //通过Permission注解构造出系统权限
			for (Role role : employee.getRoles()) { //迭代用户所具有的具体权限,如果包含,返回true
				if (role.getPrivileges().contains(privilege)) 
					return true;
			}
			return false;
		}
		return true;
	}

}

这些代码呢,其实已经简单的描述了下,就是将用户存储在数据库中间表中的角色对应的权限取出来,在构造出用户想访问的action中方法上的权限描述,相互比对,如果包含,则可以访问,否则没有权限访问。

最后就是struts.xml文件的配置了,配置好拦截器,就都搞定了,配置文件如下
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
	<package name="privilege" namespace="/control/role" extends="struts-default">
		<interceptors>
			<!-- 定义权限拦截器 -->
			<interceptor name="permission" class="com.zxyg.web.action.privilege.PermissionInterceptor"/>
			<!-- 定义拦截器栈,所谓拦截器栈,是指由一个或多个拦截器组成 -->
			<interceptor-stack name="permissionStack">
				<!-- struts2提供的拦截器栈,包含了struts2的很多核心拦截器 -->
				<interceptor-ref name="defaultStack" />
				<!-- 自己定义的放在最后面,struts2定义的放在前面 -->
				<interceptor-ref name="permission" />
			</interceptor-stack>
		</interceptors>
		
		<!-- 为此包下的所有action应用拦截器 -->
		<default-interceptor-ref name="permissionStack" />
		
		<global-results>
			<result name="message">/WEB-INF/page/share/message.jsp</result>
		</global-results>
		
		<!-- 显示角色列表 -->
		<action name="list" class="roleListAction" method="execute">
			<result name="success">/WEB-INF/page/privilege/rolelist.jsp</result>
		</action>
		
		<!-- 角色管理 -->
		<action name="manage_*" class="roleManageAction" method="{1}">
			<result name="add">/WEB-INF/page/privilege/addrole.jsp</result>
			<result name="edit">/WEB-INF/page/privilege/editrole.jsp</result>
		</action>
	</package>
</struts>

ok,到这里的时候,所有工作就完成了,当用户登录后,就可以进行访问进行权限拦截了的试验了。
2
6
分享到:
评论
6 楼 飘渺孤独 2012-09-03  
给发个源码吧 谢谢了 正在学习中
250227642@qq.com
5 楼 itoao 2011-12-23  
itoao 写道
请问能发份源码给我么 谢谢 非常感谢 最近需要用这个。

忘记给邮箱了  liangsantian@yeah.net  感激不尽!!
4 楼 itoao 2011-12-23  
请问能发份源码给我么 谢谢 非常感谢 最近需要用这个。
3 楼 catmimigo 2011-09-21  
看了您的文章后,对自定义注解十分感兴趣,能否发份源码给我,就要用户角色权限RBAC这一块。包括SQL.麻烦您了。catmimigo@msn.com
2 楼 lqysaber 2011-01-10  
可否将完整源码分享一下阿,不胜感激 kbtjhvb@yeah.net
1 楼 gyl868 2010-06-17  
很喜欢楼主的设计思路,可否将完整源码分享一下阿,不胜感激 gyl8680@gmail.com

相关推荐

    STRUTS2拦截器控制页面访问权限的设计与实现

    STRUTS2拦截器控制页面访问权限的设计与实现,解决基于STRUTS2的web应用的程序访问控制,防止非法访问

    [Struts 2权威指南--基于WebWork核心的MVC开发(高清完整版) 1/12

    第1章 Struts 2概述,第2章 Struts 2下的HelloWorld,第3章 Struts 2基础,第4章 深入Struts 2,第5章 Struts 2的类型转换,第6章 文件的上传和下载.,第7章 Struts 2的拦截器,第8章 Struts 2的输入校验,9.2 ...

    Struts2入门教程(全新完整版)

    6.使用拦截器实现权限验证 35 7.拦截器中的注解 37 8.使用PreResultListener实现回调 39 六、使用标签 40 1.基础表单标签 40 2.单选按钮和复选框: 41 3.三种方式实现下拉列表 41 4.二级联动 42 5.其它表单标签 44 6...

    图书管理系统-struts2

    (4) 使用拦截器,阻止用户注册页面的重复提交。 (5) 用户登陆成功之后,能够修改自己的密码,要求修改密码的时候,新旧密码在一个文本框输入(用,分割),通过使用自定义类型转换完成新旧密码的拆分工作。

    S2SH项目整体系统架构

    2、拦截器实现用户权限检查功能 3、拦截器自动生成日志 4、未登录非法访问页面检测 5、三种方式日志的生成 6、页面访问计数 7、选中页面对应的帮助页面实现 8、根据页面编号或名称跳转页面 9、数据库连接池 ...

    ssh框架实现通讯录

    写的比较简陋,,基于struts1.2+spring2.0+hibernate3.1框架 基本功能: 注册,登陆,添加联系人,按类别查找联系人,删除联系人,登出 表单输入使用validate框架验证,权限管理使用 Filter+action拦截器,...

    J2EE应用开发详解

    124 8.5.2 拦截器的实现原理 124 8.5.3 Struts2的内置拦截器 124 8.5.4 拦截器的配置和使用 125 8.5.5 自定义拦截器 126 8.6 一个简单的Struts2应用 130 8.7 小结 140 第9章 JSF 141 9.1 JSF技术简介 141 9.1.1 JSF...

    SSH框架实现BBS完整版

    该文档是SSH框架为基础实现的BBS论坛。其中有整合好的全面的SSH jar包,BBS前台,后台源码,这个小系统的报告,数据库关系分析等。 以下内容摘自报告目录部分(希望...4.14 根据权限定义拦截器 4.15 测试系统相应的功能

    毕业论文-办公自动化系统的设计和实现

    随着计算机网络通信技术的发展,资源...利用拦截器和过滤器实现“用户、角色、权限”三维管控,使系统更加具有层次化和结构化。 本系统具有易用性、健壮性、严密性和实用性等特点,将现代化办公和计算机技术有机结合。

    SSM开发框架

    通过拦截器进行权限检查、日志输出等操作; 05、框架实现了文件上传共通、电子文档导出、校验、全局异常处理、分页等共通,具体参见相关画面的相关功能; 06、通过注解方式实现事务控制,相关画面有测试代码,具体在...

    最新SSM整合

    通过拦截器进行权限检查、日志输出等操作; 05、框架实现了文件上传共通、电子文档导出、校验、全局异常处理、分页等共通,具体参见相关画面的相关功能; 06、通过注解方式实现事务控制 07、关于数据库链接,可以...

    Spring面试题

    基于Struts开发的应用由3类组件构成:控制器组件、模型组件、视图组件 8. Struts的validate框架是如何验证的? 在struts配置文件中配置具体的错误提示,再在FormBean中的validate()方法具体调用。 9. 说下Struts...

    spring security 参考手册中文版

    25.1 AOP联盟(MethodInvocation)安全拦截器 197 25.1.1显式MethodSecurityInterceptor配置 197 25.2 AspectJ(JoinPoint)安全拦截器 198 26.基于表达式的访问控制 200 26.1概述 200 26.1.1通用内置表达式 201 ...

Global site tag (gtag.js) - Google Analytics