【问题标题】:NancyFx and Windows AuthenticationNancyFx 和 Windows 身份验证
【发布时间】:2012-11-16 19:36:22
【问题描述】:

我想将 NancyFx 用于 Intranet Web 应用程序。所有文档和论坛只提到表单和基本身份验证。有人成功使用 Nancy 和 Windows 身份验证吗?

还有一个叫做 Nancy.Authentication.Stateless 的东西,但我看不到它的作用(看起来它是用于 APIs 的)。

【问题讨论】:

    标签: asp.net .net windows-authentication nancy


    【解决方案1】:

    Nancy 与 WindowsAuthentication 一起使用由 this thread 讨论。 Damian Hickey 提供了一个example of using Nancy, hosted by OWin with WindowsAuthentication

    我稍微修改了代码(以删除现在已弃用的NancyOwinHost):

    namespace ConsoleApplication1
    {
        using System;
        using System.Net;
        using System.Security.Principal;
        using Microsoft.Owin.Hosting;
        using Nancy;
        using Nancy.Owin;
        using Owin;
    
        internal static class Program
        {
            private static void Main(string[] args)
            {
                using (WebApp.Start<Startup>("http://localhost:9000"))
                {
                    Console.WriteLine("Press any key to quit.");
                    Console.ReadKey();
                }
            }
        }
    
        internal sealed class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                var listener = (HttpListener) app.Properties["System.Net.HttpListener"];
                listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
    
                app.UseNancy();
            }
        }
    
        public sealed class MyModule : NancyModule
        {
            public MyModule()
            {
                Get[""] = _ =>
                {
                    var env = this.Context.GetOwinEnvironment();
                    var user = (IPrincipal) env["server.User"];
    
                    return "Hello " + user.Identity.Name;
                };
            }
        }
    }
    

    特别感谢 Damian!


    该示例需要以下 NuGet 包:

    • Microsoft.Owin.Host.HttpListener
    • Microsoft.Owin.Hosting
    • Microsoft.Owin
    • Nancy
    • Nancy.Owin
    • Owin

    【讨论】:

    • 谢谢老兄,拯救了我的一天。
    • 也适用于 NTML。
    【解决方案2】:

    对于基本的 Intranet 应用程序,我需要使用 Nancy 进行 Windows 身份验证。我以 @Steven Robbins 的回答作为起点,但去掉了我们不需要的东西,然后添加了 NancyContext.CurrentUser 属性的数量。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Principal;
    using System.Web;
    using Nancy;
    using Nancy.Security;
    
    namespace YourNamespace
    {
        /// <summary>
        /// Extensions for Nancy that implement Windows Authentication.
        /// </summary>
        public static class WindowsAuthenticationExtensions
        {
            private class WindowsUserIdentity : IUserIdentity
            {
                private string _userName;
    
                public WindowsUserIdentity(string userName)
                {
                    _userName = userName;
                }
    
                #region IUserIdentity
    
                IEnumerable<string> IUserIdentity.Claims
                {
                    get { throw new NotImplementedException(); }
                }
    
                string IUserIdentity.UserName
                {
                    get { return _userName; }
                }
    
                #endregion
            }
    
            #region Methods
    
            /// <summary>
            /// Forces the NancyModule to require a user to be Windows authenticated. Non-authenticated
            /// users will be sent HTTP 401 Unauthorized.
            /// </summary>
            /// <param name="module"></param>
            public static void RequiresWindowsAuthentication(this NancyModule module)
            {
                if (HttpContext.Current == null) 
                    throw new InvalidOperationException("An HttpContext is required. Ensure that this application is running under IIS.");
    
                module.Before.AddItemToEndOfPipeline(
                    new PipelineItem<Func<NancyContext, Response>>(
                        "RequiresWindowsAuthentication",
                        context =>
                        {
                            var principal = GetPrincipal();
    
                            if (principal == null || !principal.Identity.IsAuthenticated)
                            {
                                return HttpStatusCode.Unauthorized;
                            }
    
                            context.CurrentUser = new WindowsUserIdentity(principal.Identity.Name);
    
                            return null;
                        }));
            }
    
            private static IPrincipal GetPrincipal()
            {
                if (HttpContext.Current != null)
                {
                    return HttpContext.Current.User;
                }
    
                return new WindowsPrincipal(WindowsIdentity.GetCurrent());
            }
    
            #endregion
    
        }
    }
    

    你可以这样使用它:

    public class YourModule : NancyModule
    {
        public YourModule()
        {
            this.RequiresWindowsAuthentication();
    
            Get["/"] = parameters =>
                {
                    //...
                };
        }
    

    }

    【讨论】:

    • 谢谢,这对我帮助很大。你怎么能修改这个,所以它是每个请求而不是模块级别的?还是只检查每条路线中的个别声明?
    • 您可以在路由处理程序中添加this.RequiresAuthentication()
    • OWIN 的自托管情况下并没有真正的帮助,因为您将被绑定到System.Web CodeFox 的答案符合我的要求。
    【解决方案3】:

    我最近在一个内部项目中使用了它——我不太喜欢它,它把你与 asp.net 托管联系在一起,但它确实起到了作用:

    namespace Blah.App.Security
    {
        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Security.Principal;
        using System.Web;
    
        using Nancy;
    
        public static class SecurityExtensions
        {
            public static string CurrentUser
            {
                get
                {
                    return GetIdentity().Identity.Name;
                }
            }
    
            public static bool HasRoles(params string[] roles)
            {
                if (HttpContext.Current != null && HttpContext.Current.Request.IsLocal)
                {
                    return true;
                }
    
                var identity = GetIdentity();
    
                return !roles.Any(role => !identity.IsInRole(role));
            }
    
            public static void RequiresWindowsAuthentication(this NancyModule module)
            {
                if (HttpContext.Current != null && HttpContext.Current.Request.IsLocal)
                {
                    return;
                }
    
                module.Before.AddItemToEndOfPipeline(
                    new PipelineItem<Func<NancyContext, Response>>(
                        "RequiresWindowsAuthentication",
                        ctx =>
                            {
                                var identity = GetIdentity();
    
                                if (identity == null || !identity.Identity.IsAuthenticated)
                                {
                                    return HttpStatusCode.Forbidden;
                                }
    
                                return null;
                            }));
            }
    
            public static void RequiresWindowsRoles(this NancyModule module, params string[] roles)
            {
                if (HttpContext.Current != null && HttpContext.Current.Request.IsLocal)
                {
                    return;
                }
    
                module.RequiresWindowsAuthentication();
    
                module.Before.AddItemToEndOfPipeline(new PipelineItem<Func<NancyContext, Response>>("RequiresWindowsRoles", GetCheckRolesFunction(roles)));
            }
    
            private static Func<NancyContext, Response> GetCheckRolesFunction(IEnumerable<string> roles)
            {
                return ctx =>
                    {
                        var identity = GetIdentity();
    
                        if (roles.Any(role => !identity.IsInRole(role)))
                        {
                            return HttpStatusCode.Forbidden;
                        }
    
                        return null;
                    };
            }
    
            private static IPrincipal GetIdentity()
            {
                if (System.Web.HttpContext.Current != null)
                {
                    return System.Web.HttpContext.Current.User;
                }
    
                return new WindowsPrincipal(WindowsIdentity.GetCurrent());
            }
    
            public static Func<NancyContext, Response> RequireGroupForEdit(string group)
            {
                return ctx =>
                    {
                        if (ctx.Request.Method == "GET")
                        {
                            return null;
                        }
    
                        return HasRoles(group) ? null : (Response)HttpStatusCode.Forbidden;
                    };
            }
        }
    }
    

    如果它来自本地(用于测试),它会绕过所有安全检查,这可能是个坏主意,但它是防火墙后面的事情,所以这不是问题。

    不建议您逐字使用它,但可能会为您指明正确的方向:)

    【讨论】:

      【解决方案4】:

      你可以试着帮我完成Nancy.Authentication.Ntlm。这绝对是pre-alpha。基于我对 Nancy 内部结构的有限了解,我不知道如何实现几件事。

      目前代码挑战客户端,验证答案。但我未能通知客户此操作成功。

      但我仍然在努力。真的很难。

      如果有的话,我将不胜感激您的 cmets 和拉取请求。

      【讨论】:

        【解决方案5】:

        站在巨人的立场上,我以这种方式实现了它,以允许模拟身份验证以进行测试

        using System;
        using System.Collections.Generic;
        using Nancy;
        using Nancy.Security;
        
        namespace Your.Namespace
        {
            /// <summary>
            /// Extensions for Nancy that implement Windows Authentication.
            /// </summary>
            public static class WindowsAuthenticationExtensions
            {
                private class WindowsUserIdentity : IUserIdentity
                {
                    private readonly string _userName;
        
                    public WindowsUserIdentity(string userName)
                    {
                        _userName = userName;
                    }
        
                    #region IUserIdentity
        
                    IEnumerable<string> IUserIdentity.Claims
                    {
                        get { throw new NotImplementedException(); }
                    }
        
                    string IUserIdentity.UserName
                    {
                        get { return _userName; }
                    }
        
                    #endregion
                }
        
                #region Methods
        
                /// <summary>
                /// Forces the NancyModule to require a user to be Windows authenticated. Non-authenticated
                /// users will be sent HTTP 401 Unauthorized.
                /// </summary>
                /// <param name="module"></param>
                /// <param name="authenticationProvider"></param>
                public static void RequiresWindowsAuthentication(this NancyModule module, IWindowsAuthenticationProvider authenticationProvider)
                {
                    if (!authenticationProvider.CanAuthenticate)
                        throw new InvalidOperationException("An HttpContext is required. Ensure that this application is running under IIS.");
        
                    module.Before.AddItemToEndOfPipeline(
                        new PipelineItem<Func<NancyContext, Response>>(
                            "RequiresWindowsAuthentication",
                            context =>
                            {
                                var principal = authenticationProvider.GetPrincipal();
        
                                if (principal == null || !principal.Identity.IsAuthenticated)
                                {
                                    return HttpStatusCode.Unauthorized;
                                }
        
                                context.CurrentUser = new WindowsUserIdentity(principal.Identity.Name);
        
                                return null;
                            }));
                }
        
                #endregion
        
            }
        }
        

        IWindowsAuthenticationProvider:

        using System.Security.Principal;
        
        namespace Your.Namespace
        {
            public interface IWindowsAuthenticationProvider
            {
                bool CanAuthenticate { get; }
                IPrincipal GetPrincipal();
            }
        }
        

        WindowsAuthenticationProvider:

        using System.Security.Principal;
        using System.Web;
        
        namespace Your.Namespace
        {
            public class WindowsAuthenticationProvider : IWindowsAuthenticationProvider
            {
                public bool CanAuthenticate
                {
                    get { return HttpContext.Current != null; }
                }
        
                public IPrincipal GetPrincipal()
                {
                    if (HttpContext.Current != null)
                    {
                        return HttpContext.Current.User;
                    }
        
                    return new WindowsPrincipal(WindowsIdentity.GetCurrent());
                }
            }
        }
        

        实现它有点麻烦,因为您需要将 IWindowsAuthenticationProvided 注入到每个模块中

        public DefaultModule(IWindowsAuthenticationProvider authenticationProvider) 
                {
                    this.RequiresWindowsAuthentication(authenticationProvider);
                    Get["/"] = _ => "Hello World";
                }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-02-15
          • 2012-08-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-29
          • 2018-01-02
          • 1970-01-01
          相关资源
          最近更新 更多