簡體   English   中英

如何對從C#控制台應用程序使用表單身份驗證的ASP.NET WebAPI進行身份驗證?

[英]How can I authenticate to an ASP.NET WebAPI that is using Forms Authentication From a C# Console Application?

我有一個現有的,工作的ASP.NET MVC 4 Web應用程序。 我編寫了自己的RoleProvider ,我使用的是標准[Authorize]屬性 我的控制器看起來像這樣:

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

我想在我的應用程序中添加一個WebAPI控制器,並利用我現有的管道

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

這適用於我的網站內的Javascript ajax調用(因為瀏覽器用戶已經使用Forms身份驗證cookie進行了身份驗證)。 我的問題來自C#控制台應用程序(或任何其他不屬於我的Web應用程序的應用程序)如何對此API進行身份驗證?

讓我們假設對於我公開的API的部分,我使用的代碼非常類似於在MVC3中使用WebApi這個問題。

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客戶端(只是請求API,因為表單cookie已設置)。

您可以將標准Forms Auth與自定義Basic Auth相結合,基於與Forms Auth相同的原語。 注意使用Basic,強烈建議使用HTTPS(事實上,現在越來越多的Windows組件默認不支持Basic + HTTP)。

以下是重用Forms Auth代碼的基本身份驗證模塊的示例代碼。 它還附帶了它自己的配置部分(名為'basicAuth')。 您希望確保兩個身份驗證(表單和基本)在配置時使用相同的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以使用Basic auth:

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

有許多方法可以與控制台應用程序共享cookie。 看看這里的一些想法:

http://netpl.blogspot.com/2008/02/clickonce-webservice-and-shared-forms.html

另一個簡單的選擇是公開一個不需要任何身份驗證的Web方法,獲取用戶名和密碼並將cookie返回給客戶端。

無論采用何種方法,您的目標都是以某種方式在控制台應用程序端獲取表單cookie。 從那里你很容易完成,因為你所做的就是將cookie附加到你的請求中。 web api將愉快地接受cookie。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM