`

《易道客》源码剖析之五:权限管理

阅读更多

 

一、在本系统中,权限管理分为两种,功能权限和数据权限。

1、功能权限。即用户是否对某一功能是否有操作权限的控制。

1)菜单控制:当用户不具备某一菜单操作权限时,看不到相应的菜单。

2)操作控制:可以控制是否可看到某个按钮。例如当一个用户对只有查看权限,没有增加、修改、删除的权限,则进入菜单后,看不到增加、修改、删除的按钮。

3)链接控制:如果你不具备某操作的访问权限,但你知道这个操作的链接地址,直接在地址栏中输入地址访问时,也会被拦截。

 功能权限

2、数据权限。即可以访问哪些数据,是针对特定功能的。如对于客户数据,可控制能看到本人的、本部门的、还是全公司的数据。

 数据权限

二、功能权限定义。

1、在struts.xml中,定义了两个过滤器,分别用于是否登录验证和权限验证。其中权限验证中包含有是否登陆的验证。如果一个action访问只需要验证用户是否已登陆,则用前者验证。如果一个action还需要验证用户是否有访问该action的权限,则用后者验证。

<interceptors>

  <interceptor name="loginInterceptor" class="com.abc.domain.common.util.LoginInterceptor"></interceptor>

  <interceptor name="accessInterceptor" class="com.abc.domain.common.util.AccessInterceptor"></interceptor>

  <!-- 是否登录控制 -->

  <interceptor-stack name="isLogin">

    <interceptor-ref name="defaultStack"/>

    <interceptor-ref name="loginInterceptor"/>     

  </interceptor-stack>

  <!-- 是否有操作权限,包含有是否登录的功能 -->

  <interceptor-stack name="canAccess">

    <interceptor-ref name="defaultStack"/>

    <interceptor-ref name="accessInterceptor"/>     

  </interceptor-stack>

</interceptors>

  

 

 

 

2LoginInterceptor.java中的登录验证

 

UserInfoLogin userInfo = (UserInfoLogin) ActionContext.getContext().getSession().get("userInfo_login");
if (userInfo != null)
{//正确登录,验证是否重复提交
  //......(略)
} 
else
{//未正确登录
  ActionContext.getContext().getSession().put("errorInfo","登录信息丢失,请重新登录!");
  return "gotoLogin";
}

 

 

3AccessInterceptor.java中的权限验证

 

List<CandoDto> CandoList = userInfo.getCandoList(); // 所有可以访问的功能点
String methodName = invocation.getProxy().getMethod(); //所访问方法的名称
Class cls=invocation.getAction().getClass();//所访问的action类
Method m=cls.getMethod(methodName); //所访问的方法

//重复提交验证
Token token = m.getAnnotation(Token.class);
if (token != null)
{
  return super.doIntercept(invocation);
}

//是否需要权限限制,只有有Access注释标签 的方法才会进行验证
Access annotation = m.getAnnotation(Access.class); //找到方法的权限代码
if(annotation==null) //如果为null,没有权限注释,无需控制
{
  return invocation.invoke();
}

//方法访问权限验证
String accName=cls.getName()+"."+methodName;//所访问的类全名+方法名
boolean canAcc=false;//是否通过验证
if(CandoList!=null && CandoList.size()>0)
{
  for(CandoDto candoDto:CandoList)
  {
    String classMethod=candoDto.getClassMethod();
    if(classMethod!=null && classMethod.indexOf(accName)!=-1)
    {//找到匹配项,说明有该方法权限
    canAcc=true;
    break;
    }
  }
}
if(!canAcc)
{
  ActionContext.getContext().put("errorInfo", "你没有访问权限,请与管理员联系!");
  return "noAccess"; 
}
return invocation.invoke();

 

 

4Access.java

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.METHOD})

public @interface Access

{

}

  

 

 

 

5ModuleFere类中定义了对按钮的权限过滤方法。

 

public static Button[] getCanShowButton(Button[] buttons,String moduleId)
{
  Module module=moduleMap.get(moduleId);
  List<Operation> operations=module.getOperations();
  //当前模块未定义动作的,不进行控制
  if(operations==null || operations.size()==0)
  {
  return buttons;
  }
  //开发人员和系统管理员 不作权限控制
  UserInfoLogin userInfo = (UserInfoLogin) ActionContext.getContext().getSession().get("userInfo_login");
  if(userInfo.isAdmin() || userInfo.isDev())
  { 
    return buttons;
  }

  List<Button> ls=new ArrayList<Button>();//存放可使用的按钮
  Map<String,String> myOpeMap=userInfo.getMyOpeMap();
  for(Button button:buttons)
  {
    String type=button.getType();
    if(type==null || "".equals(type.trim()))
    {//按钮无类型时,不需要控制
           ls.add(button);
           continue;
    }
    for(Operation ope:operations)
    {//当前模块定义的操作
      if(ope.getType().equals(button.getType()))
      {//操作类型与按钮类型相匹配时,表示同一动作
      if(myOpeMap.get(ope.getId())!=null)
      {//如果当前用户的所有操作中有该操作权限,则可使用此按钮。
        ls.add(button);
      }
      break;
    }
    }
  }	
  Collections.sort(ls);
  return ls.toArray(new Button[ls.size()]);
}

 

 

三、模块中对功能权限的使用。

1、在需要使用登录验证的action类之前,加入isLogin过滤器,需要进行权限验证的action类之前,加入canAccess验证。

@InterceptorRef("isLogin ")

public class MyClientAction

{}

 

或者

@InterceptorRef("canAccess")

public class MyClientAction

{}

 

2、在action类的方法中,有需要进行权限验证的方法前,加入access注释。这个注释只有在加有canAccess过滤器的类中才有效。

@Access

public String add() throws Exception

 

3action方法中,需要对按钮进行权限验证过滤。

queryFere.setTopButtons(ModuleFere.getCanShowButton(buttonArr,ModuleFactory_myClient.moduleId));

 

4、在模块定义中的按钮定义。

如:ModuleFactory_myClient.java中在按钮的定义中,有该按钮功能所需要访问的action的方法,该参数会在AccessInterceptor.java过滤器中使用,以验证用户是否有访问的权限。

ops.add(new Operation(moduleId+"_01", "增加", "add", "com.abc.domain.kh.main.inside.action.MyClientAction.add,com.abc.domain.kh.main.inside.action.MyClientAction.addDone"));

ops.add(new Operation(moduleId+"_02", "修改", "update", "com.abc.domain.kh.main.inside.action.MyClientAction.update,com.abc.domain.kh.main.inside.action.MyClientAction.updateDone"));

 

四、角色维护中对权限的管理。

1、相关的类

1ModuleOperation,模块类和操作类,分别对应模块的定义和模块下面操作(按钮)的定义。

2Menu,菜单类,里面记录有一个菜单使用的是哪个模块。

3RoleCandoDataAccess分别是角色类,角色可操作的功能权限,角色可操作的数据权限。

4DataAccessFere,数据权限管理辅助类。

 

2、源码查看

1Role.java

 

public class Role implements Comparable<Role>
{
  @Id
  @GeneratedValue(generator = "uuidGenerator")
  @GenericGenerator(name = "uuidGenerator", strategy = "uuid")
  @Column(length = 32)
  private String id;

  @Column(length = 20)
  private String name;//名称
  
  @OneToMany(cascade=CascadeType.ALL)
  @JoinColumn(name = "role")
  private List<Cando> candoes; //可以进行的操作
  

  @OneToMany(cascade=CascadeType.ALL)
  @JoinColumn(name = "role")
  private List<DataAccess> dataAccesss; //数据权限
}

 

 

2Cando.java

 

public class Cando implements Comparable<Cando>
{
  @Id
  @GeneratedValue(generator = "uuidGenerator")
  @GenericGenerator(name = "uuidGenerator", strategy = "uuid")
  @Column(length = 32)
  private String id;//标识

  @Column(length = 10)
  private String menuCode;//对应的菜单编码

  @Column(length = 32)
  private String moduleId;//模块ID

  @Column(length = 32)
  private String operationId;//对应的操作ID

  @Column
  private int moduleVer = 1; //模块版本

  @Column(length = 500)
  private String classMethod; //由Operation冗余来,多处使用

  @ManyToOne
  @JoinColumn(name = "role", insertable = true, updatable = true)
  private Role role; //对应的角色
}

 

 

3DataAccess.java

 

public class DataAccess
{
  @Id
  @GeneratedValue(generator = "uuidGenerator")
  @GenericGenerator(name = "uuidGenerator", strategy = "uuid")
  @Column(length = 32)
  private String id;//标识

  @Column(length = 20)
  private String type;//类型(控制的那个数据项)

  @Column
  private int value;//值

  @Column(length = 50)
  private String des;//描述

  @ManyToOne
  @JoinColumn(name = "role", insertable = true, updatable = true)
  private Role role; //对应的角色
}

 

 

4DataAccessFere.java,需要增加一个新数据权限管理时,只需要在该类中加入如下一段代码即可。

 

{//客户数据权限
  String name=kh;
  String des="客户信息";
  List<DataAccessDto> ls = new LinkedList<DataAccessDto>();
  ls.add(new DataAccessDto(1, "本人的数据", true));
  ls.add(new DataAccessDto(2, "本部门及所有下级部门的数据", false));
  ls.add(new DataAccessDto(3, "全公司的数据", false));
  accMap.put(name, ls);
  nameMap.put(name, des);
}

 

 

五、UserInfoLogin.java中对权限的支持的。

该类是用户登录信息保存类,在一个用户登录时,就会创新一个这样的类,放在用户session中,里面存放的是当前用户的一系列相关信息。和权限有关的属性有:

private List<CandoDto> CandoList;//用户所有可操作的Cando
private Map<String, Integer> dataAccessMap; //数据权限
private Map<String, String> myOpeMap;//我的操作map(操作ID,操作ID)

 

 

 

返回《易道客》主页

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics