ASP.NET Core 認證與授權[5]:初識授權

来源:http://www.cnblogs.com/RainingNight/archive/2017/11/21/authorization-in-asp-net-core.html
-Advertisement-
Play Games

經過前面幾章的姍姍學步,我們瞭解了在 ASP.NET Core 中是如何認證的,終於來到了授權階段。在認證階段我們通過用戶令牌獲取到用戶的Claims,而授權便是對這些的Claims的驗證,如:是否擁有Admin的角色,姓名是否叫XXX等等。本章就來介紹一下 ASP.NET Core 的授權系統的簡 ...


經過前面幾章的姍姍學步,我們瞭解了在 ASP.NET Core 中是如何認證的,終於來到了授權階段。在認證階段我們通過用戶令牌獲取到用戶的Claims,而授權便是對這些的Claims的驗證,如:是否擁有Admin的角色,姓名是否叫XXX等等。本章就來介紹一下 ASP.NET Core 的授權系統的簡單使用。

目錄

  1. 簡單授權
  2. 授權策略詳解
  3. 基於策略的授權進階

簡單授權

在ASP.NET 4.x中,我們通常使用Authorize過濾器來進行授權,它可以作用在Controller和Action上面,也可以添加到全局過濾器中。而在ASP.NET Core中也有一個Authorize特性(但不是過濾器),用法類似:

[Authorize] // Controller級別
public class SampleDataController : Controller
{
    [Authorize] // Action級別
    public IActionResult SampleAction()
    {
    }
}

IAllowAnonymous

在ASP.NET 4.x中,我們最常用的另一個特性便是AllowAnonymous,用來設置某個Controller或者Action跳過授權,它在 ASP.NET Core 中同樣適用:

[Authorize]
public class AccountController : Controller
{
    [AllowAnonymous]
    public ActionResult Login()
    {
    }

    public ActionResult Logout()
    {
    }
}

如上,LoginAction便不再需要授權,同樣,在 ASP.NET Core 中提供了一個統一的IAllowAnonymous介面,在授權邏輯中都是通過該介面來判斷是否跳過授權驗證的。

public interface IAllowAnonymous
{
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class AllowAnonymousAttribute : Attribute, IAllowAnonymous
{
}

IAuthorizeData

上面提到,在 ASP.NET Core 中,AuthorizeAttribute不再是一個MVC中的Filter了,而只是一個簡單的實現了IAuthorizeData介面的Attribute

public interface IAuthorizeData
{
    string Policy { get; set; }
    string Roles { get; set; }
    string AuthenticationSchemes { get; set; }
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class AuthorizeAttribute : Attribute, IAuthorizeData
{
    public AuthorizeAttribute() { }
    public AuthorizeAttribute(string policy)
    {
        Policy = policy;
    }
    public string Policy { get; set; }
    public string Roles { get; set; }
    public string AuthenticationSchemes { get; set; }
}

記得第一次在ASP.NET Core中實現自定義授權時,按照以前的經驗,直接繼承自AuthorizeAttribute,然後準備重寫OnAuthorization方法,結果懵逼了。然後在MVC的源碼中,苦苦搜尋AuthorizeAttribute的蹤跡,卻毫無所獲,後來才註意到它實現了IAuthorizeData介面,該介面才是認證的源頭,而Authorize特性只是認證信息的載體,並不包含任何邏輯。IAuthorizeData中定義的Policy, Roles, AuthenticationSchemes三個屬性分別代表著 ASP.NET Core 授權系統中的三種授權方式。

基於角色的授權

基於角色的授權,我們都比較熟悉,使用方式如下:

[Authorize(Roles = "Admin")] // 多個Role可以使用,分割
public class SampleDataController : Controller
{
    ...
}

基於角色的授權的邏輯與ASP.NET 4.x類似,都是使用我在《初識認證》中介紹的IsInRole方法來實現的。

基於Scheme的授權

對於AuthenticationScheme我在前面幾章也都介紹過,比如Cookie認證預設使用的AuthenticationScheme就是Cookies,在JwtBearer認證中,預設的Scheme就是Bearer

當初在學習認證時,還在疑惑,如何在使用Cookie認證的同時又支持Bearer認證呢?因為在認證中只能設置一個Scheme來執行,當看到這裡豁然開朗,後面會詳細介紹。

[Authorize(AuthenticationSchemes = "Cookies")] // 多個Scheme可以使用,分割
public class SampleDataController : Controller
{
    ...
}

當我們的應用程式中,同時使用了多種認證Scheme時,AuthenticationScheme授權就非常有用,在該授權模式下,會通過context.AuthenticateAsync(scheme)重新獲取Claims。

基於策略的授權

在ASP.NET Core中,重新設計了一種更加靈活的授權方式:基於策略的授權,也是授權的核心。

在使用基於策略的授權時,首先要定義授權策略,而授權策略本質上就是對Claims的一系列斷言。

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddAuthorization(options =>
    {
        options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
    });
}

如上,我們定義了一個名稱為EmployeeOnly的授權策略,它要求用戶的Claims中必須包含類型為EmployeeNumber的Claim。

其實,基於角色的授權和基於Scheme的授權,只是一種語法上的便捷,最終都會生成授權策略,後文會詳解介紹。

然後便可以在Authorize特性中通過Policy屬性來指定授權策略:

[Authorize(Policy = "EmployeeOnly")]
public class SampleDataController : Controller
{
    
}

授權策略詳解

AddAuthorization

授權策略的定義使用了AddAuthorization擴展方法,我們來看看它的源碼:

public static class AuthorizationServiceCollectionExtensions
{
    public static IServiceCollection AddAuthorization(this IServiceCollection services)
    {        
        services.TryAdd(ServiceDescriptor.Transient<IAuthorizationService, DefaultAuthorizationService>());
        services.TryAdd(ServiceDescriptor.Transient<IAuthorizationPolicyProvider, DefaultAuthorizationPolicyProvider>());
        services.TryAdd(ServiceDescriptor.Transient<IAuthorizationHandlerProvider, DefaultAuthorizationHandlerProvider>());
        services.TryAdd(ServiceDescriptor.Transient<IAuthorizationEvaluator, DefaultAuthorizationEvaluator>());
        services.TryAdd(ServiceDescriptor.Transient<IAuthorizationHandlerContextFactory, DefaultAuthorizationHandlerContextFactory>());
        services.TryAddEnumerable(ServiceDescriptor.Transient<IAuthorizationHandler, PassThroughAuthorizationHandler>());
        return services;
    }

    public static IServiceCollection AddAuthorization(this IServiceCollection services, Action<AuthorizationOptions> configure)
    {
        services.Configure(configure);
        return services.AddAuthorization();
    }
}

首先,是對授權進行配置的AuthorizationOptions,然後在DI系統中註冊了幾個核心對象的預設實現,我們一一來看。

AuthorizationOptions

對於Options模式,大家應該都比較熟悉了,AuthorizationOptions是添加和獲取授權策略的入口點:

public class AuthorizationOptions
{
    private IDictionary<string, AuthorizationPolicy> PolicyMap { get; } = new Dictionary<string, AuthorizationPolicy>(StringComparer.OrdinalIgnoreCase);
    // 在上一個策略驗證失敗後,是否繼續執行下一個授權策略
    public bool InvokeHandlersAfterFailure { get; set; } = true;
    public AuthorizationPolicy DefaultPolicy { get; set; } = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();

    public void AddPolicy(string name, AuthorizationPolicy policy)
    {
        PolicyMap[name] = policy;
    }

    public void AddPolicy(string name, Action<AuthorizationPolicyBuilder> configurePolicy)
    {
        var policyBuilder = new AuthorizationPolicyBuilder();
        configurePolicy(policyBuilder);
        AddPolicy(name,policyBuilder.Build());
    }

    public AuthorizationPolicy GetPolicy(string name)
    {
        return PolicyMap.ContainsKey(name) ? PolicyMap[name] : null;
    }
}

首先是一個PolicyMap字典,我們定義的策略都保存在其中,AddPolicy方法只是簡單的將策略添加到該字典中,而其DefaultPolicy屬性表示預設策略,初始值為:“已認證用戶”。

AuthorizationOptions中主要涉及到AuthorizationPolicyAuthorizationPolicyBuilder兩個對象。

AuthorizationPolicy

在 ASP.NET Core 中,授權策略具體表現為一個AuthorizationPolicy對象:

public class AuthorizationPolicy
{
    public AuthorizationPolicy(IEnumerable<IAuthorizationRequirement> requirements, IEnumerable<string> authenticationSchemes) {}
    public IReadOnlyList<IAuthorizationRequirement> Requirements { get; }
    public IReadOnlyList<string> AuthenticationSchemes { get; }

    public static AuthorizationPolicy Combine(params AuthorizationPolicy[] policies) 
    {
        return Combine((IEnumerable<AuthorizationPolicy>)policies);
    }
    public static AuthorizationPolicy Combine(IEnumerable<AuthorizationPolicy> policies) 
    {
        foreach (var policy in policies)
        {
            builder.Combine(policy);
        }
        return builder.Build();
    }
    public static async Task<AuthorizationPolicy> CombineAsync(IAuthorizationPolicyProvider policyProvider, IEnumerable<IAuthorizeData> authorizeData) 
    {
        foreach (var authorizeDatum in authorizeData)
        {
            any = true;
            var useDefaultPolicy = true;
            if (!string.IsNullOrWhiteSpace(authorizeDatum.Policy))
            {
                policyBuilder.Combine(await policyProvider.GetPolicyAsync(authorizeDatum.Policy));
                useDefaultPolicy = false;
            }
            var rolesSplit = authorizeDatum.Roles?.Split(',');
            if (rolesSplit != null && rolesSplit.Any())
            {
                policyBuilder.RequireRole(rolesSplit.Where(r => !string.IsNullOrWhiteSpace(r)).Select(r => r.Trim()));
                useDefaultPolicy = false;
            }
            var authTypesSplit = authorizeDatum.AuthenticationSchemes?.Split(',');
            if (authTypesSplit != null && authTypesSplit.Any())
            {
                foreach (var authType in authTypesSplit)
                {
                    if (!string.IsNullOrWhiteSpace(authType))
                    {
                        policyBuilder.AuthenticationSchemes.Add(authType.Trim());
                    }
                }
            }
            if (useDefaultPolicy)
            {
                policyBuilder.Combine(await policyProvider.GetDefaultPolicyAsync());
            }
        }
        return any ? policyBuilder.Build() : null;
    }
}

如上,Combine方法通過調用AuthorizationPolicyBuilder來完成授權策略的合併,而CombineAsync則是將我們上面介紹的IAuthorizeData轉換為授權策略,因此上面說基於角色/Scheme的授權本質上都是基於策略的授權。

對於AuthenticationSchemes屬性,我們在前幾章介紹認證時經常看到,用來表示我們使用哪個認證Scheme來獲取用戶的Claims,如果指定多個,則會合併它們的Claims,其實現下一章再來介紹。

Requirements屬性則是策略的核心了,每一個Requirement都代表一個授權條件,我們就先來瞭解一下它。

IAuthorizationRequirement

Requirement使用IAuthorizationRequirement介面來表示:

public interface IAuthorizationRequirement
{
}

IAuthorizationRequirement介面中並沒有任何成員,在 ASP.NET Core 中內置了一些常用的實現:

  • AssertionRequirement :使用最原始的斷言形式來聲明授權策略。

  • DenyAnonymousAuthorizationRequirement :用於表示禁止匿名用戶訪問的授權策略,併在AuthorizationOptions中將其設置為預設策略。

  • ClaimsAuthorizationRequirement :用於表示判斷Cliams中是否包含預期的Claims的授權策略。

  • RolesAuthorizationRequirement :用於表示使用ClaimsPrincipal.IsInRole來判斷是否包含預期的Role的授權策略。

  • NameAuthorizationRequirement:用於表示使用ClaimsPrincipal.Identities.Name來判斷是否包含預期的Name的授權策略。

  • OperationAuthorizationRequirement:用於表示基於操作的授權策略。

其邏輯也都非常簡單,我就不再一一介紹,只展示一下RolesAuthorizationRequirement的代碼片段:

public class RolesAuthorizationRequirement : AuthorizationHandler<RolesAuthorizationRequirement>, IAuthorizationRequirement
{
    public IEnumerable<string> AllowedRoles { get; }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RolesAuthorizationRequirement requirement)
    {
        ...

        if (requirement.AllowedRoles.Any(r => context.User.IsInRole(r)))
        {
            context.Succeed(requirement);
        }
        return Task.CompletedTask;
    }
}

AllowedRoles表示允許授權通過的角色,而它還實現了IAuthorizationHandler介面,用來完成授權的邏輯。

public interface IAuthorizationHandler
{
    Task HandleAsync(AuthorizationHandlerContext context);
}

AuthorizationRequirement並不是一定要實現IAuthorizationHandler介面,後文會詳細介紹。

AuthorizationPolicyBuilder

在上面已經多次用到AuthorizationPolicyBuilder,它提供了一系列創建AuthorizationPolicy的快捷方法:

public class AuthorizationPolicyBuilder
{
    public AuthorizationPolicyBuilder(params string[] authenticationSchemes);
    public AuthorizationPolicyBuilder(AuthorizationPolicy policy);

    public IList<IAuthorizationRequirement> Requirements { get; set; }
    public IList<string> AuthenticationSchemes { get; set; }
    public AuthorizationPolicyBuilder AddAuthenticationSchemes(params string[] schemes);
    public AuthorizationPolicyBuilder AddRequirements(params IAuthorizationRequirement[] requirements);

    public AuthorizationPolicyBuilder RequireAssertion(Func<AuthorizationHandlerContext, bool> handler);
    public AuthorizationPolicyBuilder RequireAssertion(Func<AuthorizationHandlerContext, Task<bool>> handler)
    {
        Requirements.Add(new AssertionRequirement(handler));
        return this;
    }
    public AuthorizationPolicyBuilder RequireAuthenticatedUser()
    {
        Requirements.Add(new DenyAnonymousAuthorizationRequirement());
        return this;
    }
    public AuthorizationPolicyBuilder RequireClaim(string claimType);
    public AuthorizationPolicyBuilder RequireClaim(string claimType, params string[] requiredValues);
    public AuthorizationPolicyBuilder RequireClaim(string claimType, IEnumerable<string> requiredValues)
    {
        Requirements.Add(new ClaimsAuthorizationRequirement(claimType, requiredValues));
        return this;
    }
    public AuthorizationPolicyBuilder RequireRole(params string[] roles);
    public AuthorizationPolicyBuilder RequireRole(IEnumerable<string> roles)
    {
        Requirements.Add(new RolesAuthorizationRequirement(roles));
        return this;
    }
    public AuthorizationPolicyBuilder RequireUserName(string userName)
    {
        Requirements.Add(new NameAuthorizationRequirement(userName));
        return this;
    }

    public AuthorizationPolicy Build();
    public AuthorizationPolicyBuilder Combine(AuthorizationPolicy policy);
}

在上面介紹的幾個Requirement,除了OperationAuthorizationRequirement外,都有對應的快捷添加方法,由於OperationAuthorizationRequirement並不屬於基於資源的授權,所以不在這裡,其用法留在其後續章節再來介紹。

整個授權策略的內容也就這麼多,並不複雜,整個結構大致如下:

authorization_policy

基於策略的授權進階

在上一小節,我們探索了一下授權策略的源碼,現在就來實戰一下。

我們使用AuthorizationPolicyBuilder可以很容易的在策略定義中組合我們需要的Requirement

public void ConfigureServices(IServiceCollection services)
{
    var commonPolicy = new AuthorizationPolicyBuilder().RequireClaim("MyType").Build();

    services.AddAuthorization(options =>
    {
        options.AddPolicy("User", policy => policy
            .RequireAssertion(context => context.User.HasClaim(c => (c.Type == "EmployeeNumber" || c.Type == "Role")))
        );

        options.AddPolicy("Employee", policy => policy
            .RequireRole("Admin")
            .RequireUserName("Alice")
            .RequireClaim("EmployeeNumber")
            .Combine(commonPolicy));
    });
}

如上,如果需要,我們還可以定義一個公共的策略對象,然後在策略定義中直接將其合併進來。

自定義策略

當內置的Requirement不能滿足我們的需求時,我們也可以很容易的定義自己的Requirement

public class MinimumAgeRequirement : AuthorizationHandler<NameAuthorizationRequirement>, IAuthorizationRequirement
{

    public MinimumAgeRequirement(int minimumAge)
    {
        MinimumAge = minimumAge;
    }

    public int MinimumAge { get; private set; }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, NameAuthorizationRequirement requirement)
    {
        if (context.User != null && context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth)
        {
            var dateOfBirth = Convert.ToDateTime(context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth).Value);
            int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
            if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
            {
                calculatedAge--;
            }
            if (calculatedAge >= requirement.MinimumAge)
            {
                context.Succeed(requirement);
            }
        }
        return Task.CompletedTask;
    }
}

然後就可以直接在AddPolicy中使用了:

services.AddAuthorization(options =>
{
    options.AddPolicy("Over21", policy => policy.Requirements.Add(new MinimumAgeRequirement(21)));
});

我們自定義的 Requirement 若想得到 ASP.NET Core 授權系統的執行,除了上面示例中的實現IAuthorizationHandler介面外,也可以單獨定義AuthorizationHandler,這樣可以更好的使用DI系統,並且還可以定義多個Handler,下麵就來演示一下。

多Handler模式

授權策略中的多個Requirement,它們屬於 & 的關係,只用全部驗證通過,才能最終授權成功。但是在有些場景下,我們可能希望一個授權策略可以適用多種情況,比如,我們進入公司時需要出示員工卡才可以被授權進入,但是如果我們忘了帶員工卡,可以去申請一個臨時卡,同樣可以授權成功:

public class EnterBuildingRequirement : IAuthorizationRequirement
{
}

public class BadgeEntryHandler : AuthorizationHandler<EnterBuildingRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, EnterBuildingRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == ClaimTypes.BadgeId)
        {
            context.Succeed(requirement);
        }
        else
        {
            // context.Fail();
        }
        return Task.CompletedTask;
    }
}

public class HasTemporaryStickerHandler : AuthorizationHandler<EnterBuildingRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, EnterBuildingRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == ClaimTypes.TemporaryBadgeId)
        {
            context.Succeed(requirement);
        }
        return Task.CompletedTask;
    }
}

如上,我們定義了兩個Handler,但是想讓它們得到執行,還需要將其註冊到DI系統中:

services.AddSingleton<IAuthorizationHandler, BadgeEntryHandler>();
services.AddSingleton<IAuthorizationHandler, HasTemporaryStickerHandler>();

此時,在我們的應該程式中使用EnterBuildingRequirement的授權時,將會依次執行這兩個Handler。而在上面介紹AuthorizationOptions時,提到它還有一個InvokeHandlersAfterFailure屬性,在這裡就派上用場了,只有其為true時(預設為True),才會在當前 AuthorizationHandler 授權失敗時,繼續執行下一個 AuthorizationHandler

在上面的示例中,我們使用context.Succeed(requirement)將授權結果設置為成功,而失敗時並沒有做任何標記,正常情況下都是這樣做的。但是如果需要,我們可以通過調用context.Fail()方法顯式的將授權結果設置為失敗,那麼,不管其他 AuthorizationHandler 是成功還是失敗,最終結果都將是授權失敗。

總結

ASP.NET Core 授權策略是一種非常強大、靈活的許可權驗證方案,能夠滿足大部分的授權場景。通過本文對授權策略的詳細介紹,我想應該能夠靈活的使用基於策略的授權了,但是授權策略到底是怎麼執行的呢?在下一章中,就來完整的探索一下 ASP.NET Core 授權系統的執行流程。


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

-Advertisement-
Play Games
更多相關文章
  • linux的內核版本由三部分組成: 主版本號 次版本號 末版本號 次版本號 以.分隔所代表的意思依次是: 主版本號 次版本號 末版本號 編譯版本號 廠商版本 CPU架構 其中後面部分是發行商發行版本添加上的 Linux哲學思想: 1、一切皆文件; 把幾乎所有資源,包括硬體設備都組織為文件格式; 2、 ...
  • 電腦系統: 馮諾依曼體系:運算器、控制器、存儲器、輸入、輸出 摩爾定律:由英特爾創始人之一戈登摩爾於1965年提出來,當價格不變時,集成電路上可容納的元器件的數目,約每隔18-24個月便會增加一倍,性能也將提升一倍。 電腦性能排行網站:www.top500.org windows下查看存儲單位: ...
  • /* 從右往左*/ #include <reg52.h> sbit ADDR0 = P1^0; sbit ADDR1 = P1^1; sbit ADDR2 = P1^2; sbit ADDR3 = P1^3; sbit ENLED = P1^4; void main() { unsigned int ...
  • 普通的文件和目錄許可權管理只能針對所有者、所屬組用戶、其他用戶進行管理,當許可權分配多樣化後採用普通的文件和目錄許可權管理加大複雜程度,而使用ACL(全名Access Control List)管理則可以實現靈活的許可權管理。 先查看分區是否支持ACL 上圖可以看出是支持ACL的 CentOS7預設創建的x ...
  • 操作系統的內核(Kernel) 是一組程式,這組程式的重點在於管理電腦的所有活動以及驅動系統中的所有硬體。 有了內核後,開發者不必自己去考慮機器語言、所有硬體的相關參數、程式的可移植性、專一性了。但是由於開發者使用的是操作系統提供的介面,所以需要考慮應用將在哪個操作系統中運行。 內核的功能有: 操 ...
  • 3.1 電磁干擾 EMI第一個知識點, 去耦電容的應用。 那首先要介紹一下去耦電容的應用背景, 這個背景就是電磁干擾, 也就是“傳說中” 的 EMI。1、 冬天的時候, 尤其是空氣比較乾燥的內陸城市, 很多朋友都有這樣的經歷, 手觸碰到電腦外殼、 鐵柜子等物品的時候會被電擊, 這就是“靜電放電” 現 ...
  • 預設tree命令是無法使用的,可以使用homebrew install tree安裝。 如果直接使用tree,查看的目錄裡面含有中文字元的目錄或文件時會出現漢字不能顯示的問題,可以使用tree -N查看。 ...
  • 本地YUM源伺服器最大優點是區域網的快速網路連接和穩定性。有了區域網中的YUM源伺服器,即便在Internet連接中斷的情況下,也不會影響其他YUM客戶端的軟體安裝和升級。 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...