ASP.NET CORE WEBAPI 登錄 JWT 鑒權 ,介面許可權驗證

来源:https://www.cnblogs.com/l-monstar/archive/2023/04/21/17337768.html
-Advertisement-
Play Games

###JWT的簡單使用 ####介紹 當今Web開發中,API的使用越來越廣泛,而API的安全性也變得越來越重要。其中,JWT(JSON Web Token)鑒權和授權是一種常見的解決方案。 本篇文章將會介紹JWT鑒權和授權的原理、實現方式以及註意事項。 ####什麼是JWT? JWT是一種基於JS ...


JWT的簡單使用

介紹

當今Web開發中,API的使用越來越廣泛,而API的安全性也變得越來越重要。其中,JWT(JSON Web Token)鑒權和授權是一種常見的解決方案。

本篇文章將會介紹JWT鑒權和授權的原理、實現方式以及註意事項。

什麼是JWT?

JWT是一種基於JSON格式的開放標準(RFC7519),用於在網路上傳遞聲明信息的一種簡潔、自包含的安全方式。JWT通常被用來在各個系統之間傳遞身份認證信息和用戶授權信息。

安裝相關 NuGet 包

在開始使用 JWT 進行授權鑒權之前,需要先安裝 Microsoft.AspNetCore.Authentication.JwtBearer NuGet 包。可以使用 Visual Studio 的 NuGet 管理器或者命令行工具進行安裝。

Install-Package Microsoft.AspNetCore.Authentication.JwtBearer

JWT的組成部分

JWT由三個部分組成:Header(頭部)、Payload(負載)和Signature(簽名)。

頭部(Header)

頭部通常由兩部分組成:令牌類型(即JWT)和指定該令牌所使用的簽名演算法。例如:

{
  "alg": "HS256",
  "typ": "JWT"
}
負載(Payload)

負載通常包含了需要傳遞的聲明信息,聲明信息由鍵值對組成。例如:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

其中,“sub”表示主題(subject),可以是用戶ID或其他標識符;“name”表示用戶名;“iat”表示令牌發行時間。

簽名(Signature)

簽名是對Header和Payload的內容進行數字簽名後得到的一串字元串。簽名用於驗證JWT是否被篡改或偽造。例如:

HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)

其中,“secret”為使用該令牌的伺服器端保存的密鑰。

JWT的優點

  • 簡潔:由於JWT採用了JSON格式,而JSON是一種輕量級的數據格式,因此JWT非常適合在多個服務之間傳遞信息。
  • 自包含:JWT包含了所有必要的信息,因此不需要像Session那樣在伺服器端存儲用戶狀態。
  • 安全:JWT使用數字簽名來保證消息完整性和真實性,並可以對負載進行加密處理。

JWT鑒權

JWT鑒權是指通過JWT來驗證用戶身份和許可權。在使用JWT鑒權時,客戶端將用戶憑證(例如用戶名和密碼)發送給伺服器,在伺服器驗證用戶憑證有效後,生成一個JWT並將其返回給客戶端。客戶端在以後的請求中攜帶這個JWT,伺服器通過驗證JWT的簽名和有效期等信息來驗證用戶身份和授權信息。

JWT鑒權流程
  1. 用戶登錄,向伺服器提交身份憑證(例如用戶名、密碼)。
  2. 伺服器驗證身份憑證的有效性。
  3. 如果身份憑證有效,伺服器生成一個JWT並將其返回給客戶端。
  4. 客戶端在以後的請求中攜帶JWT。
  5. 伺服器從JWT中解析出用戶ID等信息,並根據信息來驗證用戶身份和授權信息。
JWT鑒權實現
配置appsettings.json

我們需要在appsettings.json文件中配置JWT的相關信息。在您的ASP.NET Core項目中,找到appsettings.json文件,並添加以下配置:

"Authentication": {
    "SecretKey": "yourIssuer",
    "Issuer": "yourAudience",
    "Audience": "yourSecretKey"
  },
添加 JWT 鑒權服務

在ASP.NET Core中,可以使用JwtBearer認證方案來驗證JWT。首先,在Startup.cs文件中添加以下代碼:

 public void ConfigureServices(IServiceCollection services)
{
    // 添加JWT身份驗證服務
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
         .AddJwtBearer(options =>
         {
             var secretByte = Encoding.UTF8.GetBytes(Configuration["Authentication:SecretKey"]);  // 從appsettings.json讀取Jwt配置
             options.TokenValidationParameters = new TokenValidationParameters()
             {
                 ValidateIssuer = true,
                 ValidIssuer = Configuration["Authentication:Issuer"],

                 ValidateAudience = true,
                 ValidAudience = Configuration["Authentication:Audience"],

                 ValidateLifetime = true,
            
                 ValidateIssuerSigningKey = true,
                 IssuerSigningKey = new SymmetricSecurityKey(secretByte)
             };
         });
    // 其他服務配置
}


public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 其他中間件配置...

    app.UseAuthentication();
    app.UseAuthorization();
}

上面代碼中,我們向依賴註入容器中註冊了一個身份驗證方案,名稱為 JwtBearerDefaults.AuthenticationScheme,表示使用 JWT 進行身份驗證。然後,我們使用 AddJwtBearer 擴展方法,將 JWT 鑒權服務添加到應用程式中。

在 AddJwtBearer 方法中,我們需要配置 TokenValidationParameters 來驗證 JWT。其中,ValidateIssuer、ValidIssuer、ValidateAudience、ValidAudience 和 ValidateLifetime 屬性用於驗證 JWT 中的發行人、接收方、有效期等信息。IssuerSigningKey 屬性表示密鑰,用於對 JWT 進行數字簽名。最後,ValidateIssuerSigningKey 屬性用於驗證 JWT 的簽名是否正確。

生成JWT

在ASP.NET Core中,可以使用JwtSecurityToken類來創建JWT。例如:

var signingAlgorithm = SecurityAlgorithms.HmacSha256;

var claims = new[]
{
    new Claim(JwtRegisteredClaimNames.Sub,"user_id"),
    new Claim(ClaimTypes.Role,"Admin"),
    new Claim("UserId","12"),
};

var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
var signingKey = new SymmetricSecurityKey(secretByte);
var signingCredentials = new SigningCredentials(signingKey, signingAlgorithm);

var token = new JwtSecurityToken(
    issuer: _configuration["Authentication:Issuer"],
    audience: _configuration["Authentication:Audience"],
    claims,
    notBefore: DateTime.UtcNow,
    expires: DateTime.UtcNow.AddDays(1),
    signingCredentials
);

var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);

在這裡,我們首先創建了一個聲明(Claims)列表,其中包含用戶ID、角色信息。然後,我們指定了JWT的過期時間和簽名演算法,並使用SymmetricSecurityKey類來指定密鑰。最後,我們使用JwtSecurityTokenHandler類將token轉換為字元串形式的jwt。

驗證JWT
public bool ValidateAccessToken(string token)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.UTF8.GetBytes(_jwtConfig.SecretKey);
        try
        {
            tokenHandler.ValidateToken(token, new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = _jwtConfig.Issuer,
                ValidAudience = _jwtConfig.Audience,
                IssuerSigningKey = new SymmetricSecurityKey(key)
            }, out var validatedToken);
        }
        catch (Exception)
        {
            return false;
        }
        return true;
    }
}

上面代碼中,我們使用 JwtSecurityTokenHandler 類來驗證 JWT 的真實性和完整性。其中,我們使用 TokenValidationParameters 來配置驗證參數,包括是否驗證 JWT 發行人、接收方、有效期等信息,以及使用哪個密鑰對其進行數字簽名。如果驗證通過,則返回 true,否則返回 false。

JWT授權

JWT授權是指根據JWT中包含的聲明信息來驗證用戶是否具有訪問特定資源的許可權。在使用JWT授權時,我們在JWT中添加了一些聲明信息,例如用戶所屬角色、許可權等,伺服器可以通過這些信息來驗證用戶是否有權訪問特定資源。

JWT授權流程
  1. 用戶登錄,向伺服器提交身份憑證(例如用戶名、密碼)。
  2. 伺服器驗證身份憑證的有效性。
  3. 如果身份憑證有效,伺服器生成一個JWT並將其返回給客戶端。
  4. 客戶端在以後的請求中攜帶JWT。
  5. 伺服器從JWT中解析出用戶信息和聲明信息,並根據信息來驗證用戶是否有權訪問特定資源。
JWT授權實現
用戶登錄

創建傳入DTO:LoginDto 、返回DTO:LoginOutDto類

public class LoginDto
{
    public string UserName { get; set; }

    public string Pwd { get; set; }
}

public class LoginOutDto
{
    public int Code { get; set; }

    public string Msg { get; set; }

    public string access_token { get; set; }

    public string refresh_token { get; set; }

}

在用戶登錄時,我們需要對用戶提供的用戶名和密碼進行驗證,並生成訪問令牌和刷新令牌。下麵是一個簡單的示例,演示如何在ASP.NET Core中實現用戶登錄驗證,並生成JWT令牌。

[HttpPost("login")]
public LoginOutDto Login([FromBody] LoginDto input)
{
    var dto = new LoginOutDto();
    try
    {
        if (input.UserName != "admin" || input.Pwd != "bb123456")
        {
            dto.Code = 500;
            dto.Msg = "用戶名或密碼不正確";
            dto.access_token = string.Empty;
            return dto;
        }

        // 生成訪問令牌
        var accessToken = _jwtService.GenerateAccessToken();

        // 生成刷新令牌
        var refreshToken = _jwtService.GenerateRefreshToken();

        dto.Code = 200;
        dto.Msg = "登錄成功";
        dto.access_token = accessToken;
        dto.refresh_token = refreshToken;
        
    }
    catch (Exception ex)
    {

        dto.Code = 500;
        dto.Msg = "登錄失敗:" + ex.Message;
    }

    return dto;

}

在上面的示例中,我們通過調用_jwtService.GenerateAccessToken和_jwtService.GenerateRefreshToken方法來生成訪問令牌和刷新令牌,並將刷新令牌保存到資料庫或其他持久化存儲中,以便後續使用。

刷新令牌

在用戶登錄後,訪問令牌會在一定時間後過期,此時用戶需要使用刷新令牌來獲取新的訪問令牌,而無需重新登錄。下麵是一個簡單的示例,演示如何在ASP.NET Core中實現刷新令牌功能。

[HttpPost("refresh")]
public IActionResult RefreshToken(UserModel model)
{
    // 驗證刷新令牌是否有效
    var isValidRefreshToken = ValidateAccessToken(model.RefreshToken);

    if (!isValidRefreshToken)
    {
        return BadRequest(new { message = "Invalid refresh token" });
    }

    // 生成新的訪問令牌
    var accessToken = _jwtService.GenerateAccessToken(model);

    // 返回新的訪問令牌給客戶端
    return Ok(new
    {
        access_token = accessToken
    });
}

在上面的示例中,我們通過調用_jwtService.GenerateAccessToken方法來生成新的訪問令牌,並將其返回給客戶端。在生成新的訪問令牌時,我們可以使用之前保存的用戶信息,例如用戶名等

完整代碼

下麵是一個包含生成 JWT,解析 JWT,鑒權,授權和策略的完整示例。請註意,此示例僅供參考,請根據實際需求進行修改。

Startup.cs
public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
             .AddJwtBearer(options =>
             {
                 var secretByte = Encoding.UTF8.GetBytes(Configuration["Authentication:SecretKey"]);
                 options.TokenValidationParameters = new TokenValidationParameters()
                 {
                     ValidateIssuer = true,
                     ValidIssuer = Configuration["Authentication:Issuer"],

                     ValidateAudience = true,
                     ValidAudience = Configuration["Authentication:Audience"],

                     ValidateLifetime = true,

                     ValidateIssuerSigningKey = true,
                     IssuerSigningKey = new SymmetricSecurityKey(secretByte)
                 };
             });
        
        // 註入IJwtService服務
        services.AddSingleton<IJwtService, JwtService>();

        services.AddControllers();
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo { Title = "JWT.Demo", Version = "v1" });
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseSwagger();
            app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "JWT.Demo v1"));
        }

        app.UseHttpsRedirection();

        app.UseRouting();

        // 身份驗證
        app.UseAuthentication();

        // 授權
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}
IJwtService&JwtService
public interface IJwtService
{
    /// <summary>
    /// 生成JWT
    /// </summary>
    /// <returns></returns>
    string GenerateAccessToken();

    /// <summary>
    /// 刷新Token
    /// </summary>
    /// <returns></returns>
    string GenerateRefreshToken();

    /// <summary>
    /// 驗證JWT
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    bool ValidateAccessToken(string token);
}

public class JwtService : IJwtService
{
    private readonly IConfiguration _configuration;

    public JwtService(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    /// <summary>
    /// 生成JWT
    /// </summary>
    /// <returns></returns>
    public string GenerateAccessToken()
    {
        var signingAlgorithm = SecurityAlgorithms.HmacSha256;

        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub,"user_id"),
            new Claim(ClaimTypes.Role,"Admin"),
            new Claim("UserId","12"),
        };

        var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
        var signingKey = new SymmetricSecurityKey(secretByte);
        var signingCredentials = new SigningCredentials(signingKey, signingAlgorithm);

        var token = new JwtSecurityToken(
            issuer: _configuration["Authentication:Issuer"],
            audience: _configuration["Authentication:Audience"],
            claims,
            notBefore: DateTime.UtcNow,
            expires: DateTime.UtcNow.AddDays(1),
            signingCredentials
        );

        var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);

        return tokenStr;
    }

    /// <summary>
    /// 刷新Token
    /// </summary>
    /// <returns></returns>
    public string GenerateRefreshToken()
    {
        var randomNumber = new byte[32];
        using (var rng = new RNGCryptoServiceProvider())
        {
            rng.GetBytes(randomNumber);
            return Convert.ToBase64String(randomNumber);
        }
    }

    /// <summary>
    /// 驗證JWT
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    public bool ValidateAccessToken(string token)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
        try
        {
            tokenHandler.ValidateToken(token, new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = _configuration["Authentication:Issuer"],
                ValidAudience = _configuration["Authentication:Audience"],
                IssuerSigningKey = new SymmetricSecurityKey(key)
            }, out var validatedToken);
        }
        catch (Exception)
        {
            return false;
        }
        return true;
    }
}
AuthenticateController.cs
[ApiController]
[Route("auth")]
public class AuthenticateController : ControllerBase
{
    private readonly IConfiguration _configuration;
    private readonly IJwtService _jwtService;

    public AuthenticateController(IConfiguration configuration, IJwtService jwtService)
    {
        _configuration = configuration;
        _jwtService = jwtService;
    }

    [HttpPost("login")]
    public LoginOutDto Login([FromBody] LoginDto input)
    {
        var dto = new LoginOutDto();
        try
        {
            if (input.UserName != "admin" || input.Pwd != "bb123456")
            {
                dto.Code = 500;
                dto.Msg = "用戶名或密碼不正確";
                dto.access_token = string.Empty;
                return dto;
            }

            // 生成訪問令牌
            var accessToken = _jwtService.GenerateAccessToken();

            // 生成刷新令牌
            var refreshToken = _jwtService.GenerateRefreshToken();

            dto.Code = 200;
            dto.Msg = "登錄成功";
            dto.access_token = accessToken;
            dto.refresh_token = refreshToken;
            
        }
        catch (Exception ex)
        {

            dto.Code = 500;
            dto.Msg = "登錄失敗:" + ex.Message;
        }

        return dto;

    }

    [HttpPost("refresh")]
    public IActionResult RefreshToken(string token)
    {
        // 驗證刷新令牌是否有效
        var isValidRefreshToken = _jwtService.ValidateAccessToken(token);

        if (!isValidRefreshToken)
        {
            return BadRequest(new { message = "Invalid refresh token" });
        }

        // 生成新的訪問令牌
        var accessToken = _jwtService.GenerateAccessToken();

        // 返回新的訪問令牌給客戶端
        return Ok(new
        {
            access_token = accessToken
        });
    }
}

註意事項

  • 密鑰管理:在使用JWT時,密鑰是非常重要的,泄露密鑰會導致安全問題。因此,密鑰的生成、存儲和更新都必須謹慎處理。

  • 過期時間:在生成JWT時,要指定合適的過期時間,避免JWT過期後仍然可以使用。

  • 簽名演算法:簽名演算法的選擇很重要,不同的簽名演算法具有不同的安全性和效率。建議採用HMAC+

  • SHA256或RSA演算法。

    • 不要存儲敏感信息:JWT雖然安全,但仍然存在被盜用的可能性。因此,在生成JWT時,應避免將敏感信息(例如密碼、信用卡號等)存儲在負載中。
    • 使用HTTPS:在使用JWT時,建議採用HTTPS協議來保證通訊的安全性。
    • 謹慎處理“記住我”功能:在實現“記住我”功能時,需要謹慎處理,避免密鑰泄露或用戶憑證被盜用。

結論

在.NET 5 中使用 JWT 進行授權鑒權是一種安全、可靠的身份驗證方式。通過添加 JWT 鑒權服務、使用 Authorize 屬性啟用 JWT 授權、生成和驗證 JWT、使用 UseAuthentication 和 UseAuthorization 中間件來啟用身份驗證和授權,併為不同的 API 設置不同的授權策略,可以輕鬆地實現 JWT 的授權鑒權功能。


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

-Advertisement-
Play Games
更多相關文章
  • QWeb簡史 到目前為止,我們的房地產模塊的界面設計相當有限。構建列表視圖很簡單,因為只需要欄位列表。表單視圖也是如此:儘管使用了一些標記,如<group>或<page>,但在設計方面幾乎沒有什麼可做的。 然而,如果我們想給我們的應用程式一個獨特的外觀,就必須更進一步,能夠設計新的視圖。此外,PDF ...
  • The Types of Associations 在 Rails 中,可以通過 ActiveRecord 來定義不同類型的關聯關係(Associations),包括以下幾種: belongs_to:表示該模型 belongs_to 另一個模型,即該模型擁有一個外鍵(foreign key)指向另一 ...
  • springBoot自定義cron表達式註冊定時任務 一、原理 1、使用Spring自帶的TaskScheduler註冊任務 2、註冊後返回:ScheduledFuture,用於取消定時任務 3、註冊任務後不會馬上取消任務,所以將任務緩存。在需要取消任務的時候調用取消介面取消 4、cron表達式可以 ...
  • 本篇介紹了JVM中垃圾回收器相關的基礎知識,後續會深入介紹CMS、G1、ZGC等不同垃圾收集器的運作流程和原理,歡迎關註。 ...
  • 這輩子不想再看到jedisBrokenPipe!! 測試環境運行16天後報錯信息: 05:42:32.629 [http-nio-8093-exec-2] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - [log,175] - Servlet.service( ...
  • chatGPT正式發佈已經有段時間了,這段時間我也深度體驗了chatGPT的魅力。 OpenAI除了提供網頁版的chatGPT,還通過api的形式提供了很多其它服務,包括文字糾錯、圖片生成、音頻轉換等等。 作為程式員,即使有現成的openai庫,但還是免不了想自己造輪子,所以就有這個openai庫。 ...
  • "If debugging is the process of removing software bugs, then programming must be the process of putting them in." - Edsger Dijkstra “如果調試是消除軟體Bug的過程,那 ...
  • 教程簡介 Windows通訊開發平臺(Windows Communication Foundation,簡稱WCF)是由微軟開發的一系列支持數據通信的應用程式框架,可以翻譯為Windows通訊開發平臺。 整合了原有的windows通訊的 .net Remoting,WebService,Socket ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...