今天刚学习了通过自定义注解+拦截器实现权限控制,自己花了点时间整理,发到网站同网友交流分享。

一、定义一个自定义注解类

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

/**
 * 自定义注解
 * @author grace
 *
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface Limit{
	 String module();  //模块名称
	 String privilege(); //操作名称
}



二、建好所需要的表


#权限组表
CREATE TABLE `sys_role` (
  `id` varchar(36),                             #编号
  `remark` TEXT,                                #备注
  `name` VARCHAR(100)  DEFAULT NULL,             #名称
  PRIMARY KEY (`id`)
)
#操作表
CREATE TABLE sys_popedom
(
   popedomModule       VARCHAR(30),                              #模块名称
   popedomPrivilege    VARCHAR(30),                              #操作名称
   sort                INTEGER(11),                              #排序
   title               VARCHAR(200),                             #提示
   popedomName         VARCHAR(200),                             #标题
   remark              TEXT,                                     #说明
   PRIMARY KEY(popedomModule,popedomPrivilege)
)
#操作权限表
CREATE TABLE sys_popedom_privilege
(
   roleId         VARCHAR(36),        #权限组编号
   popedomModule     VARCHAR(30),     #模块名称
   popedomPrivilege  VARCHAR(30),     #操作名称
   PRIMARY KEY(roleId,popedomModule,popedomPrivilege)
)


解释下以上3个表的作用:

1.权限组表:定义了一个权限,权限有id,name和remark。


java自定义注解限定参数 java自定义注解权限拦截_java自定义注解限定参数

2.操作表:该表相当于是你这个项目里所有的权限。popedomModule代表一个大的功能模块,popedomPrivilege代表一个模块下的具体操作:常见的有增删改查,可参考下图理解。



java自定义注解限定参数 java自定义注解权限拦截_拦截器_02



3.操作权限表:该表是权限组所对应的模块和操作。例子:如下图的roleId为8af0897545e4c91b0145e4cd385d0002,在权限组表中对应的是系统管理员权限组(见上上图),该权限组拥有的模块和操作名称在下图第二三列,可对应上图知道功能。说明:系统管理员权限组在当前系统中,只拥有下图所示的模块为city和code的所有操作权限,没有模块为company下的任何操作权限。通过将用户分配到不同的权限组,和设置权限组的拥有的模块和操作,就可以实现权限控制。

java自定义注解限定参数 java自定义注解权限拦截_interceptor_03


三、定义上面三个表所对应的类和*.hbm.xml(如果需要)

权限组表:


/*
 * SysRole:权限组表
 */
@SuppressWarnings("serial")
public class SysRole implements java.io.Serializable {
	private String id;
	private String name;
	private String remark;

	public String getId() {
		return id;
	}

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

	public String getName() {
		return name;
	}

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

	public String getRemark() {
		return remark;
	}

	public void setRemark(String remark) {
		this.remark = remark;
	}

}



注:因为在操作表(sys_popedom)和权限操作表(sys_popedom_privilege)中都为联合主键。因此在这里,我对这两个类都使用:新建一个类用来存放联合主键。


操作表:

/*
 * 操作表
 */
@SuppressWarnings("serial")
public class SysPopedom  implements java.io.Serializable{
/*
 * CREATE TABLE sys_popedom
(
   popedomModule       VARCHAR(30),                              #模块名称
   popedomPrivilege    VARCHAR(30),                              #操作名称
   sort                INTEGER(11),                              #排序
   title               VARCHAR(200),                             #提示
   popedomName         VARCHAR(200),                             #标题
   remark              TEXT,                                     #说明
   PRIMARY KEY(popedomModule,popedomPrivilege)
)
 */
    private SysPopedomId id;//主键 OID
	private Integer sort;
	private String title;
	private String popedomName;
	private String remark;
	
	public SysPopedomId getId() {
		return id;
	}
	public void setId(SysPopedomId id) {
		this.id = id;
	}
	public Integer getSort() {
		return sort;
	}
	public void setSort(Integer sort) {
		this.sort = sort;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getPopedomName() {
		return popedomName;
	}
	public void setPopedomName(String popedomName) {
		this.popedomName = popedomName;
	}
	public String getRemark() {
		return remark;
	}
	public void setRemark(String remark) {
		this.remark = remark;
	}
	
}
/**
 * 操作表中的联合主键类
 * @author grace
 *
 */
@SuppressWarnings("serial")
public class SysPopedomId implements java.io.Serializable {
	private String popedomModule;
	private String popedomPrivilege;

	public String getPopedomModule() {
		return popedomModule;
	}
	
	public void setPopedomModule(String popedomModule) {
		this.popedomModule = popedomModule;
	}

	public String getPopedomPrivilege() {
		return popedomPrivilege;
	}

	public void setPopedomPrivilege(String popedomPrivilege) {
		this.popedomPrivilege = popedomPrivilege;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((popedomModule == null) ? 0 : popedomModule.hashCode());
		result = prime
				* result
				+ ((popedomPrivilege == null) ? 0 : popedomPrivilege.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final SysPopedomId other = (SysPopedomId) obj;
		if (popedomModule == null) {
			if (other.popedomModule != null)
				return false;
		} else if (!popedomModule.equals(other.popedomModule))
			return false;
		if (popedomPrivilege == null) {
			if (other.popedomPrivilege != null)
				return false;
		} else if (!popedomPrivilege.equals(other.popedomPrivilege))
			return false;
		return true;
	}
}




操作权限表:

/**
 * 操作权限表
 * @author grace
 *
 */
@SuppressWarnings("serial")
public class SysPopedomPrivilege implements java.io.Serializable {
	/*
	 * CREATE TABLE sys_popedom_privilege 
	 * ( 
	 * 	 roleId VARCHAR(36), #权限组编号
	 *	 popedomModule VARCHAR(30), #模块名称 
	 *   popedomPrivilege VARCHAR(30), #操作名称
	 * 	 PRIMARY KEY(roleId,popedomModule,popedomPrivilege) 
	 * )
	 */

	private SysPopedomPrivilegeId id;

	public SysPopedomPrivilegeId getId() {
		return id;
	}

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

}
/**
 * 操作权限表的联合主键类
 * @author grace
 *
 */
@SuppressWarnings("serial")
public class SysPopedomPrivilegeId implements java.io.Serializable {

	private String roleId;
	private String popedomModule;
	private String popedomPrivilege;

	public String getRoleId() {
		return roleId;
	}

	public void setRoleId(String roleId) {
		this.roleId = roleId;
	}

	public String getPopedomModule() {
		return popedomModule;
	}

	public void setPopedomModule(String popedomModule) {
		this.popedomModule = popedomModule;
	}

	public String getPopedomPrivilege() {
		return popedomPrivilege;
	}

	public void setPopedomPrivilege(String popedomPrivilege) {
		this.popedomPrivilege = popedomPrivilege;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((popedomModule == null) ? 0 : popedomModule.hashCode());
		result = prime
				* result
				+ ((popedomPrivilege == null) ? 0 : popedomPrivilege.hashCode());
		result = prime * result + ((roleId == null) ? 0 : roleId.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final SysPopedomPrivilegeId other = (SysPopedomPrivilegeId) obj;
		if (popedomModule == null) {
			if (other.popedomModule != null)
				return false;
		} else if (!popedomModule.equals(other.popedomModule))
			return false;
		if (popedomPrivilege == null) {
			if (other.popedomPrivilege != null)
				return false;
		} else if (!popedomPrivilege.equals(other.popedomPrivilege))
			return false;
		if (roleId == null) {
			if (other.roleId != null)
				return false;
		} else if (!roleId.equals(other.roleId))
			return false;
		return true;
	}

}



如果需要*.hbm.xml则进行配置。


四、写自定义拦截器

import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;

/**
 * 自定义拦截器
 * @author grace
 *
 */
@SuppressWarnings("serial")
public class LimitInterceptor  extends MethodFilterInterceptor{

	public String doIntercept(ActionInvocation invocation) throws Exception {
		//获取请求的action对象
		Object action=invocation.getAction();
		
		//获取请求的方法的名称
		String methodName=invocation.getProxy().getMethod();
		
		//获取action中的方法的封装类(action中的方法没有参数)
		Method method=action.getClass().getMethod(methodName, null);

		//获取request对象
		HttpServletRequest request=ServletActionContext.getRequest();
		
		//检查注解
		boolean flag=isCheckLimit(request,method);
		
		if(!flag){
			//没有权限,通过struts2转到事先定义好的页面
			return "popmsg_popedom";
		}
		
		//有权限,可以调用action中的方法
		String returnvalue=invocation.invoke();
		return returnvalue;
	}

	public boolean isCheckLimit(HttpServletRequest request, Method method) {
		if(method==null){
			return false;
		}
		
		//获取当前的登陆用户
		SysUser sysUser=SessionUtils.getSysUserFormSession(request);
		if(sysUser==null){
			return false;
		}
		//如果用户的权限组为空
		if(sysUser.getSysRole()==null){
			return false;
		}
		
		//获取当前登陆用户的权限组id
		String roleId=sysUser.getSysRole().getId();
		//处理注解
		/*
		 * 	@Limit(module="group",privilege="list")
	        public String list(){
	        	....
	        }
		 */
		//判断用户请求的method上面是否存在注解
		boolean isAnnotationPresent= method.isAnnotationPresent(Limit.class);
		
		//不存在注解
		if(!isAnnotationPresent){
			return false;
		}
		
		//存在注解,拿到由Limit类写的注解
		Limit limit=method.getAnnotation(Limit.class);
		
		//获取注解上的值
		String module=limit.module();  //模块名称
		String privilege=limit.privilege(); //操作名称
		
		/**
		 * 如果登陆用户的权限组id+注解上的@Limit(module="group",privilege="list")
		 *   * 在sys_popedom_privilege表中存在   flag=true;
		 *   * 在sys_popedom_privilege表中不存在 flag=false;
		 */
		boolean flag=false;
		
		//通过自己封装的方法拿到操作权限的业务层对象
		ISysPopedomPrivilegeService sysPopedomPrivilegeService=
		    (ISysPopedomPrivilegeService)ServiceProvinder.getService(ISysPopedomPrivilegeService.SERVICE_NAME);
		
		//查询sys_popedom_privilege表中的所有的数据
		//因为后面用到二级缓存,因此这里直接查询出所有的操作权限,而不是通过登陆用户的权限组来查询
		List<SysPopedomPrivilege> list=sysPopedomPrivilegeService.findAllSysPopedomPrivileges();
		
		if(list!=null&&list.size()>0){
		  for(int i=0;i<list.size();i++){
			  SysPopedomPrivilege s=list.get(i);
			  if(s!=null){
				  //判断登陆用户是否拥有该方法的权限:如果登陆用户的roleId和登陆用户访问的方法
				  //的注释中的module和privilege(例如@Limit(module="group",privilege="list")),这三个字段
				  //在sys_popedom_privilege表中有记录与之对应,则代表该用户拥有权限访问此方法
				   if(roleId.equals(s.getId().getRoleId())&&module.equals(s.getId().getPopedomModule())
						   &&privilege.equals(s.getId().getPopedomPrivilege())){
					   flag=true;
					   break;
				   }
			  }
		  }
		}
		return flag;
	}
}


五、在struts.xml中配置自定义拦截器和没有权限时访问的页面。

<interceptors>
	<!-- 声明拦截器 -->
	<interceptor name="limitInterceptor" class="cn.grace.interceptor.LimitInterceptor"></interceptor>
	<interceptor-stack name="limitStack">
		<interceptor-ref name="defaultStack" />
		<interceptor-ref name="limitInterceptor">
			<!-- 配置不被拦截的方法 -->
			<param name="excludeMethods">isLogin,logout,top,left</param>
		</interceptor-ref>
	</interceptor-stack>
</interceptors>
<!-- struts2运行时,执行的拦截器栈 -->
<default-interceptor-ref name="limitStack" />
  
  <global-results>
  		<!-- 转发到没有权限的页面 -->
  		<result name="popmsg_popedom">/WEB-INF/page/popmsg_popedom.jsp</result>
  </global-results>



六、通过在Action的方法中进行注释,实现权限控制。

import cn.grace.annotation.Limit;

public class testUserAction {

	/** 用户添加页面 */
	@Limit(module="user",privilege="add")
	public String add(){
		return "add";
	}
	
	/** 用户添加 **/
	@Limit(module="user",privilege="save")
	public String save(){
		return "save";
	}
	
	/** 用户删除 **/
	@Limit(module="user",privilege="delete")
	public String delete(){
		return "delete";
	}
	
	/** 用户修改页面 **/
	@Limit(module="user",privilege="edit")
	public String edit() {
		return "edit";
	}
	
	/** 用户修改  **/
	@Limit(module="user",privilege="update")
	public String update() {
		return "update";
	}

	/** 用户列表 (查询) */
	@Limit(module="user",privilege="list")
	public String list() {
		return "list";
	}
}

至此,权限控制实现完毕。


七、整个权限控制的功能实现概要为:

例子:3个不同权限的用户

1.系统管理的权限id(roleId)为:8af0897545e4c91b0145e4cd385d0002;而在sys_popedom_privilege表中,roleId为:8af0897545e4c91b0145e4cd385d0002,拥有所有模块和操作,则系统管理员拥有所有模块的所有操作权限。


2.部门经理的权限id(roleId)为:8af09d7845fe6ef90145fe70a0a80001;而在sys_popedom_privilege表中,roleId为:8af09d7845fe6ef90145fe70a0a80001,拥有部门模块的所有操作(9个),则部门经理拥有部门模块的所有9个操作权限,但没有其他(例如city和code模块)模块的操作权限。

java自定义注解限定参数 java自定义注解权限拦截_注释_04


3.小明的权限id(roleId)为:8af0897545e4c91b0145e4cd65b30003;而在sys_popedom_privilege表中,roleId为:8af0897545e4c91b0145e4cd65b30003,仅拥有部门模块的部分操作(比部门经理比少了list和edit操作),则小明拥有部门模块下的部分操作权限。如下图,其中roleId为8af0897545e4c91b0145e4cd65b30003对应的模块和操作只有下图前7行。


java自定义注解限定参数 java自定义注解权限拦截_java自定义注解限定参数_05


总结:自定义拦截器类会将当前登陆用户的roleId(上文中没有给出User表)和用户要访问的方法的@Limit注释中的module和privilege,共三个字段,与sys_popedom_privilege表中的三个字段进行比较,存在相应的记录,则表示用户有权限。否则,代表用户没有权限。