2012-11-16 147 views
19

我想将NancyFx用于Intranet Web应用程序。所有文档和论坛仅提及表单和基本身份验证。任何人都成功使用南希与Windows身份验证?NancyFx和Windows身份验证

还有一些叫做Nancy.Authentication.Stateless的东西,但我看不到它的作用(看起来像是在Apis中使用)。

回答

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; 
       }; 
     } 
    } 
} 

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

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

2

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

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

但我仍在努力工作。真的很难。

如果有的话,我会感谢您的意见和拉请求。

17

我需要Windows身份验证与南希基本的Intranet应用程序。我以@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 => 
      { 
       //... 
      }; 
    } 

}

+0

谢谢,这帮了我很多。你怎么能修改这个,所以它是按请求而不是模块级别的?或者你只是检查每条路线内的个人索赔? – mjbates7

+0

您可以在路由处理程序内添加[this.RequiresAuthentication()](https://stackoverflow.com/questions/12185257/nancyfx-authentication-per-route)。 –

+1

在'OWIN'中自托管的情况并不真正有用,因为您将被绑定到'System.Web','CodeFox'的答案符合我的要求。 – MaYaN

2

站在巨人的sholders,我以这种方式实现了它,以允许模拟来测试

认证
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"; 
     } 
16

使用南希WindowsAuthenticationthis 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; 
      }; 
     } 
    } 
} 

特别感谢达米安!


该示例需要以下的NuGet包:

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

谢谢老兄,救了我一天。 – MaYaN

+0

也适用于NTML。 – Thomas