简体   繁体   English

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

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

I have an existing, working ASP.NET MVC 4 web application. 我有一个现有的,工作的ASP.NET MVC 4 Web应用程序。 I have written my own RoleProvider and I am using the standard [Authorize] attribute . 我编写了自己的RoleProvider ,我使用的是标准[Authorize]属性 My controllers look like this: 我的控制器看起来像这样:

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

I would like to add a WebAPI controller to my application, and take advantage of my existing plumbing 我想在我的应用程序中添加一个WebAPI控制器,并利用我现有的管道

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

This works for Javascript ajax calls from within my site (since the browser user is already authenticated with a Forms auth cookie). 这适用于我的网站内的Javascript ajax调用(因为浏览器用户已经使用Forms身份验证cookie进行了身份验证)。 My question is from a C# Console Application (or any other application that is not part of my web app) how can I authenticate to this API? 我的问题来自C#控制台应用程序(或任何其他不属于我的Web应用程序的应用程序)如何对此API进行身份验证?

Lets assume that for the parts of my API which are public, I am using code very similar to what is found at this question Consuming WebApi in MVC3 . 让我们假设对于我公开的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
}

What would I need to modify here? 我需要在这里修改什么? Or would I have to scrap this and use a completely different approach? 或者我是否必须废弃这个并使用完全不同的方法? I am not tied to using Forms for API Authentication of remote clients, but I would like to keep the current elegant approach for JavaScript clients that are part of the app (just request API since forms cookie is set). 我并不依赖于使用远程客户端的API身份验证表单,但我想保留当前优雅的方法,用于作为应用程序一部分的JavaScript客户端(只是请求API,因为表单cookie已设置)。

You could combine the standard Forms Auth with a custom Basic Auth, based on the same primitives than Forms Auth. 您可以将标准Forms Auth与自定义Basic Auth相结合,基于与Forms Auth相同的原语。 Note with Basic, HTTPS is strongly recommended (and in fact more and more Windows component do not support Basic+HTTP by default nowadays). 注意使用Basic,强烈建议使用HTTPS(事实上,现在越来越多的Windows组件默认不支持Basic + HTTP)。

Here is a sample code for a Basic Authentication Module that reuses code from Forms Auth. 以下是重用Forms Auth代码的基本身份验证模块的示例代码。 It also comes with it's own configuration section (named 'basicAuth'). 它还附带了它自己的配置部分(名为'basicAuth')。 You want to make sure both auths (Forms and Basic) use the same cookie and parameters when configured together: 您希望确保两个身份验证(表单和基本)在配置时使用相同的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; }
    }
}

Once this is installed server side, you can configure the WebClient to use Basic auth: 一旦安装在服务器端,您可以配置WebClient以使用Basic auth:

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

There are numerous ways to share the cookie with the console application. 有许多方法可以与控制台应用程序共享cookie。 Take a look at some ideas here: 看看这里的一些想法:

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

Another simple option would be to expose a web method which doesn't require any authentication, gets the username and password and returns the cookie to the client. 另一个简单的选择是公开一个不需要任何身份验证的Web方法,获取用户名和密码并将cookie返回给客户端。

No matter what approach you take, your goal is to somehow get the forms cookie at the console application side. 无论采用何种方法,您的目标都是以某种方式在控制台应用程序端获取表单cookie。 From there you are easily done as all you do is you attach the cookie to your requests. 从那里你很容易完成,因为你所做的就是将cookie附加到你的请求中。 The web api will accept the cookie happily. web api将愉快地接受cookie。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何从控制台应用程序在 ASP.NET MVC 应用程序中进行身份验证 - How to authenticate in an ASP.NET MVC application from a console application 如何将对象从C#WebApp发送到ASP.NET WebAPI - how can I send object from c# webapp to asp.net webapi 通过控制台应用程序在ASP.Net Forms授权网站上进行身份验证 - Authenticate on an ASP.Net Forms Authorization website from a console app 如何使用C#登录ASP.Net Forms Authenticated站点? - How can I login to ASP.Net Forms Authenticated site using C#? 如何在同一个ASP.NET Web窗体应用程序中使用c#和vb.net代码? - How can I have c# and vb.net code behinds in the same ASP.NET Web Forms application? ASP.NET 5中的C#控制台应用程序 - C# Console Application in ASP.NET 5 使用表单身份验证和ASP.NET时如何通过链接自动对用户进行身份验证? - How to auto-authenticate users via a link when using Forms Authentication and ASP.NET? 如何在ASP.Net中延长表单身份验证时间? - How can I extend the Forms Authentication time in ASP.Net? 如何从Asp.NET Web应用程序授予对C#控制台应用程序的权限? - How to give permission to a C# Console application from Asp.NET web application? 我如何在c#asp.net应用程序中使用jquery实现文本框自动建议? - How can i implement textbox auto suggest using jquery in my c# asp.net application?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM