2012-11-16 18 views
19

Desidero utilizzare NancyFx per un'applicazione Web intranet. Tutta la documentazione e i forum menzionano solo l'autenticazione di moduli e di base. Qualcuno usa con successo Nancy con l'autenticazione di Windows?NancyFx e autenticazione di Windows

C'è anche qualcosa chiamato Nancy.Authentication.Stateless ma non riesco a vedere cosa fa (sembra che sia per l'uso in Apis).

risposta

8

Ho usato questo in un progetto interno di recente - non mi piace molto, e ti lega ad ASP.NET di hosting, ma il fatto di lavoro:

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

Si bypassa tutti i controlli di sicurezza se proviene da locale (per i test), che è probabilmente una cattiva idea, ma è una cosa dietro il firewall quindi non è un problema per questo.

non suggerirebbe lo si utilizza testualmente, ma potrebbe puntare nella giusta direzione :)

2

Si potrebbe provare a aiutarmi a finire Nancy.Authentication.Ntlm. Questo è sicuramente pre-alfa. Non so come implementare diverse cose basate principalmente sulla mia limitata conoscenza degli interni di Nancy.

Attualmente il codice sfida il cliente, convalida la risposta. Ma non sono riuscito a informare il cliente sul successo di questa operazione.

Ma continuo a lavorare sodo. Davvero difficile.

E gradirei i vostri commenti e le eventuali richieste di pull.

17

Avevo bisogno dell'autenticazione di Windows con Nancy per un'applicazione Intranet di base. Ho usato @Steven Robbins come punto di partenza, ma ho rimosso le cose che non ci servivano e poi ho aggiunto la popolazione della proprietà 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 

    } 
} 

si utilizza in questo modo:

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

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

}

+0

Grazie, questo mi ha aiutato molto. Come hai potuto modificare questo così è per richiesta piuttosto che a livello di modulo? O vorresti semplicemente controllare le singole richieste all'interno di ciascuna rotta? – mjbates7

+0

È possibile aggiungere un [this.RequiresAuthentication()] (https://stackoverflow.com/questions/12185257/nancyfx-authentication-per-route) all'interno del gestore del percorso. –

+1

Non molto utile nel caso di self-hosting in 'OWIN' dato che sarai legato a' System.Web' la risposta di 'CodeFox' ha soddisfatto i miei requisiti. – MaYaN

2

Adagiato sulle sholders dei giganti, ho implementato in questo modo per consentire l'autenticazione da beffeggiare per testare

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

implementazione si è un po 'disordinato, come è necessario l'IWindowsAuthenticationProvided iniettato in ogni modulo

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

Utilizzando Nancy con WindowsAuthentication è discusso da this thread. Damian Hickey ha fornito un example of using Nancy, hosted by OWin with WindowsAuthentication.

Ho leggermente modificato il codice (per rimuovere l'ormai deprecato 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; 
      }; 
     } 
    } 
} 

ringraziamento particolare va a Damian!


L'esempio richiede i seguenti pacchetti Nuget:

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

Grazie amico, ho salvato la giornata. – MaYaN

+0

funziona anche con NTML. – Thomas

Problemi correlati