c# - 如何从 C# 控制台应用程序向使用表单例份验证的 ASP.NET WebAPI 进行身份验证?

标签 c# asp.net security authentication asp.net-web-api

我有一个现有的、可用的 ASP.NET MVC 4 Web 应用程序。我已经编写了自己的 RoleProvider 并且我使用的是标准 [Authorize] attribute .我的 Controller 看起来像这样:

[Authorize(Roles="ContactAdmins")] //System.Web.Mvc
public ActionResult Index()

我想向我的应用程序添加一个 WebAPI Controller ,并利用我现有的管道

[Authorize(Roles="ContactAdmins")] //System.Web.Http
public IEnumerable<Contact> Get()

这适用于我站点内的 Javascript ajax 调用(因为浏览器用户已经使用 Forms auth cookie 进行了身份验证)。我的问题来自 C# 控制台应用程序(或不属于我的 Web 应用程序的任何其他应用程序),我如何才能对此 API 进行身份验证?

假设对于我的 API 的公共(public)部分,我使用的代码与在这个问题 Consuming WebApi in MVC3 中找到的代码非常相似.

var url = "http://localhost:9000/api/contacts";
using (var client = new WebClient())
using (var reader = XmlReader.Create(client.OpenRead(url)))
{
    var serializer = new XmlSerializer(typeof(Contact[]));
    var contacts = (Contact[])serializer.Deserialize(reader);
    // TODO: Do something with the contacts
}

我需要在这里修改什么?还是我必须放弃它并使用完全不同的方法?我不依赖于使用表单对远程客户端进行 API 身份验证,但我想为作为应用程序一部分的 JavaScript 客户端保留当前优雅的方法(因为设置了表单 cookie,所以只请求 API)。

最佳答案

您可以基于与 Forms Auth 相同的原语,将标准 Forms Auth 与自定义 Basic Auth 结合起来。请注意,对于 Basic,强烈建议使用 HTTPS(事实上,现在越来越多的 Windows 组件默认不支持 Basic+HTTP)。

以下是重用 Forms Auth 代码的基本身份验证模块的示例代码。它还带有自己的配置部分(名为“basicAuth”)。您希望确保两个身份验证(Forms 和 Basic)在一起配置时使用相同的 cookie 和参数:

using System;
using System.ComponentModel;
using System.Configuration;
using System.Globalization;
using System.Net;
using System.Security.Principal;
using System.Text;
using System.Web;
using System.Web.Configuration;
using System.Web.Security;

namespace MySecurity
{
    public class BasicAuthenticationModule : IHttpModule
    {
        public event EventHandler<BasicAuthenticationEventArgs> Authenticate;

        public void Dispose()
        {
        }

        protected virtual string GetRealm(HttpContext context)
        {
            return BasicAuthenticationSection.Current.GetRealm(context);
        }

        public virtual void Init(HttpApplication context)
        {
            context.AuthenticateRequest += OnAuthenticateRequest;
            context.EndRequest += OnEndRequest;
        }

        protected virtual bool FormsAuthenticate(HttpContext context, string login, string password, string realm)
        {
            // check ad-hoc forms credentials, as we can support it even if forms auth is not configured
            FormsAuthenticationConfiguration c = ((AuthenticationSection)ConfigurationManager.GetSection("system.web/authentication")).Forms;
            if ((c.Credentials == null) || (c.Credentials.Users == null))
                return false;

            foreach (FormsAuthenticationUser user in c.Credentials.Users)
            {
                if ((string.Compare(user.Name, login, true, CultureInfo.CurrentCulture) == 0) &&
                    (string.Compare(user.Password, password, true, CultureInfo.CurrentCulture) == 0))
                    return true;
            }
            return false;
        }

        protected virtual bool OnAuthenticate(HttpContext context, string login, string password, string realm)
        {
            EventHandler<BasicAuthenticationEventArgs> handler = Authenticate;
            if (handler != null)
            {
                BasicAuthenticationEventArgs e = new BasicAuthenticationEventArgs(context, login, password, realm);
                handler(this, e);
                return !e.Cancel;
            }
            return FormsAuthenticate(context, login, password, realm);
        }

        protected virtual string[] GetUserRoles(HttpContext context, string login, string realm)
        {
            // TODO: overwrite if needed
            return new string[0];
        }

        protected virtual IPrincipal GetUser(HttpContext context, FormsAuthenticationTicket ticket)
        {
            return new GenericPrincipal(new BasicAuthenticationIdentity(ticket), GetUserRoles(context, ticket.Name, GetRealm(context)));
        }

        protected virtual void OnAuthenticated(HttpContext context)
        {
        }

        protected virtual void OnEndRequest(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication)sender;
            if (application.Response.StatusCode != (int)HttpStatusCode.Unauthorized)
                return;

            string basic = "Basic Realm=\"" + GetRealm(application.Context) + "\"";
            application.Response.AppendHeader("WWW-Authenticate", basic);
        }

        public static void SignOut()
        {
            if (HttpContext.Current == null)
                return;

            HttpContext.Current.Request.Cookies.Remove(BasicAuthenticationSection.Current.Name);
            HttpContext.Current.Response.Cookies.Remove(BasicAuthenticationSection.Current.Name);
            HttpCookie cookie = new HttpCookie(BasicAuthenticationSection.Current.Name);
            cookie.Expires = DateTime.Now.AddDays(-1);
            HttpContext.Current.Response.Cookies.Add(cookie);
        }

        public static bool IsAuthenticated(HttpContext context)
        {
            if ((context == null) || (context.User == null) || (context.User.Identity == null))
                return false;

            return context.User.Identity.IsAuthenticated;
        }

        protected virtual void OnAuthenticateRequest(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication)sender;
            if ((IsAuthenticated(application.Context)) && (!BasicAuthenticationSection.Current.ReAuthenticate))
                return;

            string encryptedTicket;
            FormsAuthenticationTicket ticket;
            HttpCookie cookie = application.Context.Request.Cookies[BasicAuthenticationSection.Current.Name];
            if (cookie == null)
            {
                // no cookie, check auth header
                string authHeader = application.Context.Request.Headers["Authorization"];
                if ((string.IsNullOrEmpty(authHeader)) || (!authHeader.StartsWith("Basic ", StringComparison.InvariantCultureIgnoreCase)))
                {
                    ResponseAccessDenied(application);
                    return;
                }

                string login;
                string password;
                string lp = authHeader.Substring(6).Trim();
                if (string.IsNullOrEmpty(lp))
                {
                    ResponseAccessDenied(application);
                    return;
                }

                lp = Encoding.Default.GetString(Convert.FromBase64String(lp));
                if (string.IsNullOrEmpty(lp.Trim()))
                {
                    ResponseAccessDenied(application);
                    return;
                }

                int pos = lp.IndexOf(':');
                if (pos < 0)
                {
                    login = lp;
                    password = string.Empty;
                }
                else
                {
                    login = lp.Substring(0, pos).Trim();
                    password = lp.Substring(pos + 1).Trim();
                }

                if (!OnAuthenticate(application.Context, login, password, GetRealm(application.Context)))
                {
                    ResponseAccessDenied(application);
                    return;
                }

                // send cookie back to client
                ticket = new FormsAuthenticationTicket(login, false, (int)BasicAuthenticationSection.Current.Timeout.TotalMinutes);
                encryptedTicket = FormsAuthentication.Encrypt(ticket);
                cookie = new HttpCookie(BasicAuthenticationSection.Current.Name, encryptedTicket);
                application.Context.Response.Cookies.Add(cookie);

                // don't overwrite context user if it's been set
                if ((!IsAuthenticated(application.Context)) || (BasicAuthenticationSection.Current.ReAuthenticate))
                {
                    application.Context.User = GetUser(application.Context, ticket);
                }
                OnAuthenticated(application.Context);
                application.Context.Response.StatusCode = (int)HttpStatusCode.OK;
                return;
            }

            // there is a cookie, check it
            encryptedTicket = cookie.Value;
            if (string.IsNullOrEmpty(encryptedTicket))
            {
                ResponseAccessDenied(application);
                return;
            }

            try
            {
                ticket = FormsAuthentication.Decrypt(encryptedTicket);
            }
            catch
            {
                ResponseAccessDenied(application);
                return;
            }

            if (ticket.Expired)
            {
                ResponseAccessDenied(application);
                return;
            }

            // set context user
            // don't overwrite context user if it's been set
            if ((!IsAuthenticated(application.Context) || (BasicAuthenticationSection.Current.ReAuthenticate)))
            {
                application.Context.User = GetUser(application.Context, ticket);
            }
            OnAuthenticated(application.Context);
        }

        protected virtual void WriteAccessDenied(HttpApplication application)
        {
            if (application == null)
                throw new ArgumentNullException("application");

            application.Context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            application.Context.Response.StatusDescription = "Unauthorized";
            application.Context.Response.Write(application.Context.Response.StatusCode + " " + application.Context.Response.StatusDescription);
        }

        protected virtual void ResponseAccessDenied(HttpApplication application)
        {
            // if there is a bad cookie, kill it
            application.Context.Request.Cookies.Remove(BasicAuthenticationSection.Current.Name);
            application.Context.Response.Cookies.Remove(BasicAuthenticationSection.Current.Name);
            HttpCookie cookie = new HttpCookie(BasicAuthenticationSection.Current.Name);
            cookie.Expires = DateTime.Now.AddDays(-1);
            HttpContext.Current.Response.Cookies.Add(cookie);
            WriteAccessDenied(application);
            application.CompleteRequest();
        }
    }

    public class BasicAuthenticationSection : ConfigurationSection
    {
        public const string SectionName = "basicAuth";
        private const string DefaultCookieName = "." + SectionName;
        private static BasicAuthenticationSection _current;

        public static BasicAuthenticationSection Current
        {
            get
            {
                return _current ?? (_current = ConfigurationManager.GetSection(SectionName) as BasicAuthenticationSection ?? new BasicAuthenticationSection());
            }
        }

        [StringValidator(MinLength = 1), ConfigurationProperty("name", DefaultValue = DefaultCookieName)]
        public string Name
        {
            get
            {
                return (string)base["name"];
            }
        }

        internal string GetRealm(HttpContext context)
        {
            if (!string.IsNullOrEmpty(Realm))
                return Realm;

            return context.Request.Url.Host;
        }

        [ConfigurationProperty("realm", DefaultValue = "")]
        public string Realm
        {
            get
            {
                return (string)base["realm"];
            }
        }

        [ConfigurationProperty("domain", DefaultValue = "")]
        public string Domain
        {
            get
            {
                return (string)base["domain"];
            }
        }

        [ConfigurationProperty("reAuthenticate", DefaultValue = false)]
        public bool ReAuthenticate
        {
            get
            {
                return (bool)base["reAuthenticate"];
            }
        }

        [TypeConverter(typeof(TimeSpanMinutesConverter)), ConfigurationProperty("timeout", DefaultValue = "30"), PositiveTimeSpanValidator]
        public TimeSpan Timeout
        {
            get
            {
                return (TimeSpan)base["timeout"];
            }
        }
    }

    public class BasicAuthenticationIdentity : IIdentity
    {
        public BasicAuthenticationIdentity(FormsAuthenticationTicket ticket)
        {
            if (ticket == null)
                throw new ArgumentNullException("ticket");

            Ticket = ticket;
        }

        public FormsAuthenticationTicket Ticket;

        public string AuthenticationType
        {
            get
            {
                return BasicAuthenticationSection.SectionName;
            }
        }

        public bool IsAuthenticated
        {
            get
            {
                return true;
            }
        }

        public string Name
        {
            get
            {
                return Ticket.Name;
            }
        }
    }

    public class BasicAuthenticationEventArgs : CancelEventArgs
    {
        public BasicAuthenticationEventArgs(HttpContext context, string login, string password, string realm)
        {
            if (context == null)
                throw new ArgumentNullException("context");

            Context = context;
            Login = login;
            Password = password;
            Realm = realm;
        }

        public HttpContext Context { get; private set; }
        public string Realm { get; private set; }
        public string Login { get; private set; }
        public string Password { get; private set; }
        public IPrincipal User { get; set; }
    }
}

在服务器端安装完成后,您可以配置 WebClient 以使用基本身份验证:

WebClient client = new WebClient();
client.Credentials =  new NetworkCredential("username", "password");

关于c# - 如何从 C# 控制台应用程序向使用表单例份验证的 ASP.NET WebAPI 进行身份验证?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14760989/

相关文章:

c# - 使用 ASP.NET 和 AngularJS 的简单主页

asp.net - DropDownList SelectedIndex 值未在 AutoPostback 上更新

security - XPS 文档的安全性如何?

python - Python 中的 header 检查 (GAE)

security - Wildfly Web 应用 SSO(单点登录)/书籍推荐

c# - 从后面的代码中隐藏 ASPxPopupControl

c# - 如何在gridview的页脚绑定(bind)数据

c# - 如何获取标签的位置?

c# - session 在 IHttpModule 中不可用

c# - Skype API 的实现