昨天做了一个例子,是在Mvc4下,对于不同的用户角色(Roles),可以访问不同的页面。根据MVC3中的方式,只需要在相应的Controller上添加上类似于[Authorize(Roles="Admin")]这样的描述就行。后来通过测试发现,如果单独使用[Authorize]或者使用[Authorize(User="xxx")]都是可以的,但是一旦使用[Authorize(Roles="xxx")],就会报数据库连接错误:在 与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。未找到或无法访问服务器。请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。 (provider: SQL Network Interfaces, error: 26 - 定位指定的服务器/实例时出错)。在网上查了好多资料,都无法解决,后来我决定自定义AuthorizeAttribute。
首先,应该定义角色,以及用户和角色的对应关系:
- public enum Roles
- {
- Admin,
- PowerManager,
- User,
- Guest
- }
- public class RolesManager
- {
- public static List<Roles> GetRoles(string userName)
- {
- switch (userName)
- {
- case "admin":
- return new List<Roles> {Roles.Admin, Roles.PowerManager};
- case "gqq":
- return new List<Roles> {Roles.Admin};
- default:
- return new List<Roles> {Roles.Guest};
- }
- }
- }
其次,在登录的时候,就应该把用户的组信息写到验证Cookie中去。
- <span style="white-space:pre"> </span>[HttpPost]
- [AllowAnonymous]
- [ValidateAntiForgeryToken]
- public ActionResult Login(LoginModel model, string returnUrl)
- {
- //是否登录成功,其中AccountBiz.Login是我写的一个static方法,里面是验证用户名、密码的逻辑
- if (ModelState.IsValid && AccountBiz.Login(model.UserName, model.Password))
- {
- var roles = string.Join(",", RolesManager.GetRoles(model.UserName).ToArray());
- //如何用复杂的代码实现登录
- FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1,
- model.UserName,
- DateTime.Now,
- DateTime.Now.AddMinutes(20),
- false,
- roles //写入用户角色
- );
- //对authTicket进行加密
- string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
- //存入cookie
- var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
- System.Web.HttpContext.Current.Response.Cookies.Add(authCookie);
- if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
- && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\"))
- {
- return Redirect(returnUrl);
- }
- return RedirectToAction("Index", "Home");
- }
- // 如果我们进行到这一步时某个地方出错,则重新显示表单
- ModelState.AddModelError("", "提供的用户名或密码不正确。");
- return View(model);
- }
第三步:自定义继承自AuthorizeAttribute的类ManagerOnlyAttribute,重写AuthorizeCore方法改变验证规则。
- public class ManagerOnlyAttribute : AuthorizeAttribute
- {
- // 只需重载此方法,模拟自定义的角色授权机制
- protected override bool AuthorizeCore(HttpContextBase httpContext)
- {
- //获得当前的验证cookie
- HttpCookie authCookie = httpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
- //如果当前的cookie为空,则返回。
- if (authCookie == null || authCookie.Value == "")
- {
- return false;
- }
- FormsAuthenticationTicket authTicket;
- try
- {
- //对当前的cookie进行解密
- authTicket = FormsAuthentication.Decrypt(authCookie.Value);
- }
- catch
- {
- return false;
- }
- if (authTicket != null)
- {
- //和存入时的分隔符有关系,此处存入时的分隔符为逗号
- var userRoles = authTicket.UserData.Split(new[] {','}).ToList();
- var roles = Roles.Split(new[] {','}).ToList();
- return roles.Any(x => userRoles.Contains(x));
- }
- return false;
- }
- }
第四步:将自定义的属性描述加在需要访问的Controller或Action上。
- <strong> </strong>[ManagerOnly(Roles = "Admin,PowerManager")]
- public partial class MusicStoreController : Controller
- {<span style="font-family: Arial, Helvetica, sans-serif;">}</span>
当我们以admin用户登录,访问http://localhost:8090/MusicStore时,由于admin用户既属于Admin组,也属于PowerManager组,所以可以访问。如果以gqq登录,由于gqq属于Admin组,因此也可以访问该链接(属于Admin、PowerManager中的任意一个组即可)。如果以test用户登录,则该链接不可以访问。(根据上文代码,test属于Guest组)