ASP.net MVC中的自定义表单身份验证/授权方案

8
我正在尝试使用表单身份验证在ASP.NET MVC中创建自定义身份验证方案。我的想法是我可能会有不同的站点区域需要管理,如审核员区域和普通用户区域,它们将使用不同的登录页面等。所以这就是我想要发生的事情。
以下是我希望实现的步骤:
1. 用户访问受限页面(目前我已经使用自定义AuthorizeAttribute进行保护)。 2. 用户被重定向到特定的登录页面(而不是Web.config中的页面)。 3. 验证用户凭据(通过自定义数据库方案)并登录。
非常感谢您的帮助!
这是我目前的代码,但它无法正常工作:
 public class AdministratorAccountController : Controller
{
    public ActionResult Login()
    {
        return View("Login");
    }

    [HttpPost]
    public ActionResult Login(AdministratorAccountModels.LoginModel model, string returnUrl)
    {
        if (ModelState.IsValid)
            if (model.UserName == "admin" && model.Password == "pass") // This will be pulled from DB etc
            {
                var ticket = new FormsAuthenticationTicket(1,               // version 
                                                           model.UserName,  // user name
                                                           DateTime.Now,    // create time
                                                           DateTime.Now.AddSeconds(30), // expire time
                                                           false,           // persistent
                                                           "");             // user data

                var strEncryptedTicket = FormsAuthentication.Encrypt(ticket);
                var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, strEncryptedTicket);
                Response.Cookies.Add(cookie);

                if (!String.IsNullOrEmpty(returnUrl))
                {
                    return Redirect(returnUrl);
                }
                else
                {
                    return RedirectToAction("Index", "Home");
                }
            }
            else
            {
                ModelState.AddModelError("", "The user name or password provided is incorrect.");
            }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

    [AdministratorAuthorize]
    public ActionResult MainMenu()
    {
        return View();
    }

    public class AdministratorAuthorizeAttribute : AuthorizeAttribute
    {
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            var authenCookie = httpContext.Request.Cookies.Get(FormsAuthentication.FormsCookieName);
            if (authenCookie == null) return false;

            var ticket = FormsAuthentication.Decrypt(authenCookie.Value);
            var id = new FormsIdentity(ticket);
            var astrRoles = ticket.UserData.Split(new[] { ',' });
            var principal = new GenericPrincipal(id, astrRoles);
            httpContext.User = principal;
            return true;
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            var model = new AdministratorAccountModels.LoginModel();
            var viewData = new ViewDataDictionary(model);

            filterContext.Result = new ViewResult { ViewName = "Login", ViewData = viewData };

        }
    }
}
4个回答

15

我结合了minus4提供的代码和我自己的代码来创建了这个简化的场景,希望能帮助其他人。我添加了一些关于一开始困扰我的事情的注释。

 public class AdministratorAccountController : Controller
{
    public ActionResult Login()
    {
        return View("Login");
    }

    [HttpPost]
    public ActionResult Login(AdministratorAccountModels.LoginModel model, string returnUrl)
    {
        if (ModelState.IsValid)
            // Here you would call a service to process your authentication
            if (model.UserName == "admin" && model.Password == "pass")
            {
                // * !!! *
                // Creating a FromsAuthenticationTicket is what 
                // will set RequestContext.HttpContext.Request.IsAuthenticated to True
                // in the AdminAuthorize attribute code below
                // * !!! *
                var ticket = new FormsAuthenticationTicket(1, // version 
                                                           model.UserName, // user name
                                                           DateTime.Now, // create time
                                                           DateTime.Now.AddSeconds(30), // expire time
                                                           false, // persistent
                                                           ""); // user data, such as roles

                var strEncryptedTicket = FormsAuthentication.Encrypt(ticket);
                var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, strEncryptedTicket);
                Response.Cookies.Add(cookie);

                // Redirect back to the page you were trying to access
                if (!String.IsNullOrEmpty(returnUrl))
                {
                    return Redirect(returnUrl);
                }
                else
                {
                    return RedirectToAction("Index", "Home");
                }
            }
            else
            {
                ModelState.AddModelError("", "The user name or password provided is incorrect.");
            }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

    [AdminAuthorize]
    public ActionResult MainMenu()
    {
        return View();
    }

    public class AdminAuthorize : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (!filterContext.RequestContext.HttpContext.Request.IsAuthenticated)
            {
                // Redirect to the needed login page
                // This can be pulled from config file or anything else
                filterContext.HttpContext.Response.Redirect("/AdministratorAccount/Login?ReturnUrl=" 
                                        + HttpUtility.UrlEncode(filterContext.HttpContext.Request.RawUrl));               
            }

            base.OnActionExecuting(filterContext);
        }
    }
}

1
表单认证代码非常棒。但需要注意的是,我认为最佳实践是只有在重定向URL是本地的情况下才重定向。这可以减轻重定向攻击的可能性。请参见:http://www.asp.net/mvc/tutorials/security/preventing-open-redirection-attacks - James Hulse

3

好的,这里是你需要的内容:代码

在这个文件中,你会发现ActionFilters文件夹(AuthAccess.cs)、Plugins文件夹(security.cs(加密/解密cookie)、SessionHandler.cs(所有与登录相关的事项))、Controllers文件夹(BaseController.cs和exampleController(展示如何使用))以及loginTable SQL文件。

我使用mysql,所以你可能需要进行修改。同时我使用subsonic,因此我的模型将来自空的models文件夹。

非常简单易用,我会让它保留一段时间供你使用,祝使用愉快!

抱歉,cookie模型在这里:

using System;

namespace TestApp.Models
{
    public class CookieModel
{
    public string CurrentGuid { get; set; }
    public DateTime LoginTime { get; set; }
    public Int32 UserLevel { get; set; }
    public Int32 LoginID { get; set; }
    public bool isValidLogin { get; set; }
    public string realUserName { get; set; }
    public string emailAddress { get; set; }
}
}

非常感谢,这段代码很有帮助。我进行了一些简化以适应原始问题。 - Greg R
这很酷,意味着一个良好开端的基础,很高兴能够帮助。 - davethecoder

2

1
不完全准确,如果您在网站中有完全分开的登录区域,这些区域不应混合在一起,并且每个区域需要不同的登录,则无法使用角色。例如,如果您在网站上有管理员选项(不应对任何访问者可见),以及用于使用网站的客户帐户。当您未登录即进入安全页面时,它应该根据用户正在访问的区域将用户引导到不同的登录屏幕。 - Greg R

0

我之前处理过这个问题,我有一个用于登录的类。

例程包括登录、读取cookie、检查cookie,它们都有一个包含以下内容的模型:

姓名、电子邮件、ID、用户级别

然后你只需要自己的自定义actionFilter

例如 [CustomAuth(MinAllowedLevel=10)]

我为所有我的控制器使用一个基类,这样我就可以更轻松地链接到所有我的会话内容,并且可以像这样获取信息。

var model = pictures.all().where(x => x.userid == users.ReadCookie.userID)

如果你需要,我明天会在英国白天回来后为你整理代码。

大约10小时后,我会提供给你所有的课程内容和自定义操作过滤器,然后你只需要有一个带有用户级别字段的登录表,最好是10、20、30、40级别,以防你需要1到2之间的级别。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接