今天刚学习了通过自定义注解+拦截器实现权限控制,自己花了点时间整理,发到网站同网友交流分享。
一、定义一个自定义注解类
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。
2.操作表:该表相当于是你这个项目里所有的权限。popedomModule代表一个大的功能模块,popedomPrivilege代表一个模块下的具体操作:常见的有增删改查,可参考下图理解。
3.操作权限表:该表是权限组所对应的模块和操作。例子:如下图的roleId为8af0897545e4c91b0145e4cd385d0002,在权限组表中对应的是系统管理员权限组(见上上图),该权限组拥有的模块和操作名称在下图第二三列,可对应上图知道功能。说明:系统管理员权限组在当前系统中,只拥有下图所示的模块为city和code的所有操作权限,没有模块为company下的任何操作权限。通过将用户分配到不同的权限组,和设置权限组的拥有的模块和操作,就可以实现权限控制。
三、定义上面三个表所对应的类和*.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模块)模块的操作权限。
3.小明的权限id(roleId)为:8af0897545e4c91b0145e4cd65b30003;而在sys_popedom_privilege表中,roleId为:8af0897545e4c91b0145e4cd65b30003,仅拥有部门模块的部分操作(比部门经理比少了list和edit操作),则小明拥有部门模块下的部分操作权限。如下图,其中roleId为8af0897545e4c91b0145e4cd65b30003对应的模块和操作只有下图前7行。