【问题标题】:asp mvc 3 ActionFilter for basic authentication用于基本身份验证的 asp mvc 3 ActionFilter
【发布时间】:2014-12-28 22:26:07
【问题描述】:

我有一个使用基本身份验证的 ASP MVC3 restful 服务。搜索堆栈溢出后,我创建了以下代码。

public class BasicAuthentication : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var req = filterContext.HttpContext.Request;
        if (String.IsNullOrEmpty(req.Headers["Authorization"]))
        {
            filterContext.Result = new HttpNotFoundResult();
        }
        else
        {
            var credentials = System.Text.ASCIIEncoding.ASCII
                        .GetString(Convert.FromBase64String(req.Headers["Authorization"].Substring(6)))
                        .Split(':');
            var user = new { Name = credentials[0], Password = credentials[1] };
            if(!(user.Name == "username" && user.Password == "passwords"))
            {
                filterContext.Result = new HttpNotFoundResult();
            }
        }
    }
}

1) ActionFilterAttribute 是最好的方法吗?

2) 设置 filterContext.Result 是拒绝访问控制器方法的正确方法吗?

3) 我做错了什么吗?

谢谢。

-尼克

【问题讨论】:

  • 您是使用基本的表单/自定义类型身份验证和基本(两阶段身份验证)还是仍在使用域帐户?

标签: asp.net-mvc-3 basic-authentication


【解决方案1】:

1) ActionFilterAttribute 是最好的方法吗?
我认同。这种方法反映了内置 Authorize 属性的实现。

2) 设置filterContext.Result 是拒绝访问控制器方法的正确方法吗?
是的。这就是它的用途。 (1)

3) 我做错了什么吗?

  • 您假设 Authorization 标头的内容在 格式正确且编码正确。
  • 您假设请求用于基本身份验证,而不是任何 其他身份验证方案。
  • 我更喜欢使用HttpUnauthorizedResult() 发送http 401 错误,而不是通过 HttpNotFoundResult() 的 http 404 错误。

在我的代码实现下面(我敢肯定它也有问题)。

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        try
        {
            if (String.IsNullOrEmpty(filterContext.HttpContext.Request.Headers["Authorization"]))
            {
                filterContext.Result = new HttpUnauthorizedResult();
            }
            else
            {
                if (filterContext.HttpContext.Request.Headers["Authorization"].StartsWith("Basic ", StringComparison.InvariantCultureIgnoreCase))
                {
                    string[] credentials = ASCIIEncoding.ASCII.GetString(Convert.FromBase64String(filterContext.HttpContext.Request.Headers["Authorization"].Substring(6))).Split(':');

                    if (credentials.Length == 2)
                    {
                        if (String.IsNullOrEmpty(credentials[0]))
                        {
                            filterContext.Result = new HttpUnauthorizedResult();
                        }
                        else if (!(credentials[0] == "username" && credentials[1] == "passwords"))
                        {
                            filterContext.Result = new HttpUnauthorizedResult();
                        }
                    }
                    else
                    {
                        filterContext.Result = new HttpUnauthorizedResult();
                    }
                }
                else
                {
                    filterContext.Result = new HttpUnauthorizedResult();
                }
            }

            base.OnActionExecuting(filterContext);
        }
        catch
        {
            filterContext.Result = new HttpUnauthorizedResult();
        }
    }

备注

  • 我没有对用户名和密码进行非法字符检查。
  • 我无法确定如何实现异常处理,所以我很简单。

参考文献

(1)http://msdn.microsoft.com/en-us/magazine/gg232768.aspx

【讨论】:

    【解决方案2】:

    Adrian 的重构版本

    public class BasicAuthenticationAttribute : ActionFilterAttribute
    {
        private static readonly string AuthorizationHeader = "Authorization";
        private static readonly string BasicHeader = "Basic ";
        private static readonly string Username = "username";
        private static readonly string Password = "password";
        private static readonly char[] Separator = ":".ToCharArray();
    
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            try
            {
                if (!Authenticated(filterContext.HttpContext.Request))
                    filterContext.Result = new HttpUnauthorizedResult();
    
                base.OnActionExecuting(filterContext);
            }
            catch
            {
                filterContext.Result = new HttpUnauthorizedResult();
            }
        }
    
        private bool Authenticated(HttpRequestBase httpRequestBase)
        {
            bool authenticated = false;
    
            if (String.IsNullOrEmpty(httpRequestBase.Headers[AuthorizationHeader]) == false &&
                httpRequestBase.Headers[AuthorizationHeader].StartsWith(BasicHeader, StringComparison.InvariantCultureIgnoreCase))
            {
                string[] credentials = Encoding.ASCII.GetString(Convert.FromBase64String(
                    httpRequestBase.Headers[AuthorizationHeader].Substring(BasicHeader.Length))).Split(Separator);
    
                if (credentials.Length == 2 && credentials[0] == Username && credentials[1] == Password)
                {
                    authenticated = true;
                }
            }
    
            return authenticated;
        }
    }
    

    【讨论】:

    • +1 时间和精力。很有用。但是,如果第一个条件是“未”通过身份验证,则引发 HttpUnauthorisedResult。
    【解决方案3】:
    【解决方案4】:

    1) 不,ActionFilter 属性不是验证用户的好方法。 (因为我们需要验证一次并设置验证cookie,所以HttpContext.User将保持验证直到cookie过期)

    2) 是的,设置 filtercontext.Result 是防止访问的理想方法。 (但不是分配 HttpNotFoundResult,而是使用 RedirectResult 重定向到登录页面)

    3) 我真的不明白为什么要有这样的授权实现。 最好的方法是有一个动作来接收表单发布的数据(用户名和密码)。并使用 Authorize 属性来防止未经授权的访问。

    以下是 VS2010 中默认 MVC3 示例应用程序的代码。

        [HttpPost]
        public ActionResult LogOn(LogOnModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                if (Membership.ValidateUser(model.UserName, model.Password))
                {
                    FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
                    if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                        && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
                    {
                        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);
        }
    

    【讨论】:

    • 我了解表单登录模型,并将其用于普通网站。这对宁静的服务有何作用?用户如何提供登录信息?
    • MVC 使用表单身份验证仍然很安静。您是否正在寻找一些系统/服务将调用 mvc uri,传递网络凭据。如果是这样,上述方法似乎很好。(但更好的是你可以使用 BaseController 并从中继承所有子控制器。然后你可以在 OnActionExecuting 方法中编写与上面相同的代码,除非不是所有代码都由 API 调用)
    • 我认为您在回答时忽略了问题标题中的“服务”和“基本身份验证”这两个词。
    • 答案是无关紧要的,部分原因也无关紧要。无论使用 WebAPI 还是 MVC,过滤器都是验证和自动化请求的首选方式。重定向到登录页面不是身份验证过程的问题,如果需要,这将由 ASP.Net 或其他层完成。这个答案不正确的另一个问题是表单身份验证使用cookie,而API通常使用授权标头,通常是不记名令牌,但还有许多其他方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-12
    • 2018-04-19
    • 2021-01-25
    • 1970-01-01
    • 2012-07-26
    • 2016-03-07
    • 2021-11-13
    相关资源
    最近更新 更多