.NET JWT入坑

news2024/11/24 14:32:15

前言

JWT (JSON Web Token) 是一种安全传输信息的开放标准,由Header、Payload和Signature三部分组成。它主要用于身份验证、信息交换和授权。JWT可验证用户身份,确保访问权限,实现单点登录,并在客户端和服务器之间安全地交换信息。因其简单、安全和便捷,JWT在现代Web应用中广泛使用。

用JWT有多个原因:

  1. 无状态的身份验证:JWT允许服务器无需保存用户的会话信息,因为所有必要的信息都存储在令牌本身中。这降低了服务器的存储需求,并提高了系统的可扩展性。

  2. 跨域身份验证:由于JWT是自我包含的,并且可以被轻松传递,因此它非常适合跨域身份验证。用户可以在一个服务上验证身份,然后使用相同的令牌访问另一个服务,从而实现单点登录(SSO)。

  3. 安全性:JWT可以通过使用强大的加密算法(如HS256, RS256等)进行签名,以确保其完整性和真实性。服务器可以使用公钥验证令牌的签名,从而确保它没有被篡改,并且确实是由受信任的颁发者签发的。

  4. 减少数据库查询:由于用户的身份信息都存储在JWT中,服务器无需每次都去数据库中查询用户的身份信息,从而减少了数据库的压力和查询时间。

  5. 可定制性:JWT的Payload部分可以包含自定义的信息,如用户角色、权限等,这使得JWT非常灵活,并可以根据具体需求进行定制。

  6. 易于分发和共享:JWT可以轻松地通过网络传输,并且可以在多个服务和客户端之间共享,这使得它在微服务架构和分布式系统中非常有用。

  7. 标准化和互操作性:JWT是一个开放标准(RFC 7519),这意味着不同的系统和语言都可以使用相同的方式生成和验证JWT,从而提高了系统的互操作性。

环境 Win10  VS2022  .NET8 

✨ 建立项目jwttest

1.创建TestJwtController

2.下载JWT 

3.建实体类

/// <summary>
/// 用户信息类
/// </summary>
public class LoginRs
{
    /// <summary>
    /// 用户ID
    /// </summary>
    public string UserId { get; set; }
   /// <summary>
   /// 用户密码
   /// </summary>
    public string PasswordMD5 { get; set; }
}
/// <summary>
/// 用户登录信息类
/// </summary>
public class LoginInfo
{
    /// <summary>
    /// 用户信息
    /// </summary>
    public string UserId { get; set; }
    /// <summary>
    /// 检验时间
    /// </summary>
    public DateTime Expires { get; set; }
}
/// <summary>
/// rsmodel
/// </summary>
public class RsModel
{
    /// <summary>
    /// 是否成功
    /// </summary>
    public bool isOk { get; set; }
    /// <summary>
    /// 返回值
    /// </summary>
    public int code { get; set; }
    /// <summary>
    /// 返回消息
    /// </summary>
    public string msg { get; set; }
    /// <summary>
    /// 返回数据  
    /// </summary>
    public object rsData { get; set; }


}

4.添加post login

      // POST api/<ValuesController>
      [HttpPost]
      public string Login([FromBody] LoginRs loginRequest)
      {
          if (loginRequest == null) return JsonConvert.SerializeObject(new RsModel() { code = 0, isOk = false, msg = "登录信息为空!" });

          #region  判断userid pwd

          if (loginRequest.UserId != "admin" || loginRequest.PasswordMD5 != "admin")
          {
              return JsonConvert.SerializeObject(new RsModel() { code = 0, isOk = false, msg = "用户名和密码不正确!" });
          }
          #endregion
          LoginInfo Info = new LoginInfo()
          {
              UserId = loginRequest.UserId,
              Expires = DateTime.Now.AddDays(1)
          };
          const string secretKey = "myseckey";//口令加密秘钥
          byte[] key = Encoding.UTF8.GetBytes(secretKey);
          IJwtAlgorithm algorithm = new HMACSHA256Algorithm();//加密方式
          IJsonSerializer serializer = new JsonNetSerializer();//序列化Json
          IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();//base64加解密
          IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);//JWT编码
          var token = encoder.Encode(Info, key);//生成令牌

          return JsonConvert.SerializeObject(new RsModel() { code = 1, isOk = true, rsData = token, msg = "登录成功!" });
      }

5.登录验证

这里使用swagger方便检验    👉    .NET MVC API Swagger入坑

6.测试没问题,写个JwtHelper

 public static class JwtHelper
 {

     private static readonly string JwtKey = "mysecret";
     /// <summary>
     /// 获取加密解密
     /// </summary>
     /// <returns></returns>
     private static IJwtEncoder GetEncoder()
     {
         IJwtAlgorithm algorithm = new HMACSHA256Algorithm();//加密方式
         IJsonSerializer serializer = new JsonNetSerializer();//序列化Json
         IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();//base64加解密
         IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);//JWT编码
         return encoder;
     }

     /// <summary>
     /// 获取解密密钥
     /// </summary>
     /// <returns></returns>
     private static IJwtDecoder GetDecoder()
     {
         IJsonSerializer serializer = new JsonNetSerializer();
         IDateTimeProvider provider = new UtcDateTimeProvider();
         IJwtValidator validator = new JwtValidator(serializer, provider);
         IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
         IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
         IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
         return decoder;
     }

     /// <summary>
     /// 加密
     /// </summary>
     public static string Encode(object payload)
     {
         var encoder = GetEncoder();
         var token = encoder.Encode(payload, JwtKey);
         return token;
     }

     /// <summary>
     /// 解密
     /// </summary>
     public static T Decode<T>(string token)
     {
         var decoder = GetDecoder();
         var data = decoder.Decode(token, JwtKey);
         var res = JsonConvert.DeserializeObject<T>(data);
         return res;
     }

     /// <summary>
     /// 解密,只返回Json文本
     /// </summary>
     /// <param name="token"></param>
     /// <returns></returns>
     public static string Decode(string token)
     {

         var decoder = GetDecoder();
         var data = decoder.Decode(token, JwtKey);
         return data;
     }

 }

把中间的加密算法替换成helper的Encode

   var token = JwtHelper.Encode(Info);

7.添加token加密类

调用Decode方法 解密token

8.测试JWT

{"isOk":true,"code":1,"msg":"登录成功!","rsData":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJVc2VySWQiOiJhZG1pbiIsIkV4cGlyZXMiOiIyMDI0LTA0LTEwVDAxOjUxOjUwLjk5NDgxNzQrMDA6MDAifQ.eYJovquJFezVhfdLp-Hro2vnMoQsUwgXLkYcZSYEy7U"}

测试解密

解密成功

⭐️JwtBearer

9、添加NuGet包Microsoft.AspNetCore.Authentication.JwtBearer

10、在appsettings.json中添加JWT配置节点

  "JWT": {
    "SecKey": "im6666666!#@$%@%^^&*(~Czmjklneafguvioszb%yuv&*6WVDf5dw#5dfw6f5w6faW%FW^f5wa65f^AWf56", //密钥
    "Issuer": "im666", //发行者
    "ExpireSeconds": 7200 //过期时间 2h
  },

11.添加jwt类


    using Microsoft.IdentityModel.Tokens;
    using System.Diagnostics;
    using System.IdentityModel.Tokens.Jwt;
    using System.Security.Claims;
    using System.Text;

namespace webapijwttest.Models
{
    /// <summary>
    /// 授权JWT类
    /// </summary>
    public class JwtHelper2
        {
            private readonly IConfiguration _configuration;
            /// <summary>
            /// Token配置
            /// </summary>
            /// <param name="configuration"></param>
            public JwtHelper2(IConfiguration configuration)
            {
                _configuration = configuration;
            }
            /// <summary>
            /// 创建Token 这里面可以保存自己想要的信息
            /// </summary>
            /// <param name="username"></param>
            /// <param name="mobile"></param>
            /// <returns></returns>
            public string CreateToken(string username, string mobile)
            {
                try
                {
                    // 1. 定义需要使用到的Claims
                    var claims = new[]
                    {
                    new Claim("username", username),
                    new Claim("mobile", mobile),
                    /* 可以保存自己想要信息,传参进来即可
                    new Claim("sex", "sex"),
                    new Claim("limit", "limit"),
                    new Claim("head_url", "xxxxx")
                    */
                };
                    // 2. 从 appsettings.json 中读取SecretKey
                    var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecKey"]));
                    // 3. 选择加密算法
                    var algorithm = SecurityAlgorithms.HmacSha256;
                    // 4. 生成Credentials
                    var signingCredentials = new SigningCredentials(secretKey, algorithm);
                    // 5. 根据以上,生成token
                    var jwtSecurityToken = new JwtSecurityToken(
                        _configuration["Jwt:Issuer"],    //Issuer
                        _configuration["Jwt:ExpireSeconds"],  //ExpireSeconds
                        claims,                          //Claims,
                        DateTime.Now,                    //notBefore
                        DateTime.Now.AddSeconds(30),     //expires
                        signingCredentials               //Credentials
                    );
                    // 6. 将token变为string
                    var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
                    return token;
                }
                catch (Exception)
                {
                    throw;
                }
            }
            /// <summary>
            /// 获取信息
            /// </summary>
            /// <param name="jwt"></param>
            /// <returns></returns>
            public static string ReaderToken(string jwt)
            {
                var str = string.Empty;
                try
                {
                    //获取Token的三种方式
                    //第一种直接用JwtSecurityTokenHandler提供的read方法
                    var jwtHander = new JwtSecurityTokenHandler();
                    JwtSecurityToken jwtSecurityToken = jwtHander.ReadJwtToken(jwt);
                    str = jwtSecurityToken.ToString();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }
                return str;
            }
            /// <summary>
            /// 解密jwt
            /// </summary>
            /// <param name="jwt"></param>
            /// <returns></returns>
            public string JwtDecrypt(string jwt)
            {
                StringBuilder sb = new StringBuilder();
                try
                {
                    JwtSecurityTokenHandler tokenHandler = new();
                    TokenValidationParameters valParam = new();
                    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecKey"]));
                    valParam.IssuerSigningKey = securityKey;
                    valParam.ValidateIssuer = false;
                    valParam.ValidateAudience = false;
                    //解密
                    ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwt,
                            valParam, out SecurityToken secToken);
                    foreach (var claim in claimsPrincipal.Claims)
                    {
                        sb.Append($"{claim.Type}={claim.Value}");
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }
                return sb.ToString();
            }
        }
}

12.Program.cs注册JWT服务

#region JWT服务
// 注册JWT服务
builder.Services.AddSingleton(new JwtHelper2(builder.Configuration));
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateIssuer = true, //是否验证Issuer
        ValidIssuer = builder.Configuration["Jwt:Issuer"], //发行人Issuer
        ValidateAudience = false, //是否验证Audience      
        ValidateIssuerSigningKey = true, //是否验证SecurityKey
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecKey"])), //SecurityKey
        ValidateLifetime = true, //是否验证失效时间
        ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值,解决服务器端时间不同步问题(秒)
        RequireExpirationTime = true,
    };
}
);
#endregion

添加swagger authorization


builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Web API", Version = "v1" });
    //开启注释
    var xmlFile = $"{Assembly.GetEntryAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);//需要 生成 目录生成XML
    c.IncludeXmlComments(xmlPath, true);
    // 配置 JWT Bearer 授权
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "JWT Authorization header using the Bearer scheme",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.Http,
        Scheme = "bearer"
    });
    var securityScheme = new OpenApiSecurityScheme
    {
        Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
    };
    var securityRequirement = new OpenApiSecurityRequirement { { securityScheme, new string[] { } } };
    c.AddSecurityRequirement(securityRequirement);




});


var app = builder.Build();


//启用验证中间件
app.UseAuthentication();
app.UseAuthorization();

13.添加jwt测试api

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity.Data;
using Microsoft.AspNetCore.Mvc;
using webapijwttest.Models;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace webapijwttest.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class Jwt2Controller : ControllerBase
    {
        private readonly JwtHelper2 _jwt;
        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="jwtHelper"></param>
        public Jwt2Controller(JwtHelper2 jwtHelper)
        {
            _jwt = jwtHelper;
        }
        /// <summary>
        /// 获取Token
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public IActionResult GetToken(LoginRs user)
        {
            //参数验证等等....
            if (string.IsNullOrEmpty(user.UserId))
            {
                return Ok("参数异常!");
            }
            //这里可以连接mysql数据库做账号密码验证
            //这里可以做Redis缓存验证等等
            //这里获取Token,当然,这里也可以选择传结构体过去
            var token = _jwt.CreateToken(user.UserId, user.PasswordMD5);
            //解密后的Token
            var PWToken = _jwt.JwtDecrypt(token);
            return Ok(token + "解密后:" + PWToken);
        }
        /// <summary>
        /// 获取自己的详细信息,其中 [Authorize] 就表示要带Token才行
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Authorize]
        public IActionResult GetSelfInfo()
        {
            //执行到这里,就表示已经验证授权通过了
            /*
             * 这里返回个人信息有两种方式
             * 第一种:从Header中的Token信息反向解析出用户账号,再从数据库中查找返回
             * 第二种:从Header中的Token信息反向解析出用户账号信息直接返回,当然,在前面创建        Token时,要保存进使用到的Claims中。
            */
            return Ok("授权通过了!");
        }
    }
}

调用

检测控制器

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using webapijwttest.Models;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace webapijwttest.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthController : ControllerBase
    {
        private ILogger<AuthController> _logger = null;
        private JwtHelper2 _iJWTService = null;
        private readonly IConfiguration _configuration;

        public AuthController(ILogger<AuthController> logger, JwtHelper2 jWTService, IConfiguration configuration)
        {
            this._logger = logger;
            _iJWTService = jWTService;
            _configuration = configuration;
        }

        [Route("Get")]
        [HttpGet]
        public IEnumerable<int> Get()
        {//未加授权认证
            return new List<int>() { 1, 3, 5, 7, 9 };
        }

        [Route("GetData")]
        [HttpGet]
        [Authorize]
        public List<object> GetData()
        {//添加了授权认证,需要使用token
            return new List<object>() { new { userName = "123", remark = "1234" } };
        }

        [Route("Login")]
        [HttpGet]
        public string Login(string name, string password)
        {
            if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(password))
            {
                string token = this._iJWTService.CreateToken(name,password);
                return JsonConvert.SerializeObject(new { result = true, token });
            }
            else
            {
                return JsonConvert.SerializeObject(new { result = false, token = "" });
            }
        }


    }
}

调用

把token放进 

测试GetData

当超过时间调用则GetData失败

END🐟🐟🐟

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1589906.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HarmonyOS实战开发-音视频录制、如何实现音频录制和视频录制功能的应用

介绍 音视频录制应用是基于AVRecorder接口开发的实现音频录制和视频录制功能的应用&#xff0c;音视频录制的主要工作是捕获音频信号&#xff0c;接收视频信号&#xff0c;完成音视频编码并保存到文件中&#xff0c;帮助开发者轻松实现音视频录制功能&#xff0c;包括开始录制…

【JavaWeb】异步请求——AJAX

目录 Ajax&#xff08;Asynchronous JavaScript and XML&#xff09;优点传统Web与Ajax的差异Ajax工作流程Ajax 经典应用场景XMLHttpRequest常用方法事件常用属性 ajax: GET请求和POST请求的区别 传统Ajax实现传统方式实现Ajax的不足 $.ajax()语法常用属性参数常用函数参数 Aja…

Docker 镜像仓库常见命令

Docker Registry (镜像仓库) 常用命令 docker login 功能&#xff1a;登录到一个 Docker 镜像仓库&#xff0c;如果没有指定镜像仓库的地址&#xff0c;默认就是官方的 Docker Hub 仓库。 语法&#xff1a; docker login [options] [server]选项&#xff1a; -u&#xff1a;登…

Missing artifact org.opencv:opencv:jar:4.10.0 [opencv-4.10.0.jar]

Missing artifact org.opencv:opencv:jar:4.10.0 [opencv-4.10.0.jar] https://mvnrepository.com/artifact/org.opencv/opencv 根本就没有 找了个旧项目的opencv-410.jar修改下opencv-4.10.0.jar放到目录下面就好了 D:\localRepository\org\opencv\opencv\4.10.0 OpenCV-C…

Failed to delete XXXX.jar

Failed to delete XXXX.jar 问题&#xff1a;idea控制台报Failed to clean project:Failed to delete idea中点击maven->对应pom->lifecycle->clean时&#xff0c;报错 原因&#xff1a;target文件可能时编译的文件被其他程序占用&#xff0c;导致资源无法回收 解…

u盘为什么一插上电脑就蓝屏,u盘一插电脑就蓝屏

u盘之前还好好的&#xff0c;可以传输文件&#xff0c;使用正常&#xff0c;但是最近使用时却出现问题了。只要将u盘一插入电脑&#xff0c;电脑就显示蓝屏。u盘为什么一插上电脑就蓝屏呢?一般&#xff0c;导致的原因有以下几种。一&#xff0c;主板的SATA或IDE控制器驱动损坏…

农业现代化:UWB模块为农业领域带来的效益和便利

随着科技的进步和农业现代化的推进&#xff0c;超宽带&#xff08;UWB&#xff09;技术正逐渐在农业领域发挥重要作用。UWB模块作为UWB技术的核心组成部分&#xff0c;具有高精度、实时性强的特点&#xff0c;为农业生产提供了新的技术手段和解决方案。本文将探讨UWB模块在农业…

WSL访问adb usb device

1.Windows上用PowerShell运行&#xff1a; winget install --interactive --exact dorssel.usbipd-win 2.在WSLUbuntu上终端运行&#xff1a; sudo apt install linux-tools-generic hwdata sudo update-alternatives --install /usr/local/bin/usbip usbip /usr/lib/linux-too…

最优算法100例之44-不用加减乘除做加法

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 不用加减乘除做加法 题解报告 最优解法:使用异或 1)异或是查看两个数哪些二进制位只有一个为1,这些是非进位位,可以直接…

界面控件DevExpress WinForms/WPF v23.2 - 富文本编辑器支持内容控件

众所周知内容控件是交互式UI元素(文本字段、下拉列表、日期选择器)&#xff0c;用于在屏幕上输入和管理信息。内容控件通常在模板/表单中使用&#xff0c;以标准化文档格式和简化数据输入。DevExpress文字处理产品库&#xff08;Word Processing Document API、WinForm和WPF富文…

(文章复现)考虑网络动态重构的分布式电源选址定容优化方法

参考文献&#xff1a; [1]朱俊澎,顾伟,张韩旦,等.考虑网络动态重构的分布式电源选址定容优化方法[J].电力系统自动化,2018,42(05):111-119. 1.摘要 以投资周期经济收益最高为目标&#xff0c;基于二阶锥规划提出了一种考虑网络动态重构的分布式电源选址定容优化方法。首先&am…

【Proteus仿真】按键控制LED流水灯定时器时钟

0~65535 每隔1us计数加1 总共定时时间65535us 64535离计数器溢出差值1000&#xff0c;所以计时时间为1ms #include <REGX51.H> void inittimer0() {TMOD0x01;//0000 0001TF00;//SCON可位寻址&#xff0c;TF1产生中断TR01;//定时器启动TL064535%256;//定时1msTH064536/256…

OSPF的P2P和Broadcast

OSPF为什么会有P2P和BROADCAST两种类型 OSPF&#xff08;开放最短路径优先&#xff09;协议中存在P2P&#xff08;点对点&#xff09;和BROADCAST&#xff08;广播多路访问&#xff09;两种网络类型&#xff0c;主要是为了适应不同类型的网络环境和需求。具体分析如下&#xf…

云原生(八)、Kubernetes基础(一)

K8S 基础 # 获取登录令牌 kubectl create token admin --namespace kubernetes-dashboard1、 NameSpace Kubernetes 启动时会创建四个初始名字空间 default:Kubernetes 包含这个名字空间&#xff0c;以便于你无需创建新的名字空间即可开始使用新集群。 kube-node-lease: 该…

飞书API(3):Python 自动读取多维表所有分页数据的三种方法

上一小节介绍了怎么使用 Python 读取多维表的数据&#xff0c;看似可以成功获取到了所有的数据&#xff0c;但是在实际生产使用过程中&#xff0c;我们会发现&#xff0c;上一小节的代码并不能获取到所有的多维表数据&#xff0c;它只能获取一页&#xff0c;默认是第一页。因为…

算法打卡day33

今日任务&#xff1a; 1&#xff09;509. 斐波那契数 2&#xff09;70. 爬楼梯 3&#xff09;746.使用最小花费爬楼梯 509. 斐波那契数 题目链接&#xff1a;509. 斐波那契数 - 力扣&#xff08;LeetCode&#xff09; 斐波那契数&#xff0c;通常用 F(n) 表示&#xff0c;形成…

分布式技术---------------消息队列中间件之 Kafka

目录 一、Kafka 概述 1.1为什么需要消息队列&#xff08;MQ&#xff09; 1.2使用消息队列的好处 1.2.1解耦 1.2.2可恢复性 1.2.3缓冲 1.2.4灵活性 & 峰值处理能力 1.2.5异步通信 1.3消息队列的两种模式 1.3.1点对点模式&#xff08;一对一&#xff0c;消费者主动…

CMake构建OpenCv并导入QT项目过程中出现的问题汇总

前言 再此之前请确保你的环境变量是否配置&#xff0c;这是总共需要配置的环境变量 E:\cmake\bin E:\OpenCv\opencv\build\x64\vc15\bin F:\Qt\Tools\mingw730_64\bin F:\Qt\5.12.4\mingw73_64\bin 问题一&#xff1a; CMake Error: CMake was unable to find a build program…

【网络】服务器间FTP传输文件被限速问题的排查(未达最优解)

服务器间FTP传输文件被限速问题的排查 问题描述具体问题软硬件环境文件传输方式的2种策略FTP相关信息问题表现问题解决结论 发散探讨——基于此问题进行发散研究相关知识从FileZilla软件入手从Windows入手从Linux入手从协议入手Windows和Linux的文件共享&#xff0c;分别是使用…

Spring Boot REST API - 项目实现

Spring Boot REST API - 项目实现 书接上文 Spring Boot REST API - CRUD 操作&#xff0c;一些和数据库相关联的注解在 [spring] spring jpa - hibernate CRUD 主要的 layer 如下&#xff1a; #mermaid-svg-4XoU1vfE9GEVVJpw {font-family:"trebuchet ms",verdana…