イントラネット Web アプリに NancyFx を使用したいと考えています。すべてのドキュメントとフォーラムでは、フォームと基本認証のみが言及されています。Windows 認証で Nancy を正常に使用できる人はいますか?
Nancy.Authentication.Stateless というものもありますが、それが何をするのかわかりません (Apis で使用するようです)。
イントラネット Web アプリに NancyFx を使用したいと考えています。すべてのドキュメントとフォーラムでは、フォームと基本認証のみが言及されています。Windows 認証で Nancy を正常に使用できる人はいますか?
Nancy.Authentication.Stateless というものもありますが、それが何をするのかわかりません (Apis で使用するようです)。
基本的なイントラネット アプリケーションには、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 =>
{
//...
};
}
}
私は最近これを内部プロジェクトで使用しました - 私はそれがあまり好きではなく、それはあなたを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;
};
}
}
}
ローカル (テスト用) からのものである場合、すべてのセキュリティ チェックをバイパスします。これはおそらく悪い考えですが、ファイアウォールの背後にあるため、これは問題ではありません。
逐語的に使用することはお勧めしませんが、正しい方向に向けるかもしれません:)
Nancy.Authentication.Ntlmを終了するのを手伝ってください。これは間違いなくプレアルファです。ナンシーの内部に関する私の限られた知識に主に基づいて、いくつかのことを実装する方法がわかりません。
現在、コードはクライアントにチャレンジし、答えを検証します。しかし、私はこの操作の成功についてクライアントに通知できませんでした。
しかし、私はまだ一生懸命働いています。とても大変。
And I would appreciate your comments and pull requests if any.
巨人の肩の上に立って、このように実装して、認証をテスト用にモックできるようにしました
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";
}