老话题,权限设计及实现!

前言

  以前也写过两篇关于权限这个话题的文章《我所理解的权限》《Asp.net Mvc 身份验证、异常处理、权限验证(拦截器)》,最近在新的项目中,权限设计这块做了重新的考虑和设计。一直有人说权限这个东西不能太死,所以我们本着把权限尽量做到透明化,而设计了这样的权限管理。

设计思路

1、因为项目使用的是asp.net mvc,在mvc中一个action就对应的是一个URL,一般来说一个action只会做一件事情,所以我们获取请求的action就可以知道你将要干什么,那么我们把系统中所有的模块全部存放到数据库中,并且把功能按钮也放到数据库中,可以构造成一个树形菜单的形式。如图:

其中,这里分为导航权限和功能权限,导航权限指:系统运行时会自动根据权限分配加载到导航树上去的;功能权限指:这个页面上有哪些按钮。这里我采用一个标识来区分哪些为导航权限,哪些为功能权限。因为登录系统,加载导航栏时只需要获取导航权限进行验证就OK了。

2、保存用户权限,在角色赋权的页面,直接加载整个导航表中的数据,采用树形展示,在角色表中保存模块表的主键ID即可。

3、为角色关联用户则可以直接关联用户的ID或者工号什么的都可以。

4、拉取权限加载导航,用户登录即可去角色表中获取到该用户拥有哪些角色。获取模块表ID,根据模块表ID,获取模块表数据,剔除功能权限,然后动态加载导航。

5、功能权限的验证方案,这也是考虑很久的一个地方,以前的思路就是直接传一个参数值去判断是否显示这个按钮。这次采用了对HTML进行剔除的方式进行功能按钮的隐藏和显示。

具体实现

1、导航权限验证:采用拦截器,在action执行前进行权限验证。

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    //当前访问地址
    string Code = string.Format("/{0}/{1}", filterContext.RouteData.Values["controller"].ToString(), filterContext.RouteData.Values["action"].ToString()).ToLower();
    string[] ListResource = ResourceBiz.Instance.Get(new string[] { "Url" }).Select(p => p.Url).Where(p => !string.IsNullOrEmpty(p)).Select(p => p.ToLower()).ToArray();//获取模块表导航代码这个字段与当前URL比对
    if (!ListResource.Contains(Code))//模块表中不存在的URL,默认不进行验证
        return;
    if (!Authentication.GetResourceCode().Contains(Code))//Authentication.GetResourceCode()为当前用户所拥有的权限
    {
        //验证不通过
        ContentResult Content = new ContentResult();
        Content.Content = "<script type='text/javascript'>alert('权限验证不通过!');history.go(-1);</script>";
        filterContext.Result = Content;
    } 
}

2、验证页面的功能权限:也是采用拦截器,在action执行后,获取即将渲染的HTML源码进行分析及剔除操作。

//获取即将呈现的HTML,剔除功能按钮
public override void OnResultExecuted(ResultExecutedContext filterContext)
{//用户控件则不进行筛选
   if (!filterContext.IsChildAction)
      filterContext.HttpContext.Response.Filter = new WhitespaceFilter(filterContext.HttpContext.Response, filterContext);//重写
}
//重写
public class WhitespaceFilter : System.IO.MemoryStream
{
    private System.IO.Stream Filter = null;
    private ResultExecutedContext filterContext = null;
    private string Source = string.Empty;

    //构造函数,用来接收变量
    public WhitespaceFilter(HttpResponseBase HttpResponseBase, ResultExecutedContext filterContexts)
    {
        Filter = HttpResponseBase.Filter;
        filterContext = filterContexts;
    }
    //读取HTML源码
    public override void Write(byte[] buffer, int offset, int count)
    {
        Source += System.Text.Encoding.UTF8.GetString(buffer);//HTML源码  
    }
    //分析进行权限处理
    public override void Close()
    {
        //当前访问地址
        string Code = string.Format("/{0}/{1}", filterContext.RouteData.Values["controller"].ToString(), filterContext.RouteData.Values["action"].ToString()).ToLower();
        string[] ListResource = ResourceBiz.Instance.Get(new string[] { "Url" }).Select(p => p.Url).Where(p => !string.IsNullOrEmpty(p)).Select(p => p.ToLower()).ToArray();//获取模块表所有记录
        if (ListResource.Contains(Code))//模块表中不存在的URL,默认不进行验证
        { //解析处理
            HtmlDocument Document = new HtmlDocument();
            Document.LoadHtml(Source);
            HtmlNode htmlNode = Document.DocumentNode;

       /*这里需要获取这个模块下所有的共功能权限,然后跟你所拥有的这个页面的功能权限比对,如果不拥有这个功能权限,则可以根据规则获取到这段HTML,然后删除掉*/
/*_______________________开始分析处理功能按钮,这里可以自己增加验证规则___________________________*/ HtmlNodeCollection hnc = htmlNode.SelectNodes("//a");//获取需要验证的功能按钮HTML,由开发人员自己定义,你也可以给个特定的标识来标识这个标签为功能按钮,譬如:htmlNode.SelectNodes("//a[@class='add']");获取神马的 if (hnc != null) { foreach (HtmlNode node in hnc) { //拿到所有A标签,然后把href取出来,跟当前用户所拥有的功能权限比对,如果相等或者包含则删除 string CodeStr = node.Attributes["href"] != null ? node.Attributes["href"].Value.ToLower() : ""; node.ParentNode.RemoveAll(); } } } Filter.Write(System.Text.Encoding.UTF8.GetBytes(Source), 0, System.Text.Encoding.UTF8.GetByteCount(Source)); base.Close(); } }

这样设计的话,系统中关于权限的地方就全部透明化了。只是在功能权限验证时,要把所有情况全部考虑进去,不知道从文字中大家能不能明白我所需要表达的意思,文笔实在太差了。

posted @ 2012-12-03 16:08  CHild.  阅读(4699)  评论(19编辑  收藏  举报