基于URL实现权限控制

     最近一直在做毕业设计的后台管理模块。很早以前就想写一篇关于权限控制的文章,苦于一直不理解如何用URL实现。以至于当初设计数据库和编写页面实现的时候都没有将权限的URL考虑进去,当时只想直接匹配权限的名称就可以了。直到前几天在JavaEye论坛上看到了一篇题为《一个简易实用的web权限管理模块的应用与实现》的文章才对基于URL的权限控制有了较深的认识。加上之前一直在用另一种方法来实现相同功能,所以这几天一直在总结这两种方法的优劣之处。

       值得提醒的是,本文只关注基于URL和不基于URL两种方法的优劣之处和具体如何实现。数据库设计不会讲的很详细,虽然那也是很重要的,但是关于它的好坏很难有一个统一的标准,这个要看系统具体实现的功能和对效率的要求如何。其实即使是教科书上面提到的规范在实际应用中也是起到参考作用而已,很多时候我们宁愿牺牲规范来换取效率。总之一句话:简单才是美。如果想深入去研究数据库该如何设计的话建议去看看上面提到的文章中的回复,里面大量回复都是针对数据库设计的。好了,先引用上述文章的一些重点内容先:

 

 >>>>以下为引用内容:

本文介绍一个简易实用的web权限管理模块的应用与实现。

    先介绍数据模型和应用界面,后继对实现细节做选择性阐述。

    数据表关系如下:

 

 

 

 

 

 

 

    该图标明了登陆用户、角色、部门(机构)、用户组、角色和模块功能之间的关系。为方便起见,所有表都只保留必要字段。

      在本系统设计中,如下概念有着相对特殊的含义。

      一、用户(user):系统的使用者。

      二、部门(org):体现了用户的行政关系,

      三、组(group) :是某相同职能的用户的集合,可以和用户一样与角色产生关联。设置组的目的是为了方便用户的角色分配,减少用户与角色的直接对应关系。用户的角色可以是其组角色和其直接分配的角色之合集。限于作者的时间和精力,组功能在该系统中没有具体的实现。
     
四、角色(role):角色对应着某些功能(function)的集合,被分配一个角色意味着有权执行这些功能。角色表中的字段"functions"记录相关的功能id,id之间用逗号隔开。

      五、功能(function):系统的一个或者多个执行准入。

      那么如何表现功能以最终实现控制用户的每一个细微动作呢?假如不特定于某种架构,可以这么设计该表字段:

Sql代码:省略。

    假定有三个web访问路径
  http://127.0.0.1:8080/app/sys/user.jsp?action=index&userid=1203 
  http://127.0.0.1:8080/app/sys/user.yuetong?action=add
  http://127.0.0.1:8080/app/sys/user.yuetong?action=update&userid=1203
    
这三个访问点被人为的划分为两个功能准入(当然亦可以是一个或者三个),见下图     

 

 

 

 

 

 

 

 

 

 

   由此可知,功能是衡量用户准入的最小刻度。在用户访问某个地址的时候,我们可以通过解析URL对比他拥有的功能权限来实现权限管理。

     借助于某些架构或者设计思路,可以避免用户直接访问JSP页面,甚至全系统的访问地址都使用同一后缀,这种情况下可以省去SUFFIX字段。 本系统就是这种情况(JSP页面置于WEB-INF下,采用struts2架构)。

     六、功能模块树(function tree):功能的目录组织,起分类的作用。在为角色设定功能的时候,用户界面可以利用带选择框的js树。而这颗js树是后台的功能树表以及功能表的联合表现形式。功能模块树可以方便的与菜单树建立映射关系,限于作者的时间和精力,该系统并未实现菜单树。

 

>>>>引用结束

 

 

       作者对权限控制这一块估计也是比较有经验了,考虑的东西挺全面的。从他的数据库设计中可以看出真正核心的表只有两个:角色和权限。当然实际开发时一般不太可能设计的这么简单,其他表的作用就是用来优化你的数据模型和方便以后扩展用的。你的数据库设计的灵不灵活、健不健壮、安不安全就看其他表如何设计了。本着简单就是美的原则,我设计的数据库有四个表:

 

 

 

 

用户信息:保存用户的基本信息,rolid与角色组的ID关联

角色组:方便用户管理,用户通过角色组和权限发生关联

角色权限:rolid和fctid分别和角色组ID,功能列表ID发生关联。have字段的数据类型为Boolean,表示ID为rolid的角色组是否具有ID为fctid的权限

功能列表:系统的一个或者多个执行准入。由于系统使用了Struts2,所以后缀名统一为action,省略了suffix字段。Module是为了以后功能增多,需要分模块的时候预留的,目前没有实现。

 

       除去那些組的概念,这个设计和上文作者的最大不同就是角色组和权限发生关联的方式,他是用角色组里面的一个字段functions保存权限ID,id之间用逗号隔开。我的想法比较简单,既然角色组和权限列表之间是多对多的关系,那就在它们之间新建一个连接表。比较麻烦的就是他们之间的级联关系,如果你没有用Hibernate或EJB来帮你实现这些级联操作又不想在数据库里面使用外键,那么在用代码操作数据库的时候就要格外留心了,通常我都不能一次就把那些级联关系用代码完整的实现,每次都等到出错了再去找原因。所以动手写代码前还是考虑周全点好。

 

       OK,到具体实现了。第一种方法,假设我不用URL,那么新建权限的时候只需填写权限的名称和描述,很简单。

 


假设我新增了一个叫“管理用户组”的权限,那么我首先需要在用户登录成功后保存他的角色组ID到Session里面,然后在对应的Action类的开头添加这些代码:

 

Integer rolid = (Integer) ActionContext.getContext().getSession().get(

              "rolid");   // 获取角色组ID

       if (manager.getRight(rolid, "用户组管理")) {

           // do something

           return "grouplist";

        } else {

           if (manager.getMSG().equals("unallocate")) {

              this.setMsg("对不起,该权限尚未分配!");

           } else if(manager.getMSG().equals("error")) {

              this.setMsg("系统出错!");

           } else {

              this.setMsg("对不起,你无权查看用户组信息!");

           }

           return "error";

}

manager是Manager类的一个实例, getRight()方法实现查询角色组是否具有某项权限的功能。需要传入角色组ID和权限名称参数。如果查询不到权限名称为“用户组管理”的权限则说明该权限还没有分配。

这样做的缺点很明显:

1.造成代码冗余。假如我的用户组管理又分为显示、新增、修改和删除的功能,那么我需要在每个方法的开头都加上这些代码。那么当权限变多了,就要不断的重复这些代码,重用性低也很不利于以后的维护。

2.   灵活性差。如果我需要对某项权限的名称进行修改,那么只能在后台先把原来的权限删除掉,然后再新建一个和代码里面的名称相同的匹配起来。又或者是直接到代码里面的修改,改完还有重新部署,太麻烦了。

3. 效率低。每次执行判断都需要去数据库执行一次而查询,还是多表连接查询。

4.  暂时想不到

 

造成这些缺点的原因就是在代码里面直接使用到了数据库里面保存的信息。数据库里面的数据是会经常改变的,而代码在项目完成后是不能再改的。不然就叫做升级或者是你系统有漏洞了。所以需要在代码和权限信息之间引入一种相对不变的“变量“,然后让代码和那个“变量“发生关联,而数据库里面只需添加几个字段。那个“变量”就是URL,相信很少人会在web.xml或struts.xml里面部署完URL后还会去改它。不过即使修改也没关系,容器会自动帮你重新部署,问题不大。

 

第二种方法:使用URL。那么新建权限的时候就要填写多两个信息。



 

同样新增一个叫做“管理用户组”的权限,url为/sys/group,params为list。

那么同样在登录成功后需要保存用户的角色组ID,然后实现一个方法去根据角色组ID来查询该角色组拥有的所有权限的URL,然后再用request.getRequestURL()来获得当前URL和用户具有的URL进行匹配。这样做确实变得灵活了,你可以在管理后台尽情修改权限信息,包括名称和URL,只要和你在部署文件中编写的URL保持一致就可以了。但是依然存在代码冗余和效率差的缺点。下面是我的改进方法:

 

       1.  设计一个PowerInfo类用来保存用户的权限信息,当用户登录成功后,新建一个对象把查询数据库返回的信息保存在里面,然后再把对象添加到Session。以后需要进行权限判断的时候都从Session里面取出该对象进行URL匹配。这样就解决了效率低的问题。

public class PowerInfo {
 // 可访问资源
 private ArrayList<String> uri;
 private ArrayList<String> params;
 // 记录用户的越权次数,当次数超过规定的时候自动退出系统
 private int ultravires;
 public ArrayList<String> getUri() {
  return uri;
 }
 public void setUri(ArrayList<String> uri) {
  this.uri = uri;
 }
 public ArrayList<String> getParams() {
  return params;
 }
 public void setParams(ArrayList<String> params) {
  this.params = params;
 }
 public int getUltravires() {
  return ultravires;
 }
 public void setUltravires(int ultravires) {
  this.ultravires = ultravires;
 }

 

      2.  使用Filter过滤所有的用户请求,在Filter里面用当前URL和用户权限的URL进行匹配。这样就解决了代码冗余的问题。实现这个方法的时候有个问题需要注意,当我使用了Struts2后,发现凡是.action后缀的请求都不经过我的Filter。只是.jsp的才会过滤。这个问题具体为什么我也不是很清楚,但和你部署Filter的位置肯定有关,因为当我把自己写的Filter部署到struts2的Filter前面的时候问题就解决了。估计如果struts2的Filter先在前面处理了.action的请求后不执行chain.doFilter(request,response)就直接返回界面给用户了。

 

以上是本人对基于URL实现权限控制的一些不成熟想法。欢迎各位指出不足之处。



一下是本人结合自己项目的实际情况发表的一些看法。

要做的更加灵活的话,需要增强

五、功能(function):系统的一个或者多个执行准入。

那么如何表现功能以最终实现控制用户的每一个细微动作呢?假如不特定于某种架构,可以这么设计该表字段:

Sql代码:省略。

    假定有三个web访问路径
  http://127.0.0.1:8080/app/sys/user.jsp?action=index&userid=1203 
  http://127.0.0.1:8080/app/sys/user.yuetong?action=add
  http://127.0.0.1:8080/app/sys/user.yuetong?action=update&userid=1203
    
这三个访问点被人为的划分为两个功能准入(当然亦可以是一个或者三个),见下图     

 

 

 

 


每个url都要设置成一个功能,要有参数和参数值两个字段,我们项目里面要多个固定的参数和多个固定的参数值才可以定位到一个页面

比如:

   http://127.0.0.1:8080/app/sys/user.jsp?action=index&type=oldUser&userid=1203   
   http://127.0.0.1:8080/app/sys/user.yuetong?action=add &type=new


  http://127.0.0.1:8080/app/sys/user.yuetong?action=update&step=updateBaseInfo&userid=1203



  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值