21

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

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

4

5 回答 5

18

此线程讨论了将Nancy 与 WindowsAuthentication 一起使用。Damian Hickey 提供了一个使用 Nancy 的示例,该示例由 OWin 和 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;
            };
        }
    }
}

特别感谢达米安!


该示例需要以下 NuGet 包:

  • Microsoft.Owin.Host.HttpListener
  • Microsoft.Owin.Hosting
  • Microsoft.Owin
  • Nancy
  • Nancy.Owin
  • Owin
于 2014-10-02T15:30:56.947 回答
17

对于基本的 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 =>
            {
                //...
            };
    }

}

于 2013-10-14T23:12:19.077 回答
8

我最近在一个内部项目中使用了它——我不太喜欢它,它把你和 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;
                };
        }
    }
}

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

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

于 2012-11-21T15:14:18.707 回答
2

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

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

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

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

于 2012-12-16T13:18:22.270 回答
2

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

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";
        }
于 2014-06-19T12:43:45.837 回答