代码改变世界

[讨论]ASP.NET MVC控制到Action参数的权限对策

2010-11-30 16:30  bugfly  阅读(1073)  评论(5编辑  收藏  举报

意图:有时候,用户权限的控制不单单是是Action级别就可以解决,有可能细致到Action参数,如index(string department)这种Action,也许会根据department参数的不同而划分权限。所以就想研究一下是否要控制到Action参数的权限对策,希望大家讨论下。

 

我做了一个简单的Demo演示,具体到所有文件的代码就不一一POST出来了,希望大家可以明白我的思路。

 

首先,没有打算建数据库来测试结果,所以这里用上了Mock Object来模拟数据库。

 

Model First,类代码就不上了,直接上类图。

ActionRes是Action资源类。

ActionParamRes是Action参数资源类。

ControllerRes是Controller资源类。

UserRole是角色。

SysUser是用户。

 

先看看AccountController

 

代码
1 using System;
2  using System.Collections.Generic;
3  using System.Linq;
4  using System.Web;
5  using System.Web.Mvc;
6  using Domain;
7  using Repository;
8  using Moq;
9  using WebUI.Views.Account;
10
11 namespace WebUI.Controllers
12 {
13 public class AccountController : Controller
14 {
15 private IUserRepository<SysUser> _Repository;
16
17 //
18 // GET: /Account/
19 [HttpGet]
20 public ActionResult Login()
21 {
22 return View();
23 }
24
25 [HttpPost]
26 public ActionResult Login(Account_LoginVM FormUser)
27 {
28 //Mock一个数据库访问接口实例
29 initRepository(FormUser);
30
31 SysUser DbUser = this._Repository.findByUserID(FormUser.UserID);
32
33 if (FormUser.UserPwd != DbUser.UserPwd)
34 {
35 HttpContext.Response.Write("密码错误!");
36 HttpContext.Response.End();
37 }
38
39 HttpContext.Session["User"] = DbUser;
40
41 return RedirectToRoute(new { controller = "Home", action = "Index", id = "Admin" });
42 }
43
44 /// <summary>
45 /// 为了实现模拟接口的辅助方法
46 /// </summary>
47 /// <param name="FormUser"></param>
48 private void initRepository(Account_LoginVM FormUser)
49 {
50 var roleList = initMockRoleData();
51 var mock = new Mock<IUserRepository<SysUser>>();
52
53 mock.Setup(m => m.findByUserID(FormUser.UserID))
54 .Returns(new SysUser()
55 {
56 Id = 1,
57 UserID = FormUser.UserID,
58 UserPwd = "654321",
59 NickName = "HuntSoul",
60 Roles = roleList
61 });
62
63 _Repository = mock.Object;
64 }
65
66 /// <summary>
67 /// 为了实现模拟接口的辅助方法
68 /// </summary>
69 /// <param name="FormUser"></param>
70 private static List<UserRole> initMockRoleData()
71 {
72 var actionList = new List<ActionRes>()
73 {
74 new ActionRes()
75 {
76 Id=1,
77 ResourceName="Index",
78 Description="Action",
79 ActionParam=
80 new ActionParamRes()
81 {
82 Id=1,
83 ResourceName="Guest",
84 Description="Action Paramerter"
85 }
86 },
87 //以下注释是加入权限时用的
88 // new ActionRes()
89 //{
90 // Id=2,
91 // ResourceName="Index",
92 // Description="Action",
93 // ActionParam=
94 // new ActionParamRes()
95 // {
96 // Id=1,
97 // ResourceName="Admin",
98 // Description="Action Paramerter"
99 // }
100 // }
101 };
102
103 var controllerList = new List<ControllerRes>()
104 {
105 new ControllerRes()
106 {
107 Id=1,
108 ResourceName="Home",
109 Description="Home Controller",
110 Actions=actionList
111
112 }
113 };
114
115 var roleList = new List<UserRole>
116 {
117 new UserRole()
118 {
119 Id = 1,
120 Controllers =controllerList
121 }
122 };
123 return roleList;
124 }
125 }
126 }
127

 

 

 

 

 

这个Controller主要负责登录验证跳转,可以看出,我们跳转到Index的Action ID参数是Admin.而注释了的第87行以下的部分是,一个ActionParameter资源,暂时没有Admin这个资源,所以可以想到跳转到Index()的时候应该是登陆失败的,这是我的意图。我们先看看Index的代码

 

 

代码
1 using System;
2  using System.Collections.Generic;
3  using System.Linq;
4  using System.Web;
5  using System.Web.Mvc;
6  using WebUI.ActionFilters;
7
8  namespace WebUI.Controllers
9 {
10 public class HomeController : Controller
11 {
12 //
13 // GET: /Home/
14   [HttpGet]
15 [UserAuthority]
16 public ActionResult Index(string id)
17 {
18 return View();
19 }
20 }
21 }
22

 

 

 

再看看UserAuthorityAttribute的内容

 

 

代码
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Web;
5 using System.Web.Mvc;
6 using Domain;
7
8 namespace WebUI.ActionFilters
9 {
10 public class UserAuthorityAttribute : ActionFilterAttribute
11 {
12 public override void OnActionExecuting(ActionExecutingContext filterContext)
13 {
14 var controller = filterContext.RouteData.Values["controller"].ToString();
15 var action = filterContext.RouteData.Values["action"].ToString();
16 var id = filterContext.RouteData.Values["id"].ToString();
17 SysUser User=HttpContext.Current.Session["User"] as SysUser;
18
19 if (!this.isAllowed(User, controller, action, id))
20 {
21 HttpContext.Current.Response.Write("没有权限!");
22 HttpContext.Current.Response.End();
23 }
24 }
25
26 public override void OnActionExecuted(ActionExecutedContext filterContext)
27 {
28 base.OnActionExecuted(filterContext);
29 }
30
31 protected bool isAllowed(SysUser User,string controller,string action,string id)
32 {
33 for (int i = 0; i < User.Roles.Count; i++)
34 {
35 for (int j = 0; j < User.Roles[i].Controllers.Count; j++)
36 {
37 if (User.Roles[i].Controllers[j].ResourceName == controller)
38 {
39 if (User.Roles[i].Controllers[j].Actions.Count > 0)
40 {
41 for (int k = 0; k < User.Roles[i].Controllers[j].Actions.Count; k++)
42 {
43 if (id != null)
44 {
45 if (User.Roles[i].Controllers[j].Actions[k].ResourceName == action &&
46 User.Roles[i].Controllers[j].Actions[k].ActionParam.ResourceName == id)
47 {
48 return true;
49 }
50 }
51 else
52 {
53 if (User.Roles[i].Controllers[j].Actions[k].ResourceName == action)
54 {
55 return true;
56 }
57 }
58 }
59 }
60 }
61 }
62 }
63 return false;
64 }
65 }
66 }

 

 

 

具体测试代码就是这些,我们尝试一下登陆。输入账号Guest和密码123456。

结果 XD

 

当然是登陆失败了,因为在Mock Repository的时候已经设置了密码是654321 ,我们重新输入。

 

这就是我们要的结果,原因是ActionParamRes并没有关于Admin参数的权限,现在我们把之前在initMockRoleData方法内注释的代码反注释。

加入Admin参数后,再看结果。

 

代码
//以下注释是加入权限时用的
new ActionRes()
{
Id
=2,
ResourceName
="Index",
Description
="Action",
ActionParam
=
new ActionParamRes()
{
Id
=1,
ResourceName
="Admin",
Description
="Action Paramerter"
}
}

 

 

 

 

OK,大致演示流程就是这样,希望可以表达我的意图。为了让大家更清晰,我建了一个DB Diagram,虽然没保存- -,方便大家了解。图有点小,太大就叉叉了。

 

完整Demo

结语:写这篇博文,纯为了和大家交流见解,或许我的想法一开始就是错的,或许有更好的做法,欢迎拍砖。