[Nancy] 如何保護資源

Nancy 也有提供驗證讓我們保護資源,接下來看看如何實作

Nancy 驗證

要訪問當前經過身份驗證的用戶,只需訪問 NancyContext.CurrentUser 屬性。 值若為 null 表示當前請求的用戶尚未通過身份驗證,其他任何內容均表示該用戶已通過身份驗證。

 

是否通過驗證

判斷 NancyContext.CurrentUser 是否為 null,以下範例是使用 Before Pipeline

public class SecureModule : NancyModule
{
    public SecureModule()
    {
        Before += ctx => {
            return (this.Context.CurrentUser == null) ? new HtmlResponse(HttpStatusCode.Unauthorized) : null;
        };
        
        // Your routes here
    }
}

 

UserIdentity.UserName 是否為空,要自行實作這個抽象

public interface IUserIdentity
{
    /// <summary>
    /// Gets or sets the name of the current user.
    /// </summary>
    string UserName { get; set; }

    /// <summary>
    /// Gets or set the claims of the current user.
    /// </summary>
    IEnumerable<string> Claims { get; set; } 
}

 

為了避免重複建造輪子,Nancy 提供了以下擴充方法

RequiresAuthentication:確保已通過身分驗證,若沒通過將回傳 HttpStatusCode.Unauthorized ,驗證方式,CurrentUser不為 null,UserName不為空;範例如下:

public class SecureModule : NancyModule
{
    public SecureModule()
    {
        this.RequiresAuthentication();
    }
        
    // Your routes here
}

RequiresClaims:啟用後,必須滿足所有的 Claim 定義

RequiresAnyClaim:只要有滿足 Claim 定義中的其中一個

RequiresValidatedClaims:定義一個方法讓你完整的控制 Claims laims 驗證流程,方法簽章為 Func<IEnumerable<string>, bool> 

RequiresHttps:必須要是 Https

範例如下:

public class SecureModule : NancyModule
{
    public SecureModule()
    {
        this.RequiresHttps();
        this.RequiresAuthentication();
        this.RequiresClaims(new [] { "Admin" });
    }
        
    // Your routes here
}

 

或是你可以定義自己的擴充方法,範例如下:

public static class ModuleSecurity
{
    public static void RequiresAuthentication(this NancyModule module)
    {
        module.Before.AddItemToEndOfPipeline(RequiresAuthentication);
    }

    private static Response RequiresAuthentication(NancyContext context)
    {
        Response response = null;
        if ((context.CurrentUser == null) ||
            String.IsNullOrWhiteSpace(context.CurrentUser.UserName))
        {
            response = new Response { StatusCode = HttpStatusCode.Unauthorized };
        }

        return response;
    }

}

 

Basic Authentication

開發環境

  • VS 2019

  • .NET Framework 4

  • Nancy 1.4.1

安裝套件

Install-Package Nancy.Authentication.Basic -Version 1.4.1

Install-Package Nancy.Hosting.Self -Version 1.4.1

 

實作 IUserIdentity

public class UserIdentity : IUserIdentity
{
    public string UserName { get; set; }
 
    public IEnumerable<string> Claims { get; set; }
}

 

實作 IUserValidator

在這裡驗證帳號、密碼,為了演示沒有寫得很完整,你得自己實作完整的驗證

public class UserValidator : IUserValidator
{
    public IUserIdentity Validate(string username, string password)
    {
        if (username == "yao" && password == "pass@w0rd1~")
        {
            var identity = new UserIdentity
            {
                UserName = username,
                Claims   = new List<string> {"User"}
            };
            return identity;
        }
 
        //anonymous.
 
        return null;
    }
}

 

定義 NancyModule

調用 RequiresAuthentication 方法

public class GeneratorNancyModule  : NancyModule
{
    public GeneratorNancyModule()
    {
        this.RequiresAuthentication();
        this.Get["guid"] = p => Guid.NewGuid().ToString();
        this.Get["id"] = p => Guid.NewGuid().ToString();
    }
}

 

啟用 Basic Authentication

這裡我使用 Bootstrapper 的 ApplicationStartup 啟用 Basic Authentication

public class AuthenticationBootstrapper : DefaultNancyBootstrapper
{
    protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
    {
        base.ApplicationStartup(container, pipelines);
 
        var configuration = new BasicAuthenticationConfiguration(container.Resolve<IUserValidator>(),
                                                                 "MyRealm",
                                                                 UserPromptBehaviour.NonAjax);
        pipelines.EnableBasicAuthentication(configuration);
    }
}

  

Ctrl+F5 執行應用程式,若有 URL 保留區的問題請參考以下連結
https://dotblogs.com.tw/yc421206/2020/01/30/via_nancy_create_rest_api#%E7%B6%81%E5%AE%9A%20URL

 

用瀏覽器訪問服務,就能看到驗證視窗跳出來了

 

輸入帳密,就能訪問資源,得到資料

 

接著換用 Postman 調試,當沒有帶著 Authentication Header 訪問資源時會得到 401

 

驗證完成後順利的得到資料

 

允許匿名訪問

在 ASP.NET MVC、Web API,有 [Authorize]、[AllowAnonymous] 可以讓我們決定哪些資源需要授權才能訪問,在 Nancy 要怎麼實現?

我實作一個擴充方法,排除需要驗證的 Route Name

public static class ModuleSecurityExtension
{
    public static void RequiresAuthentication(this INancyModule module, string[] excludes)
    {
        module.AddBeforeHookOrExecute(p =>
                                      {
                                          Response response = null;
                                          if (excludes.Contains(p.ResolvedRoute.Description.Name))
                                          {
                                              return response;
                                          }
 
                                          if (p.CurrentUser == null ||
                                              string.IsNullOrWhiteSpace(p.CurrentUser.UserName))
                                          {
                                              response = new Response
                                              {
                                                  StatusCode = HttpStatusCode.Unauthorized
                                              };
                                          }
 
                                          return response;
                                      }, "Requires Authentication");
    }
}

 

在 NancyModule 調用 RequiresAuthentication 方法並傳入不需要驗證的 Route Name

public class GeneratorNancyModule  : NancyModule
{
    public GeneratorNancyModule()
    {
        //this.RequiresAuthentication();
        this.RequiresAuthentication(new[] {"name:id"});
        this.Get["name:guid","guid"] = p => Guid.NewGuid().ToString();
        this.Get["name:id","id"] = p => Guid.NewGuid().ToString();
    }
}

 

範例位置

https://github.com/yaochangyu/sample.dotblog/tree/master/Nancy/Lab.Security

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo