ASP.NET Core 認證與授權[6]:授權策略是怎麼執行的?

来源:http://www.cnblogs.com/RainingNight/archive/2017/11/22/authorize-how-to-work-in-asp-net-core.html
-Advertisement-
Play Games

在上一章中,詳細介紹了 ASP.NET Core 中的授權策略,在需要授權時,只需要在對應的Controler或者Action上面打上 特性,並指定要執行的策略名稱即可,但是,授權策略是怎麼執行的呢?懷著一顆好奇的心,忍不住來探索一下它的執行流程。 目錄 1. "MVC中的授權" "Authoriz ...


在上一章中,詳細介紹了 ASP.NET Core 中的授權策略,在需要授權時,只需要在對應的Controler或者Action上面打上[Authorize]特性,並指定要執行的策略名稱即可,但是,授權策略是怎麼執行的呢?懷著一顆好奇的心,忍不住來探索一下它的執行流程。

目錄

  1. MVC中的授權
  2. IPolicyEvaluator
  3. IAuthorizationService

在《(上一章》中提到,AuthorizeAttribute只是一個簡單的實現了IAuthorizeData介面的特性,並且在 ASP.NET Core 授權系統中並沒有使用到它。我們知道在認證中,還有一個UseAuthentication擴展方法來激活認證系統,但是在授權中並沒有類似的機制。

這是因為當我們使用[Authorize]通常是在MVC中,由MVC來負責激活授權系統。本來在這個系列的文章中,我並不想涉及到MVC的知識,但是為了能更好的理解授權系統的執行,就來簡單介紹一下MVC中與授權相關的知識。

MVC中的授權

當我們使用MVC時,首先會調用MVC的AddMvc擴展方法,用來註冊一些MVC相關的服務:

public static IMvcBuilder AddMvc(this IServiceCollection services)
{
    var builder = services.AddMvcCore();

    builder.AddAuthorization();

    ...
}

public static IMvcCoreBuilder AddAuthorization(this IMvcCoreBuilder builder)
{
    AddAuthorizationServices(builder.Services);
    return builder;
}

internal static void AddAuthorizationServices(IServiceCollection services)
{
    services.AddAuthenticationCore();
    services.AddAuthorization();
    services.AddAuthorizationPolicyEvaluator();

    services.TryAddEnumerable(
        ServiceDescriptor.Transient<IApplicationModelProvider, AuthorizationApplicationModelProvider>());
}

在上面AddAuthorizationServices中的前三個方法都屬於 ASP.NET Core 《Security》項目中提供的擴展方法,其中前兩個在前面幾章已經介紹過了,對於AddAuthorizationPolicyEvaluator放到後面再來介紹,我們先來看一下MVC中的AuthorizationApplicationModelProvider

AuthorizationApplicationModelProvider

在MVC中有一個ApplicationModel的概念,它用來封裝Controller, Filter, ApiExplorer等。對應的,在MVC中還提供了一系列的ApplicationModelProvider來初始化ApplicationModel的各個部分,而AuthorizationApplicationModelProvider就是用來初始化與授權相關的部分。

public class AuthorizationApplicationModelProvider : IApplicationModelProvider
{
    public void OnProvidersExecuting(ApplicationModelProviderContext context)
    {
        foreach (var controllerModel in context.Result.Controllers)
        {
            var controllerModelAuthData = controllerModel.Attributes.OfType<IAuthorizeData>().ToArray();
            if (controllerModelAuthData.Length > 0)
            {
                controllerModel.Filters.Add(GetFilter(_policyProvider, controllerModelAuthData));
            }
            foreach (var attribute in controllerModel.Attributes.OfType<IAllowAnonymous>())
            {
                controllerModel.Filters.Add(new AllowAnonymousFilter());
            }
            foreach (var actionModel in controllerModel.Actions)
            {
                var actionModelAuthData = actionModel.Attributes.OfType<IAuthorizeData>().ToArray();
                if (actionModelAuthData.Length > 0)
                {
                    actionModel.Filters.Add(GetFilter(_policyProvider, actionModelAuthData));
                }
                foreach (var attribute in actionModel.Attributes.OfType<IAllowAnonymous>())
                {
                    actionModel.Filters.Add(new AllowAnonymousFilter());
                }
            }
        }
    }
}

如上,首先查找每個Controller中實現了IAuthorizeData介面的特性,然後將其轉化為AuthorizeFilter並添加到Controller的Filter集合中,緊接著再查找實現了IAllowAnonymous介面的特性,將其轉化為AllowAnonymousFilter過濾器也添加到Filter集合中,然後以同樣的邏輯查找Action上的特性並添加到Action的Filter集合中。

其中的關鍵點就是將IAuthorizeData(也就是通過我們熟悉的[Authorize]特性)轉化為MVC中的AuthorizeFilter過濾器:

public static AuthorizeFilter GetFilter(IAuthorizationPolicyProvider policyProvider, IEnumerable<IAuthorizeData> authData)
{
    if (policyProvider.GetType() == typeof(DefaultAuthorizationPolicyProvider))
    {
        var policy = AuthorizationPolicy.CombineAsync(policyProvider, authData).GetAwaiter().GetResult();
        return new AuthorizeFilter(policy);
    }
    else
    {
        return new AuthorizeFilter(policyProvider, authData);
    }
}

CombineAsync在上一章的《AuthorizationPolicy》中已經介紹過了,我們往下看看AuthorizeFilter的實現。

AuthorizeFilter

在MVC中有一個AuthorizeFilter過濾器,類似我們在ASP.NET 4.x中所熟悉的[Authorize],它實現了IAsyncAuthorizationFilter介面,定義如下:

public class AuthorizeFilter : IAsyncAuthorizationFilter, IFilterFactory
{
    public AuthorizeFilter(AuthorizationPolicy policy) {}
    public AuthorizeFilter(IAuthorizationPolicyProvider policyProvider, IEnumerable<IAuthorizeData> authorizeData) : this(authorizeData) {}
    public AuthorizeFilter(IEnumerable<IAuthorizeData> authorizeData) {}

    public IEnumerable<IAuthorizeData> AuthorizeData { get; }
    public AuthorizationPolicy Policy { get; }

    public virtual async Task OnAuthorizationAsync(AuthorizationFilterContext context)
    {
        var effectivePolicy = Policy;
        if (effectivePolicy == null)
        {
            effectivePolicy = await AuthorizationPolicy.CombineAsync(PolicyProvider, AuthorizeData);
        }
        var policyEvaluator = context.HttpContext.RequestServices.GetRequiredService<IPolicyEvaluator>();
        var authenticateResult = await policyEvaluator.AuthenticateAsync(effectivePolicy, context.HttpContext);
        if (context.Filters.Any(item => item is IAllowAnonymousFilter))
        {
            return;
        }
        var authorizeResult = await policyEvaluator.AuthorizeAsync(effectivePolicy, authenticateResult, context.HttpContext, context);

        ... // 如果授權失敗,返回ChallengeResult或ForbidResult
    }
}

AuthorizeFilter的OnAuthorizationAsync方法會在Action執行之前觸發,其調用IPolicyEvaluator來完成授權,將執行流程切回到 ASP.NET Core 授權系統中。關於MVC中IApplicationModelProvider以及Filter的概念,在以後MVC系列的文章中再來詳細介紹,下麵就繼續介紹 ASP.NET Core 的授權系統,也就是《Security》項目。

IPolicyEvaluator

IPolicyEvaluator是MVC調用授權系統的入口點,其定義如下:

public interface IPolicyEvaluator
{
    Task<AuthenticateResult> AuthenticateAsync(AuthorizationPolicy policy, HttpContext context);
    Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource);
}

在上面介紹的AddMVC中,調用了AddAuthorizationPolicyEvaluator擴展方法,它有如下定義:

public static class PolicyServiceCollectionExtensions
{
    public static IServiceCollection AddAuthorizationPolicyEvaluator(this IServiceCollection services)
    {
        services.TryAdd(ServiceDescriptor.Transient<IPolicyEvaluator, PolicyEvaluator>());
        return services;
    }
}

由此可知IPolicyEvaluator的預設實現為PolicyEvaluator,我們就從它入手,來一步一步解剖 ASP.NET Core 授權系統的執行步驟。

AuthorizeFilter中,依次調到了AuthenticateAsyncAuthorizeAsync方法,我們就一一來看。

AuthenticateAsync(AuthenticationSchemes)

為什麼還有一個AuthenticateAsync方法呢,這不是在認證階段執行的嗎?我們看下它的實現:

public class PolicyEvaluator : IPolicyEvaluator
{
    public virtual async Task<AuthenticateResult> AuthenticateAsync(AuthorizationPolicy policy, HttpContext context)
    {
        if (policy.AuthenticationSchemes != null && policy.AuthenticationSchemes.Count > 0)
        {
            ClaimsPrincipal newPrincipal = null;
            foreach (var scheme in policy.AuthenticationSchemes)
            {
                var result = await context.AuthenticateAsync(scheme);
                if (result != null && result.Succeeded)
                {
                    newPrincipal = SecurityHelper.MergeUserPrincipal(newPrincipal, result.Principal);
                }
            }

            if (newPrincipal != null)
            {
                context.User = newPrincipal;
                return AuthenticateResult.Success(new AuthenticationTicket(newPrincipal, string.Join(";", policy.AuthenticationSchemes)));
            }
            else
            {
                context.User = new ClaimsPrincipal(new ClaimsIdentity());
                return AuthenticateResult.NoResult();
            }
        }

        return (context.User?.Identity?.IsAuthenticated ?? false) 
            ? AuthenticateResult.Success(new AuthenticationTicket(context.User, "context.User"))
            : AuthenticateResult.NoResult();
    }
}

在《上一章》中,我們知道在AuthorizationPolicy中有AuthenticationSchemesIAuthorizationRequirement兩個屬性,並詳細介紹介紹了Requirement,但是沒有提到AuthenticationSchemes的調用。

那麼,看到這裡,也就大概明白了,它與Requirements的執行是完全獨立的,併在它之前執行,用於重置Claims,那麼為什麼要重置呢?

在認證的章節介紹過,在認證階段,只會執行預設的認證Scheme,context.User就是使用context.AuthenticateAsync(DefaultAuthenticateScheme)來賦值的,當我們希望使用非預設的Scheme,或者是想合併多個認證Scheme的Claims時,就需要使用基於Scheme的授權來重置Claims了。

它的實現也很簡單,直接使用我們在授權策略中指定的Schemes來依次調用認證服務的AuthenticateAsync方法,並將生成的Claims合併,最後返回我們熟悉的AuthenticateResult認證結果。

AuthorizeAsync(Requirements)

接下來再看一下PolicyEvaluatorAuthorizeAsync方法:

public class PolicyEvaluator : IPolicyEvaluator
{
    private readonly IAuthorizationService _authorization;
    public PolicyEvaluator(IAuthorizationService authorization)
    {
        _authorization = authorization;
    }

    public virtual async Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource)
    {
        var result = await _authorization.AuthorizeAsync(context.User, resource, policy);
        if (result.Succeeded) return PolicyAuthorizationResult.Success();
        return (authenticationResult.Succeeded) ? PolicyAuthorizationResult.Forbid() : PolicyAuthorizationResult.Challenge();
    }
}

該方法會根據Requirements來完成授權,具體的實現是通過調用IAuthorizationService來實現的。

最終返回的是一個PolicyAuthorizationResult對象,併在授權失敗時,根據認證結果來返回Forbid(未授權)Challenge(未登錄)

public class PolicyAuthorizationResult
{
    private PolicyAuthorizationResult() { }
    public bool Challenged { get; private set; }
    public bool Forbidden { get; private set; }
    public bool Succeeded { get; private set; }
}

IAuthorizationService

然後就到了授權的核心對象AuthorizationService,也可以稱為授權的外交官,我們也可以直接在應用代碼中調用該對象來實現授權,它有如下定義:

public interface IAuthorizationService
{    
    Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName);
    Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements);
}

AuthorizeAsync中還涉及到一個resource對象,用來實現面向資源的授權,放在下一章中再來介紹,而在本章與《前一章》的示例中,該值均為null

ASP.NET Core 中還為IAuthorizationService提供了幾個擴展方法:

public static class AuthorizationServiceExtensions
{
    public static Task<AuthorizationResult> AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, string policyName) {}
    public static Task<AuthorizationResult> AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, AuthorizationPolicy policy) {}
    public static Task<AuthorizationResult> AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, object resource, IAuthorizationRequirement requirement) {}
    public static Task<AuthorizationResult> AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, object resource, AuthorizationPolicy policy) {}
}

其預設實現為DefaultAuthorizationService:

public class DefaultAuthorizationService : IAuthorizationService
{
    private readonly AuthorizationOptions _options;
    private readonly IAuthorizationHandlerContextFactory _contextFactory;
    private readonly IAuthorizationHandlerProvider _handlers;
    private readonly IAuthorizationEvaluator _evaluator;
    private readonly IAuthorizationPolicyProvider _policyProvider;

    public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName)
    {        
        var policy = await _policyProvider.GetPolicyAsync(policyName);
        return await this.AuthorizeAsync(user, resource, policy);
    }

    public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements)
    {
        var authContext = _contextFactory.CreateContext(requirements, user, resource);
        var handlers = await _handlers.GetHandlersAsync(authContext);
        foreach (var handler in handlers)
        {
            await handler.HandleAsync(authContext);
            if (!_options.InvokeHandlersAfterFailure && authContext.HasFailed)
            {
                break;
            }
        }
        return _evaluator.Evaluate(authContext);
    }
}

通過上面代碼可以看出,在《上一章》中介紹的授權策略,在這裡獲取到它的Requirements,後續便不再需要了。而在AuthorizationService中是通過調用四大核心對象來完成授權,我們一一來看。

IAuthorizationPolicyProvider

由於在[Authorize]中,我們指定的是策略的名稱,因此需要使用IAuthorizationPolicyProvider來根據名稱獲取到策略對象,預設實現為DefaultAuthorizationPolicyProvider

public class DefaultAuthorizationPolicyProvider : IAuthorizationPolicyProvider
{
    private readonly AuthorizationOptions _options;

    public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
    {
        return Task.FromResult(_options.DefaultPolicy);
    }

    public virtual Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {
        return Task.FromResult(_options.GetPolicy(policyName));
    }
}

在上一章中介紹過,我們定義的策略都保存在《AuthorizationOptions》的字典中,因此在這裡只是簡單的將AuthorizationOptions中的同名方法非同步化。

IAuthorizationHandlerContextFactory

授權上下文是我們接觸較多的對象,當我們自定義授權Handler時就會用到它,它是使用簡單工廠模式來創建的:

public class DefaultAuthorizationHandlerContextFactory : IAuthorizationHandlerContextFactory
{
    public virtual AuthorizationHandlerContext CreateContext(IEnumerable<IAuthorizationRequirement> requirements, ClaimsPrincipal user, object resource)
    {
        return new AuthorizationHandlerContext(requirements, user, resource);
    }
}

授權上下文中主要包含用戶的Claims和授權策略的Requirements

public class AuthorizationHandlerContext
{
    private HashSet<IAuthorizationRequirement> _pendingRequirements;
    private bool _failCalled;
    private bool _succeedCalled;

    public AuthorizationHandlerContext(IEnumerable<IAuthorizationRequirement> requirements, ClaimsPrincipal user, object resource)
    {
        Requirements = requirements; User = user; Resource = resource;
        _pendingRequirements = new HashSet<IAuthorizationRequirement>(requirements);
    }

    public virtual bool HasFailed { get { return _failCalled; } }
    public virtual bool HasSucceeded => !_failCalled && _succeedCalled && !_pendingRequirements.Any();
    public virtual void Fail()
    {
        _failCalled = true;
    }
    public virtual void Succeed(IAuthorizationRequirement requirement)
    {
        _succeedCalled = true;
        _pendingRequirements.Remove(requirement);
    }
}

如上,_pendingRequirements中保存著所有待驗證的Requirements,驗證成功的Requirement則從中移除。

IAuthorizationHandlerProvider

兜兜轉轉,終於進入到了授權的最終驗證邏輯中了,首先,使用IAuthorizationHandlerProvider來獲取到所有的授權Handler

IAuthorizationHandlerProvider的預設實現為DefaultAuthorizationHandlerProvider:

public class DefaultAuthorizationHandlerProvider : IAuthorizationHandlerProvider
{
    private readonly IEnumerable<IAuthorizationHandler> _handlers;

    public DefaultAuthorizationHandlerProvider(IEnumerable<IAuthorizationHandler> handlers)
    {
        _handlers = handlers;
    }

    public Task<IEnumerable<IAuthorizationHandler>> GetHandlersAsync(AuthorizationHandlerContext context)
        => Task.FromResult(_handlers);
}

在《上一章》中,我們還介紹到,我們定義的Requirement,可以直接實現IAuthorizationHandler介面,也可以單獨定義Handler,但是需要註冊到DI系統中去。

在預設的AuthorizationHandlerProvider中,會從DI系統中獲取到我們註冊的所有Handler,最終調用其HandleAsync方法。

我們在實現IAuthorizationHandler介面時,通常是繼承自AuthorizationHandler<TRequirement>來實現,它有如下定義:

public abstract class AuthorizationHandler<TRequirement> : IAuthorizationHandler where TRequirement : IAuthorizationRequirement
{
    public virtual async Task HandleAsync(AuthorizationHandlerContext context)
    {
        foreach (var req in context.Requirements.OfType<TRequirement>())
        {
            await HandleRequirementAsync(context, req);
        }
    }

    protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement);
}

如上,首先會在HandleAsync過濾出與Requirement對匹配的Handler,然後再調用其HandleRequirementAsync方法。

那我們定義的直接實現IAuthorizationHandler了介面的Requirement又是如何執行的呢?

AddAuthorization擴展方法中可以看到,預設還為IAuthorizationHandler註冊了一個PassThroughAuthorizationHandler,定義如下:

public class PassThroughAuthorizationHandler : IAuthorizationHandler
{
    public async Task HandleAsync(AuthorizationHandlerContext context)
    {
        foreach (var handler in context.Requirements.OfType<IAuthorizationHandler>())
        {
            await handler.HandleAsync(context);
        }
    }
}

它負責調用該策略中所有實現了IAuthorizationHandler介面的Requirement

IAuthorizationEvaluator

最後,通過調用IAuthorizationEvaluator介面,來完成最終的授權結果,預設實現為DefaultAuthorizationEvaluator:

public class DefaultAuthorizationEvaluator : IAuthorizationEvaluator
{
    public AuthorizationResult Evaluate(AuthorizationHandlerContext context)
        => context.HasSucceeded
            ? AuthorizationResult.Success()
            : AuthorizationResult.Failed(context.HasFailed
                ? AuthorizationFailure.ExplicitFail()
                : AuthorizationFailure.Failed(context.PendingRequirements));
}

當我們在一個策略中指定多個Requirement時,只有全部驗證通過時,授權上下文中的HasSucceeded才會為True,而HasFailed代表授權結果的顯式失敗。

這裡根據授權上下文的驗證結果來生成授權結果:

public class AuthorizationResult
{
    public bool Succeeded { get; private set; }
    public AuthorizationFailure Failure { get; private set; }
    public static AuthorizationResult Success() => new AuthorizationResult { Succeeded = true };
    public static AuthorizationResult Failed(AuthorizationFailure failure) => new AuthorizationResult { Failure = failure };
    public static AuthorizationResult Failed() => new AuthorizationResult { Failure = AuthorizationFailure.ExplicitFail() };
}

public class AuthorizationFailure
{
    private AuthorizationFailure() { }
    public bool FailCalled { get; private set; }
    public IEnumerable<IAuthorizationRequirement> FailedRequirements { get; private set; }
    public static AuthorizationFailure ExplicitFail()
    {
        return new AuthorizationFailure { FailCalled = true, FailedRequirements = new IAuthorizationRequirement[0] };
    }
    public static AuthorizationFailure Failed(IEnumerable<IAuthorizationRequirement> failed)
        => new AuthorizationFailure { FailedRequirements = failed };

}

整個授權流程的結構大致如下:

authorization_service

總結

通過對 ASP.NET Core 授權系統執行流程的探索,可以看出授權是主要是通過調用IAuthorizationService來完成的,而授權策略的本質是提供 Requirement ,我們完全可以使用它們兩個來完成各種靈活的授權方式,而不用局限於策略。在 ASP.NET Core 中,還提供了基於資源的授權,放在下一章中來介紹,並會簡單演示一下在一個通用許可權管理系統中如何來授權。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最終環境: Ubuntu17.10、Apache2.4.27、MySQL5.7.20、PHP7.1 1. 安裝 apache 官方源有,直接安裝: 2. 安裝 mysql 官方源有,直接安裝: 安裝期間會提示設置 MySQL administrator 的密碼 PS:需要什麼軟體或包,直接用 apt ...
  • 方法一:支持rsync的網站 對於常用的centos、Ubuntu、等使用官方yum源在 http://mirrors.ustc.edu.cn 都存在鏡像。 同時 http://mirrors.ustc.edu.cn 網站又支持 rsync 協議, 可以通過rsync實現 鏡像yum源。 在wind ...
  • Linux下軟體的安裝一般由3個步驟組成: 若取消編譯: 若卸載軟體: 本節主要討論configure配置腳本。 如下圖所示,有些軟體就有configure配置腳本: 就可以使用命令./configure –help 輸出詳細的選項列表 常用選項如下: --host 編譯運行後的程式,預設為buil ...
  • 三劍客之awk 第1章 awk簡介 1.1 awk簡介 一種名字怪異的語言。 模式掃描和處理。處理文本流,水流。 awk不僅僅是linux系統中的一個命令,而且是一種編程語言,可以用來處理數據和生成報告。 處理的數據可以是一個或多個文件,可以是來自標準輸入,也可以通過管道獲取標準輸入,awk可以 在 ...
  • [20171120]關於find 軟連接問題.txt--//上個星期為了測試oracle參數filesystemio_options,將資料庫做了一次移動.但是我使用find對軟鏈接目錄查詢時--//遇到一些問題做一個記錄1.建立軟鏈接:$ cp -a /mnt/ramdisk/book /home ...
  • 1. CentOS6及以前 在CentOS6及以前的版本中,free命令輸出是這樣的: 第一行: 系統記憶體主要分為四部分:used(程式已使用記憶體),free(空閑記憶體),buffers(buffer cache),cached(Page cache)。 系統總記憶體total = used + fr ...
  • 紙殼CMS發佈了2.3版本,主要是添加了商城功能,強化產品功能。讓您的網站輕鬆實現電子商務。 有關2.3版本的更多信息,請查看以下鏈接: https://github.com/SeriaWei/ZKEACMS.Core/releases/tag/v2.3 ...
  • 返回總目錄 本小節目錄 Move Method(搬移函數) Move Field(搬移欄位) 1Move Method(搬移函數) 概要 你的程式中,有個函數與其所駐類之外的另一個類進行更多交流:調用後者,或被或者調用。 在該函數最常引用的類中建立一個有著類似行為的新函數。將舊函數變成一個單純的委托 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...