【问题标题】:ASP.NET MVC 4 Web API Authentication with Membership Provider使用成员资格提供程序的 ASP.NET MVC 4 Web API 身份验证
【发布时间】:2012-07-17 02:48:10
【问题描述】:

我有一个使用 Web API 的 ASP.NET MVC 4 项目。在控制器上,我已使用 [Authorize] 属性将类设置为需要授权。对于身份验证,我使用 ASP.NET Membership Provider 并将我的 Web.Config 设置为使用“表单”身份验证。这是我卡住的地方:

在我完成 API 测试之前,一切都运行良好,我想使用 [Authorize] 属性保护控制器,这样我就可以开始在我的 Membership Provider 中测试针对用户的身份验证。所以我启动 Fiddler 并进行相同的调用,添加 Authorization:Basic 属性以及来自我的会员提供者的用户名:密码,如下所示:

我得到的响应是 401 未授权,在“Auth”下我得到“没有 WWW-Authenticate Header is present”。然后我意识到 API 正在寻找一个 SHA1 编码的密钥。所以我从搜索中启动了一个 SHA1 生成器,并为我的 username:password 获取了一个哈希,并像这样更新我的请求标头:

这也不起作用,我得到相同的结果。此外,我显然需要某种“共享密​​钥”来与服务器一起使用来解码我的用户名/密码。

所以我的问题:

  1. 如何从服务器获取此密钥(或者在这种情况下,虚拟 IIS 在 VS 2012 上运行)。
  2. 如何使用它在 Fiddler 中使用来自 ASP.NET Membership Provider 的用户名/密码进行经过身份验证的调用。
  3. 我将如何在我的客户端应用程序中使用它来进行相同的调用(C# WPF 应用程序)。
  4. 在我的 HTTP 调用中与 SSL 结合使用时,这是否最实用?如果不是,那是什么?

提前致谢!

【问题讨论】:

    标签: asp.net-mvc asp.net-membership forms-authentication asp.net-web-api fiddler


    【解决方案1】:

    您可以将basic authentication 与 SSL 一起使用。在服务器端,我们可以编写一个自定义委托处理程序,它将通过查询我们注册的成员资格提供程序来验证凭据,如果有效,则检索角色并设置当前主体:

    public class BasicAuthenticationMessageHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var authHeader = request.Headers.Authorization;
    
            if (authHeader == null)
            {
                return base.SendAsync(request, cancellationToken);
            }
    
            if (authHeader.Scheme != "Basic")
            {
                return base.SendAsync(request, cancellationToken);
            }
    
            var encodedUserPass = authHeader.Parameter.Trim();
            var userPass = Encoding.ASCII.GetString(Convert.FromBase64String(encodedUserPass));
            var parts = userPass.Split(":".ToCharArray());
            var username = parts[0];
            var password = parts[1];
    
            if (!Membership.ValidateUser(username, password))
            {
                return base.SendAsync(request, cancellationToken);
            }
    
            var identity = new GenericIdentity(username, "Basic");
            string[] roles = Roles.Provider.GetRolesForUser(username);
            var principal = new GenericPrincipal(identity, roles);
            Thread.CurrentPrincipal = principal;
            if (HttpContext.Current != null)
            {
                HttpContext.Current.User = principal;
            }
    
            return base.SendAsync(request, cancellationToken);
        }
    }
    

    然后我们在Application_Start注册这个处理程序:

    GlobalConfiguration.Configuration.MessageHandlers.Add(
        new BasicAuthenticationMessageHandler()
    );
    

    现在我们可以有一个 Api 控制器,该控制器将使用 [Authorize] 属性进行修饰,以确保只有经过身份验证的用户才能访问其操作:

    [Authorize]
    public class ValuesController : ApiController
    {
        public string Get()
        {
            return string.Format("Hello {0}", User.Identity.Name);
        }
    }
    

    好的,现在让我们看一个示例客户端:

    using System;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Text;
    
    class Program
    {
        static void Main()
        {
            // since for testing purposes I am using IIS Express
            // with an invalid SSL certificate I need to desactivate
            // the check for this certificate.
            ServicePointManager.ServerCertificateValidationCallback += 
                (sender, certificate, chain, sslPolicyErrors) => true;
    
            using (var client = new HttpClient())
            {
                var buffer = Encoding.ASCII.GetBytes("john:secret");
                var authHeader = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(buffer));
                client.DefaultRequestHeaders.Authorization = authHeader;
                var task = client.GetAsync("https://localhost:44300/api/values");
                if (task.Result.StatusCode == HttpStatusCode.Unauthorized)
                {
                    Console.WriteLine("wrong credentials");
                }
                else
                {
                    task.Result.EnsureSuccessStatusCode();
                    Console.WriteLine(task.Result.Content.ReadAsAsync<string>().Result);
                }
            }
        }
    }
    

    【讨论】:

    • 谢谢达林,这正是我所需要的!
    • User.Identity.Name 在控制器方法中对我来说是空的,User.Identity.IsAuthenticatedfalse。我可以在调试器中看到 Thread.CurrentPrincipal 在消息处理程序中设置为具有正确用户名和角色的主体。还尊重[Authorization(Roles=...)] 属性:如果我使用角色错误的用户,我会收到未经授权的响应并且无法到达控制器方法。但是User.Identity 没有我在消息处理程序中设置的值。您知道可能出了什么问题吗?
    • @Slauma,我无法重现这一点。也许您可以开始一个新线程,逐步解释如何重现问题(当然从一个空项目开始)。
    • 我自己无法在一个新项目中重现它。但同时我发现 User 主体在 ApiController 构造函数中是正确的,但在控制器方法中是错误的。介于两者之间的是自定义 MediaTypeFormatter。通过设置一些断点,我看到调用链是:ApiController 构造函数->MediaTypeFormatter->ApiController 方法。尽管我在那里没有使用任何与安全相关的东西,但在格式化程序中,主体会以某种方式被覆盖。如果我注释掉 global.asax 中的格式化程序注册,则主体保持正确。我需要继续调试...
    • 嗨,方法:“Membership.ValidateUser()”返回 false。我假设我需要设置会员默认数据库连接?如果是这样,我在哪里做?
    猜你喜欢
    • 2012-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-15
    • 2011-07-25
    • 2011-03-27
    • 2011-02-06
    • 1970-01-01
    相关资源
    最近更新 更多