2012-11-16 35 views
19

Chcę użyć NancyFx do internetowej aplikacji intranetowej. Cała dokumentacja i fora wymieniają tylko formularze i podstawowe uwierzytelnianie. Ktoś skutecznie korzysta z Nancy z uwierzytelnianiem Windows?Uwierzytelnianie NancyFx i Windows

Istnieje również coś o nazwie Nancy.Authentication.Stateless, ale nie widzę, co to robi (wygląda na to, że jest do użytku w Apis).

Odpowiedz

8

użyłem tego w projekcie wewnętrznym niedawno - I naprawdę nie podoba, a to wiąże Cię do ASP.NET hosting, ale spełnił swoje zadanie:

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

Omija wszelkie kontrole bezpieczeństwa jeśli pochodzi z lokalnego (do testowania), co jest prawdopodobnie złym pomysłem, ale jest za zaporą ogniową, więc nie stanowi to problemu.

Nie polecam go używać verbatim, ale może skierować Cię w dobrym kierunku :)

2

Można spróbować pomóc mi skończyć Nancy.Authentication.Ntlm. To zdecydowanie pre-alfa. Nie wiem, jak zaimplementować kilka rzeczy opartych głównie na mojej ograniczonej wiedzy na temat wewnętrznych elementów Nancy.

Obecnie kod rzuca wyzwanie klientowi, sprawdza poprawność odpowiedzi. Ale nie udało mi się poinformować klienta o powodzeniu tej operacji.

Ale wciąż ciężko pracuję. Naprawdę trudny.

Byłbym wdzięczny za wszelkie uwagi i prośby o pociągnięcie, jeśli takie istnieją.

17

Potrzebowałem uwierzytelniania systemu Windows z Nancy do podstawowej aplikacji intranetowej. Jako punkt wyjściowy użyłem odpowiedzi @Steven Robbins, ale usunęliśmy rzeczy, których nie potrzebowaliśmy, a następnie dodaliśmy populację właściwości 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 

    } 
} 

go używać tak:

public class YourModule : NancyModule 
{ 
    public YourModule() 
    { 
     this.RequiresWindowsAuthentication(); 

     Get["/"] = parameters => 
      { 
       //... 
      }; 
    } 

}

+0

Dzięki, bardzo mi to pomogło. Jak możesz to zmienić, aby było to na jedno żądanie, a nie na poziomie modułu? A może po prostu sprawdzisz poszczególne roszczenia w ramach każdej trasy? – mjbates7

+0

Możesz dodać [this.RequiresAuthentication()] (https://stackoverflow.com/questions/12185257/nancyfx-authentication-per-route) wewnątrz programu obsługi trasy. –

+1

Niezbyt pomocne w przypadku samodzielnego hostowania w 'OWIN', ponieważ będziesz przywiązany do' System.Web' odpowiedź 'CodeFox' spełniła moje wymagania. – MaYaN

2

Stojąc na sholders gigantów, jakie wprowadziły go w ten sposób, aby umożliwić uwierzytelnianie być wyśmiewany do testowania

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

go wykonawcza jest trochę brudny jak potrzebne są IWindowsAuthenticationProvided wtryskiwana do każdego modułu

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

Korzystanie Nancy z WindowsAuthentication została omówiona przez this thread. Damian Hickey dostarczył example of using Nancy, hosted by OWin with WindowsAuthentication.

mam nieco zmodyfikowany kod (w celu usunięcia teraz przestarzałej 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; 
      }; 
     } 
    } 
} 

Specjalne podziękowania dla Damiana!


Przykład wymaga następujących pakietów Nuget:

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

Dzięki stary, uratowałeś mi dzień. – MaYaN

+0

Działa również z NTML. – Thomas

Powiązane problemy