MVC身份验证及权限管理

MVC自带的ActionFilter 数据库

在Asp.Net WebForm的中要作到身份认证微软为咱们提供了三种方式,其中最经常使用的就是咱们的Form认证,须要配置相应的信息。例以下面的配置信息: 数组

<authentication mode="Forms">
    <forms loginUrl="Login.aspx" defaultUrl="Default.aspx" protection="All" />
</authentication>
<authorization>
    <deny users="?"/>
    <allow users="*"/>
</authorization>

说明咱们登陆页面是Login.aspx,登陆成功后的默认页面是Default.aspx,而咱们用户信息采用验证和加密两种方式。并且最重要的 是咱们要写好受权方式(下面的受权必定要写不然只说明使用Forms认证而后设置相关属性是没有用的),拒绝全部匿名用户,只有登陆用户能够正常访问。这 样以后咱们设置点击登陆按钮将用户名写进cookie(也就是执行FormsAuthentication.SetAuthCookie(name, false);)就能够了。 cookie

在Asp.Net MVC中咱们一样可使用Forms认证,可是若是你按照WebForm中的作法去作就不行了。例如你这样配置信息: ide

<authentication mode="Forms">
    <forms loginUrl="~/Account/Login" defaultUrl="~/Home/Index" protection="All"/>
</authentication>
<authorization>
    <deny users="?"/>
    <allow users="*"/>
</authorization>

你在Login.aspx中设置登陆来触发AccountController中的Logon来登陆,其中Logon代码: 函数

public ActionResult Logon(string name,string password)    
{    
    if (name == "jianxin160" && password == "160796")    
    {    
        FormsAuthentication.SetAuthCookie(name, false);    
        return Redirect("~/Home/Index");    
    }    
    else
    {    
        return Redirect("/");    
    }    
}

这样的操做以后你会发现你的Logon是不会执行的。缘由是什么呢?怎么一样的设置为何到了MVC中就不行了?缘由就是两者机制不一样,由于你设置的受权方式让Logon没法访问了。那么咱们怎么来作呢? ui

其实在Asp.Net MVC中咱们有更好的方式来作这一切,咱们不须要受权方式,也就是说咱们的配置信息像这样: 加密

<authentication mode="Forms">
    <forms loginUrl="~/Account/Login" defaultUrl="~/Home/Index" protection="All"/>
</authentication>

不须要说明匿名用户不能登陆等。固然了,你会发现仅仅就这样作确定不行的咱们还要换一种方式告诉系统哪些是须要登陆才能访问的。你或许 想,o(︶︿︶)o 唉,那太麻烦了吧。其实不是这样的,很简单,咱们只须要在须要认证的Action上标记[Authorize]就能够了。例如我在Home文件夹中有两个 页面Index和Home,我如今想让Index通过认证才能访问,而Home不须要,那么只须要给Index这个Action标记 [Authorize],也就是: spa

[Authorize]    
public ActionResult Index()    
{    
    return View();    
}    

public ActionResult Home()    
{    
    return View();    
}

这样Index就必须登陆以后才能访问,而Home是不须要登陆的。若是你须要进行角色受权那么您就能够在标记Authorize的时候指明角色 (例如[Authorize(Role=Administrators)] ),不过您这是就必须使用微软给咱们提供的Membership机制了,由于您的Role不多是无缘无故有的,而是存在于对应的数据库中的,这个我在另 一篇博客中提到过就很少说了。 code

自定义ActionFilter orm

有时候这样的认证或许您还不可以知足,或者说您以为不够灵活,那么也没有关系,Asp.Net MVC是容许您自定义ActionFilter的。例如我如今自定义身份认证:

using System;   
using System.Collections.Generic;   
using System.Linq;   
using System.Web;   
using System.Web.Mvc;   
using System.Web.Security;    

namespace FormFormsAuthenticationMvc   
{   
    public class RequiresAuthenticationAttribute:ActionFilterAttribute   
    {   
        public override void  OnActionExecuting(ActionExecutingContext filterContext)   
        {   
            if (!filterContext.HttpContext.User.Identity.IsAuthenticated)   
            {   
                string returnUrl = filterContext.HttpContext.Request.Url.AbsolutePath;   
                string redirectUrl = string.Format("?ReturnUrl={0}", returnUrl);   
                string loginUrl = FormsAuthentication.LoginUrl + redirectUrl;   
                filterContext.HttpContext.Response.Redirect(loginUrl, true);   
            }   
        }    
    }   
}

若是须要进行用户管理,我再定义角色相关的Filter:

using System;    
using System.Collections.Generic;    
using System.Linq;    
using System.Web;    
using System.Web.Mvc;    
using System.Web.Security;    

namespace MvcApplication1.MyClass    
{    
    public class RequiresRoleAttribute:ActionFilterAttribute    
    {    
        public string Role { get; set; }    

        public override void OnActionExecuting(ActionExecutingContext filterContext)    
        {    
            if (!string.IsNullOrEmpty(Role))    
            {    
                if (!filterContext.HttpContext.User.Identity.IsAuthenticated)    
                {    
                    string returnUrl = filterContext.HttpContext.Request.Url.AbsolutePath;    
                    string redirectUrl = string.Format("?ReturnUrl={0}", returnUrl);    
                    string loginUrl = FormsAuthentication.LoginUrl + redirectUrl;    
                    filterContext.HttpContext.Response.Redirect(loginUrl, true);    
                }    
                else
                {    
                    bool isAuthenticated = filterContext.HttpContext.User.IsInRole(Role);    
                    if (!isAuthenticated)    
                    {    
                    throw new UnauthorizedAccessException("You have no right to view the page!");    
                    }    
                }    
            }    
            else
            {    
                throw new InvalidOperationException("No Role Specified!");    
            }    
        }    
    }    
}

其实您会发现上面两个Attribute其实MVC自带的Authorized已经解决了,这里主要告诉你们若是有须要您是能够扩展的。

好了,今天就到这里吧!源代码下载:FormFormsAuthenticationMvc

 

ASP.NET MVC 创建 ASP.NET 基础之上,不少 ASP.NET 的特性(如窗体身份验证、成员资格)在 MVC 中能够直接使用。本文旨在提供可参考的代码,不会涉及这方面太多理论的知识。

本文仅使用 ASP.NET 的窗体身份验证,不会使用它的 成员资格(Membership) 和 角色管理 (RoleManager),缘由有二:一是不灵活,二是和 MVC 关系不太。

1、示例项目

image

User.cs 是模型文件,其中包含了 User 类:

public class User
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Password { get; set; }
    public string[] Roles { get; set;  }
}

UserRepository 为数据存取类,为了演示方便,并无链接数据库,而是使用一个数组来做为数据源:

public class UserRepository
{
    private static User[] usersForTest = new[]{
        new User{ ID = 1, Name = "bob", Password = "bob", Roles = new []{"employee"}},
        new User{ ID = 2, Name = "tom", Password = "tom", Roles = new []{"manager"}},
        new User{ ID = 3, Name = "admin", Password = "admin", Roles = new[]{"admin"}},
    };

    public bool ValidateUser(string userName, string password)
    {
        return usersForTest
            .Any(u => u.Name == userName && u.Password == password);
    }

    public string[] GetRoles(string userName)
    {
        return usersForTest
            .Where(u => u.Name == userName)
            .Select(u => u.Roles)
            .FirstOrDefault();
    }

    public User GetByNameAndPassword(string name, string password)
    {
        return usersForTest
            .FirstOrDefault(u => u.Name == name && u.Password == password);
    }
}

2、用户登陆及身份验证

方式一

修改 AccountController:原有 AccountController 为了实现控制反转,对窗体身份验证进行了抽象。为了演示方便,我去除了这部分(以及注册及修改密码部分):

public class AccountController : Controller
{
    private UserRepository repository = new UserRepository();
    
    public ActionResult LogOn()
    {
        return View();
    }

    [HttpPost]
    public ActionResult LogOn(LogOnModel model, string returnUrl)
    {
        if (ModelState.IsValid)
        {
            if (repository.ValidateUser(model.UserName, model.Password))
            {
                FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
                if (!String.IsNullOrEmpty(returnUrl)) return Redirect(returnUrl);
                else return RedirectToAction("Index", "Home");
            }
            else
                ModelState.AddModelError("", "用户名或密码不正确!");
        }
        return View(model);
    }

    public ActionResult LogOff()
    {
        FormsAuthentication.SignOut();
        return RedirectToAction("Index", "Home");
    }
}

修改 Global.asax:

public class MvcApplication : System.Web.HttpApplication
{
    public MvcApplication()
    {
        AuthorizeRequest += new EventHandler(MvcApplication_AuthorizeRequest);
    }

    void MvcApplication_AuthorizeRequest(object sender, EventArgs e)
    {
        IIdentity id = Context.User.Identity;
        if (id.IsAuthenticated)
        {
            var roles = new UserRepository().GetRoles(id.Name);
            Context.User = new GenericPrincipal(id, roles);
        }
    }
    //...
}

给 MvcApplication 增长构造函数,在其中增长 AuthorizeRequest 事件的处理函数。

代码下载:Mvc-FormsAuthentication-RolesAuthorization-1.rar (243KB)

方式二

此方式将用户的角色保存至用户 Cookie,使用到了 FormsAuthenticationTicket。

修改 AccountController:

public class AccountController : Controller
{
    private UserRepository repository = new UserRepository();
    
    public ActionResult LogOn()
    {
        return View();
    }

    [HttpPost]
    public ActionResult LogOn(LogOnModel model, string returnUrl)
    {
        if (ModelState.IsValid)
        {
            User user = repository.GetByNameAndPassword(model.UserName, model.Password);
            if (user != null)
            {
                FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
                    1,
                    user.Name,
                    DateTime.Now,
                    DateTime.Now.Add(FormsAuthentication.Timeout),
                    model.RememberMe,
                    user.Roles.Aggregate((i,j)=>i+","+j)
                    );                    
                HttpCookie cookie = new HttpCookie(
                    FormsAuthentication.FormsCookieName,
                    FormsAuthentication.Encrypt(ticket));
                Response.Cookies.Add(cookie);

                if (!String.IsNullOrEmpty(returnUrl)) return Redirect(returnUrl);
                else return RedirectToAction("Index", "Home");
            }
            else
                ModelState.AddModelError("", "用户名或密码不正确!");
        }
        return View(model);
    }

    public ActionResult LogOff()
    {
        FormsAuthentication.SignOut();
        return RedirectToAction("Index", "Home");
    }
}

修改 Global.asax:

public class MvcApplication : System.Web.HttpApplication
{
    public MvcApplication()
    {
        AuthorizeRequest += new EventHandler(MvcApplication_AuthorizeRequest);
    }

    void MvcApplication_AuthorizeRequest(object sender, EventArgs e)
    {
        var id = Context.User.Identity as FormsIdentity;
        if (id != null && id.IsAuthenticated)
        {
            var roles = id.Ticket.UserData.Split(',');
            Context.User = new GenericPrincipal(id, roles);
        }
    }
    //...
}

代码下载:Mvc-FormsAuthentication-RolesAuthorization-2.rar (244KB)

3、角色权限

使用任一种方式后,咱们就能够在 Controller 中使用 AuthorizeAttribute 实现基于角色的权限管理了:

[Authorize(Roles = "employee,manager")]
public ActionResult Index1()
{
    return View();
}
[Authorize(Roles = "manager")]
public ActionResult Index2()
{
    return View();
}
[Authorize(Users="admin", Roles = "admin")]
public ActionResult Index3()
{
    return View();
}

4、简要说明

MVC 使用 HttpContext.User 属性进行来进行实现身份验证及角色管理,一样 AuthorizeAttribute 也根据 HttpContext.User 进行角色权限验证。

因些不要在用户登陆后,将相关用户信息保存在 Session 中(网上常常看到这种作法),将用户保存在 Session 中是一种很是很差的作法。

也不要在 Action 中进行角色权限判断,应该使用 AuthorizeAttribute 或它的子类,如下的方式都是错误的:

public ActionResult Action1()
{
    if (Session["User"] == null) { /**/}
    /**/
}
public ActionResult Action2()
{
    if (User.Identity == null) { /**/}
    if (User.Identity.IsAuthenticated == false) { /**/}
    if (User.IsInRole("admin") == false) { /**/}
    /**/
}
相关文章
相关标签/搜索