十年磨一劍--從程序員到架構師

一个.net程序员,一个企业应用的开发者,喜欢系统架构,数据库,领域驱动,面向对象,表现层技术。关注重用的理论和实践。设计原则:简单,快速,适应变化能力强,表现层灵活多变...

博客园 首页 新随笔 联系 订阅 管理

谢谢随心所欲和其它网友的关注和看法。
我想在我提出使用URL进行安全管控作为我的web应用程序安全的基础,一定有很多网友的不解,所以就先解释一下这个问题吧。
使用URL进行权限管控的意思是:
1.在上篇中我说过,web应用程序的本质其实就是Request。所以要让您的web应用程序安全,最直接最有效的手段就是让每一次Request都置于你的管控范围之内。这就好比您的web应用程式是一个大型游乐场,而您的安全模组就是这个游乐场的入口检票处。这样凡是要进行您的游乐场的人员,必须都已经通过了您的资格认定了,在web应用程序中它们是无法避开入口而FQ进入的。
因此使用URL进行安全管控的关键不是判断URL,而是判断每次request,检查每次request是否合法,以防止安全漏洞。
作法我已经给出,就是上篇的HttpModule实现。
2.使用URL进行安全管控,是对web应用程序的安全的一次高度抽象。您可以在设计您的web应用程序时完全不管安全问题,该新增时调用新增方法,该审核时调用审核方法。而不用在调用这些方法之前在加入检查是否有调用这些方法的权限。因为在你的方法执行之前,用户的动作合法性已经经过验证,它是有效的。
这样安全管控就可以和业务逻辑脱耦,互不影响,相互独立。好处不言而喻:可以动态调整安全管控而不影响web应用程序本身;可以重用安全管控元件,而无须每开发一次新系统时重新考虑该问题。

在进行我的设计之前,先提提asp.net自带的安全管控方法吧。
asp.net通过在web.config中提供Location节来完成页面的授权动作,这和我的以URL进行安全管控本质上是完全一样的,都是让每一次的Request都需通过认证。
但其只是简单的将授权主体分为匿名用户,用户名和角色名,授权客体则是URL或资料夹,然后将所有的信息hardcode在配置文件中。这对于一个小型系统可能适合,但是对于开发像ERP等大型企业应用系统来说则远远不够。
1.首先我的用户,角色都是可以动态变化的(可以让用户新增,删除)
2.其次我的用户是可以根据其具体业务需求来随时调整某个用户或角色的权限的(也就是说他们可以自己分配权限)
3.遇到像主管休假时,还可以根据其自己的设定让用户将其权限再分配给其它用户来完成诸如代理的功能。
4.我有很多的企业应用系统,我要集中管理我的用户,角色,以及它们的权限(一个专门的权限管控或分配系统)

在关于web应用程序安全的思考(一)的最后,我给出了一个权限管控类的大致框架。
它有以下几个特点:
1.它是一个HttpModule
2.它捕获其中的AuthorizeRequest事件
3.它在这个事件中进行提取用户ID(认证)和判断用户是否有权(授权),以及对于没有权限的处理动作(拒绝)三个部分。

所以我们的安全管控模组的设计都从第3点的这三个部分出发。
首先我们要设计这三个接口来:
1.认证接口

Interface IAuthenticate
{
     
//没有传参数是因为可以通过HttpContext.Current来获取这次请求的所有上下文信息
     
//返回用户标识(userid),如果用户没有登录,则返回null
     string Authenticate();
}


2.授权接口

Interface IAuthorize
{
    
//将用户id传入进行权限判断,同样其它参数通过HttpContext.Current来获得
    
//返回false表示无权登录
    bool Authorize(string userid);
}


3.无权登录时处理的动作

Interface IRequestRefuser
{
    
//当无权时进行此方法进行拒绝
    void Refuse();
}



关于这3个接口如何使用,其实在(一)中我有代码,把getuserid,hasright等方法做替换即可。


现在我们一个一个的接口来做实现
1.认证接口
要对所有的request作出管控,显然只有一种认证方法是远远不够的,就简单如aspx和web service吧,前者可以通过cookie或session来存储用

户ID,而后者就有可能通过soap header或某个参数来调用,甚至以后还会出现其它的user id提取方法。所以对于默认的认证类别我没有使用

hardcode方法,而是采用动态加载动态模组来完成。

//首先我会增加一个认证模组,每个认证模组实现某一类Request的认证方式

Interface IAuthenticateModule
{
    
//是否匹配本次请求,采用当前这个认证模组
    bool IsMatch();
    
    
//认证
    string Authenticate();
    
}


//我对认证的实现
class DefaultAuthenticate:IAuthenticate
{
   
//加载的认证模组
   ArrayList _modules = new ArrayList();   

   
static DefaultAuthenticate()
   
{
       读取本系统的配置,利用反射机制动态加载认证模组
   }


   
//认证
   string Authenticate()
   
{
      
//遍历每个模组,找出第一个匹配的模组进行认证
      
//所以与配置的顺序有关(如果有2个相同的匹配,可以把专用的模组放在前面)
      foreach(IAuthenticateModule module in _modules)
      
{  
          
if(module.IsMatch())
          
{
              
return module.Authenticate();
          }

      }

   }

}


//至于实现则很简单
//如以下为aspx的认证方式
class WebFormAuthenticateModule:IAuthenticateModule
{
    
bool IsMatch()
    
{
       
if(Request.Url请求的是aspx文件)
           
return true;
       
return false;
    }


    
string Authenticate()
    
{
       
if(Session["userid"]!=null)
           
return Session["userid"].ToString();
       
return null;
    }

}


//以下为webservice的认证方式
class WebServiceAuthenticateModule:IAuthenticateModule
{
    
bool IsMatch()
    
{
       
if(Request.Url请求的是asmx文件)
           
return true;
       
return false;
    }


    
string Authenticate()
    
{
       
string userid = 提取soap头的user id栏位
       
return userid;
        
    }

}

 

当然您可以在您的安全模组中默认先实现一些认证模组,然后配置时放在后面一些。如果您的新系统有新的认证方式,只要实现一个新的认证模组再配置进去就可以了。

今天就先到这里吧,下篇我会对授权进行实现,希望能给大家一些启发。

posted on 2006-12-24 11:03  Kevin Zou  阅读(3001)  评论(6编辑  收藏  举报