ASP.NET Core Web API 实现 JWT 身份验证

在ASP.NET Core WebApi中使用标识框架(Identity)-CSDN博客

因为一般需要和标识框架一起使用,建议先查看标识框架用法

一.为什么需要JWT

我们的系统需要实现认证,即服务端需要知道登录进来的客户端的身份,管理员有管理员的权限,普通用户有普通用户的权限.

但服务端是基于HTTP协议的,该协议本质上是无状态,两次请求本质上是独立的,也就是说该协议无法帮我们实现认证.

1、传统的 Session 认证机制

早期认证的实现方式是Session,流程大概如下:

(1)用户登录,服务端验证用户名密码成功后,生成一个唯一的 SessionId。

(2)这个 SessionId 存在服务端内存(或者数据库、Redis)里,对应一个用户状态。

(3)服务端通过 Set-Cookie 把 SessionId 写入浏览器 Cookie。

(4)浏览器后续请求自动携带 Cookie,服务端用这个 SessionId 找到用户信息。

Session 的缺点:

问题说明
服务器内存压力每登录一个用户,服务器都要保存一份 Session 数据,用户多了就容易撑爆内存
不适合分布式多台服务器集群部署时,Session 要么共享存储(如 Redis),要么做 Session 粘性路由,增加系统复杂度
跨域难处理前后端分离、跨域 API 调用时,Cookie 不好用或者需要复杂的 CORS 配置
状态管理复杂如果 Session 丢失、超时、清理,用户体验会很差,需要额外处理

2、JWT 的出现:无状态化认证 

随着微服务、云原生、前后端分离等架构兴起,开发者开始追求一种 「无状态」且「轻量级」 的认证方案,JWT 应运而生。

3、JWT 对比 Session 的核心区别

对比点SessionJWT
状态管理服务端有状态,需要存储每个用户 Session完全无状态,Token 自包含用户信息
存储位置服务端内存/数据库客户端自行保存(通常存在本地存储或 Cookie)
跨服务需要共享 Session 或做负载均衡粘性天然支持多服务,无需 Session 同步
扩展性横向扩展困难服务端可任意扩容
性能每次请求都查找 Session不需要查 Session,Token 自解密验证

4、JWT 的工作流程概览(无状态认证) 

(1)用户登录,后端生成 JWT 返回给前端。

(2)前端保存好 JWT

(3)每次 API 请求,前端把 JWT 放到 Authorization: Bearer 头里。

(4)后端中间件解析 JWT,验签,通过后即可认为该用户已登录。

服务端只负责「验签 + 解密」,不保存任何 Session 状态。

5、JWT 的优点

优点说明
跨服务、跨平台多服务架构天然支持,移动 App、Web 前端、第三方系统都可以用同一个 Token
减少服务器压力服务端无需保存登录状态
性能高每次只需做一次 Token 验证,无需 Session 查询
易与 CDN、API 网关等集成请求携带 Token,网关层即可完成鉴权
标准化基于开放标准 RFC7519,广泛支持,工具链成熟

6、为什么现在很多新项目都选择 JWT? 

  • 适合微服务

  • 适合前后端分离

  • 适合跨平台 App

  • 适合无状态、弹性伸缩的云架构

7、JWT 取代 Session,不是因为它绝对更好,而是因为它更「适应当代架构」

JWT 并不是完美无缺,它也有一些缺点,比如:

缺点说明
Token 无法主动失效如果用户登出或者权限变更,老 Token 依然有效(可通过 Token 黑名单、Token 版本号等方式绕过)
容易被盗用如果 Token 泄露,别人拿到 Token 就可以冒充用户
Token 较长JWT 体积大,不适合非常高频短连接场景

二.什么是 JWT?

JWT,全称 JSON Web Token,是一种开放标准(RFC 7519),用于在不同系统之间安全地传输信息。它是一种基于 JSON 格式、经过数字签名的数据令牌,主要应用于 身份认证信息交换 场景。


1.JWT 的核心用途

  1. 身份认证(Authentication)
    用户登录成功后,服务器生成一个包含用户身份信息的 JWT,返回给客户端。客户端后续每次请求,都带上这个 Token,服务器通过验证 Token,确认用户身份,无需重复登录。

  2. 信息交换(Information Exchange)
    系统之间可以通过 JWT 安全地交换一些加密或不可篡改的声明信息。

2.JWT 的结构:三段式组成

一个典型的 JWT 长这样(这是被算法处理过的):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwicm9sZSI6IkFkbWluIn0.NhJzHfJZKIo0FPWqGk92OukUjD0YPgXVyknzZoAW_2Y
 

被点号分隔成三段: 

(1) Header(头部)
指定 Token 的类型(通常是 JWT)以及签名所用的算法,比如 HS256

{"alg": "HS256","typ": "JWT"
}

(2) Payload(负载)
放具体的声明信息(Claims),比如用户 ID、用户名、角色、过期时间等。

{"userName": "admin","role": "Admin","exp": 1719820800
}

(3) Signature(签名)
防止篡改。由 Header、Payload 和一个 Secret 密钥(只有服务端知道),通过指定算法生成。

签名生成方式(以 HMAC-SHA256 为例):

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

三.控制台使用

1.环境搭建

先创建一个控制台程序生成JWT,需要安装JWT读写的NuGet包

System.IdentityModel.Tokens.Jwt

 2.生成JWT

using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
static void Main(string[] args)
{// 创建用户的 Claims 列表// Claim就代表一条用户信息。Claim有两个主要的属性:Type和Value,它们都是string类型的,Type代表用户信息的类型,Value代表用户信息的值。// Type属性可以是预定义的类型,如ClaimTypes.Name、ClaimTypes.Role等,也可以是自定义的类型。var claims = new List<Claim>();// 用户唯一标识,比如用户ID,这里用"6"做示例claims.Add(new Claim(ClaimTypes.NameIdentifier, "6"));// 用户姓名,这里是 "ZhangSan"claims.Add(new Claim(ClaimTypes.Name, "ZhangSan"));// 用户角色,注意:可以有多个角色声明claims.Add(new Claim(ClaimTypes.Role, "User"));claims.Add(new Claim(ClaimTypes.Role, "Admin"));// 自定义 Claim,比如扩展字段,这里自定义了一个 "jz" 字段claims.Add(new Claim("jz", "112233"));// 定义密钥字符串,生产环境一般放在配置文件,不要硬编码string key = "kjdfsjffd^kjfkfkds#dsffdsdsfd@fdsufdsfo33300";// 设置 Token 的过期时间,这里是 1 天后过期DateTime expires = DateTime.Now.AddDays(1);// 把密钥字符串转成字节数组byte[] secBytes = Encoding.UTF8.GetBytes(key);// 根据密钥生成对称加密安全密钥对象var secKey = new SymmetricSecurityKey(secBytes);// 指定签名算法,这里使用 HMAC-SHA256var credentials = new SigningCredentials(secKey, SecurityAlgorithms.HmacSha256Signature);// 创建 JWT Token 对象,包括:claims、过期时间、签名凭据var tokenDescriptor = new JwtSecurityToken(claims: claims,                   // 载荷:用户身份信息expires: expires,                 // 有效期signingCredentials: credentials   // 签名信息);// 把 JwtSecurityToken 对象序列化成最终的 Token 字符串string jwt = new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);// 输出 TokenConsole.WriteLine(jwt);
}
eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjYiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiWmhhbmdTYW4iLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOlsiVXNlciIsIkFkbWluIl0sImp6IjoiMTEyMjMzIiwiZXhwIjoxNzUwOTg5ODMwfQ.7sl9Y18uxGU-Xd9Ly3rfXKnKidBJ_ZZjPyZOwnTR_0c

3.JWT解析

Header 和 Payload 是明文的,只是做了 Base64Url 编码,不是加密

比如一个原始 Payload:

{"userName": "admin","role": "Admin","exp": 1719820800
}

 Base64Url 编码后就是一串字母数字:

eyJ1c2VyTmFtZSI6ImFkbWluIiwicm9sZSI6IkFkbWluIiwiZXhwIjoxNzE5ODIwODAwfQ

 JWT 三部分都是明文(Header 和 Payload 可直接 Base64Url 解码),JWT 的重点是防篡改而不是保密

所以不难理解,别人拿到你的JWT就可以冒充你.

jwt在线解密/加密 - JSON中文网json中文网致力于在中国推广json,json Web Tokens 是目前流行的跨域认证解决方案,json中文网提供jwt解密/加密工具,提供HS256、HS384和HS512等签名算法的编码和校验。https://www.json.cn/jwt 可以将得到的JWT直接放到jwt解析网站上就能解析出前两部分的信息.

或者使用下面这个方法

string jwt = Console.ReadLine()!;
string[] segments = jwt.Split('.');
string head = JwtDecode(segments[0]); // 头部
string payload = JwtDecode(segments[1]); // 负载
Console.WriteLine("--------head--------");
Console.WriteLine(head);
Console.WriteLine("--------payload--------");
Console.WriteLine(payload);string JwtDecode(string s)
{s = s.Replace('-', '+').Replace('_', '/');switch (s.Length % 4){case 2:s += "==";break;case 3:s += "=";break;}var bytes = Convert.FromBase64String(s); // 解码return Encoding.UTF8.GetString(bytes);
}

 可以看到信息被解析出来了,由于JWT会被发送到客户端,而负载中的内容是以明文形式保存的,因此一定不要把不能被客户端知道的信息放到负载中。

JWT的编码和解码规则都是公开的,而且负载部分的Claim信息也是明文的,因此恶意攻击者可以对负载部分中的用户ID等信息进行修改,从而冒充其他用户的身份来访问服务器上的资源。因此,服务器端需要对签名部分进行校验,从而检查JWT是否被篡改了。

// 从控制台读取用户输入的 JWT 字符串
string jwt = Console.ReadLine()!;  // 注意:加了 "!" 是为了告诉编译器:这里不会是 null// 定义密钥字符串(要与生成 JWT 时用的密钥保持一致,否则验证会失败)
string secKey = "kjdfsjffd^kjfkfkds#dsffdsdsfd@fdsufdsfo33300";// 创建一个 JWT Token 解析器
JwtSecurityTokenHandler tokenHandler = new();// 定义 Token 验证参数
TokenValidationParameters valParam = new();// 设置签名验证的密钥,必须和生成时一致
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secKey));
valParam.IssuerSigningKey = securityKey;// 不验证签发者 (Issuer),这里简化处理(生产环境可以启用校验)
valParam.ValidateIssuer = false;// 不验证接收者 (Audience),同样为了简化
valParam.ValidateAudience = false;// 开始验证 Token
// ValidateToken 方法会做:
// 1. 验证签名
// 2. 验证 Token 是否过期
// 3. 返回解析后的 ClaimsPrincipal 对象(包含用户身份信息)
// out 参数会返回原始的 SecurityToken 对象
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwt, valParam, out SecurityToken secToken);// 遍历解析出来的 Claim 列表,并输出每个 Claim 的类型和值
foreach (var claim in claimsPrincipal.Claims)
{Console.WriteLine($"{claim.Type}={claim.Value}");
}

如果篡改JWT,程序运行时就会抛出内容为“Signature validation failed”的异常。exp值是过期时间,如果收到过期的JWT,即使签名校验成功,ValidateToken方法也会抛出异常


四.WebApi中使用

1.环境准备

 "JWT": {"SigningKey": "kjdfsjffd^kjfkfkds#dsffdsdsfd@fdsufdsfo33300EXTRA","ExpireSeconds": "3600"}
   public class JwtSetting{public string SigningKey { get; set; }public int ExpireSeconds { get; set; }}

我们先在配置系统appsettings.json中配置一个名字为JWT的节点,并在节点下创建SigningKey、ExpireSeconds两个配置项,分别代表JWT的密钥和过期时间(单位为秒)。
我们再创建一个对应JWT节点的配置类JwtSetting,类中包含SigningKey、ExpireSeconds这两个属性。

安装Microsoft.AspNetCore.Authentication.JwtBearer包,这个包封装了简化ASP.NET Core中使用JWT的操作

 2.注册服务

// 将配置文件中的 JWT 部分绑定到 JwtSetting 配置类
builder.Services.Configure<JwtSetting>(builder.Configuration.GetSection("JWT"));// 注册 JWT Bearer 身份认证服务
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(x =>{// 从配置中读取 JWT 设置对象(比如密钥等信息)var jwtOpt = builder.Configuration.GetSection("JWT").Get<JwtSetting>();// 把密钥字符串转为字节数组byte[] keyBytes = Encoding.UTF8.GetBytes(jwtOpt.SigningKey);// 用密钥生成对称安全密钥对象var secKey = new SymmetricSecurityKey(keyBytes);// 配置 Token 验证参数x.TokenValidationParameters = new TokenValidationParameters(){// 是否验证 Token 的签发者(Issuer),这里关闭ValidateIssuer = false,// 是否验证 Token 的接收方(Audience),这里关闭ValidateAudience = false,// 是否验证 Token 的过期时间,生产环境一般要打开,这里关闭是为了开发方便ValidateLifetime = false,// 是否验证 Token 的签名,生产环境一定要开ValidateIssuerSigningKey = true,// 用来验证签名的密钥IssuerSigningKey = secKey};});

 本质上就是中间件,别忘了使用.

 3.给登录用户发JWT

// 控制器:负责处理用户登录请求,并生成 JWT Token
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{private readonly IOptions<JwtSetting> _jwtSetting;           // JWT 配置信息private readonly ILogger<AuthController > _logger;private readonly UserManager<User> _userManager;private readonly RoleManager<Role> _roleManager;public AuthController(ILogger<AuthController > logger, UserManager<User> userManager,RoleManager<Role> roleManager, IOptions<JwtSetting> jwtSetting){_logger = logger;_userManager = userManager;_roleManager = roleManager;_jwtSetting = jwtSetting;}// 登录接口,接收用户名密码,验证成功后生成 JWT[HttpPost]public async Task<IActionResult> Login(LoginRequest loginRequest){string userName = loginRequest.UserName;string password = loginRequest.Password;// 使用 Identity 框架查找用户var user = await _userManager.FindByNameAsync(userName);if (user == null){return BadRequest("用户不存在");}// 判断用户是否被锁定(连续登录失败导致)var islocked = await _userManager.IsLockedOutAsync(user);if (islocked){// 用户锁定,返回 400,提示锁定信息return BadRequest("用户已锁定!");}// 校验密码var success = await _userManager.CheckPasswordAsync(user, password);if (!success){// 密码错误,记录一次失败尝试(用于锁定机制)var r = await _userManager.AccessFailedAsync(user);if (!r.Succeeded){// 记录失败信息失败,返回错误return BadRequest("访问失败信息写入错误!");}else{// 普通密码错误返回 400return BadRequest("失败!");}                }//重置访问失败计数await _userManager.ResetAccessFailedCountAsync(user);// 构建 JWT Claims(载荷里的用户信息)var claims = new List<Claim>{// 用户唯一标识new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),// 用户名new Claim(ClaimTypes.Name, user.UserName)};// 查询用户角色,并把每个角色加入到 Claimsvar roles = await _userManager.GetRolesAsync(user);foreach (var role in roles){claims.Add(new Claim(ClaimTypes.Role, role));}// 调用封装好的 Token 构建方法,生成 JWT 字符串string jwtToken = BuildToken(claims, _jwtSetting.Value);// 把 Token 返回给前端return Ok(jwtToken);}/// <summary>/// 根据用户 Claims 和 JWT 配置,生成 JWT Token 字符串/// </summary>private static string BuildToken(IEnumerable<Claim> claims, JwtSetting _jwtSetting){// 设置 Token 过期时间DateTime expires = DateTime.Now.AddSeconds(_jwtSetting.ExpireSeconds);// 根据配置的密钥生成安全密钥对象byte[] keyBytes = Encoding.UTF8.GetBytes(_jwtSetting.SigningKey);var secKey = new SymmetricSecurityKey(keyBytes);// 指定签名算法,这里用 HMAC-SHA256var credentials = new SigningCredentials(secKey, SecurityAlgorithms.HmacSha256Signature);// 创建 Token 对象,包括过期时间、签名凭据、Claimsvar tokenDescriptor = new JwtSecurityToken(expires: expires,signingCredentials: credentials,claims: claims);// 序列化成最终 Token 字符串return new JwtSecurityTokenHandler().WriteToken(tokenDescriptor);}
}

 4.接口校验JWT

 [Route("[controller]/[action]")][ApiController][Authorize]  // 表示:访问此控制器下的所有 Action,都必须登录并携带有效 JWTpublic class UserInfoController : ControllerBase{/// <summary>/// 测试用接口:返回当前登录用户的身份信息(从 JWT Claims 解析)/// </summary>[HttpGet]public IActionResult Hello(){// 从 Claims 中获取用户IDstring id = this.User.FindFirst(ClaimTypes.NameIdentifier)!.Value;// 获取用户名string userName = this.User.FindFirst(ClaimTypes.Name)!.Value;// 获取用户拥有的所有角色IEnumerable<Claim> roleClaims = this.User.FindAll(ClaimTypes.Role);// 把角色列表拼接成逗号分隔的字符串string roleNames = string.Join(',', roleClaims.Select(c => c.Value));// 返回身份信息return Ok($"id={id}, userName={userName}, roleNames={roleNames}");}}

添加的[Authorize]表示这个控制器类下所有的操作方法都需要登录后才能访问。
ControllerBase中定义的ClaimsPrincipal类型的User属性代表当前登录用户的身份信息,我们可以通过ClaimsPrincipal的Claims属性获得当前登录用户的所有Claim信息,不过我们一般通过FindFirst方法根据Claim的类型来查找需要的Claim,如果用户身份信息中含有多个同类型的Claim,我们则可以通过FindAll方法来找到所有Claim。

5.swagger调试

直接访问401无权限 ,我们需要传入jwt才能访问该接口.

ASP.NET Core要求(这也是HTTP的规范)JWT放到名字为Authorization的HTTP请求报文头中,报文头的值为“Bearer JWT”。

Swagger中默认没有提供设置自定义HTTP请求报文头的方式,因此对于需要传递Authorization报文头的接口,调试起来很麻烦。我们可以通过对OpenAPI进行配置,从而让Swagger中可以发送Authorization报文头。

 // 注册 Swagger 服务,同时配置 JWT 认证支持builder.Services.AddSwaggerGen(c =>{// 定义一个 OpenApiSecurityScheme:告诉 Swagger,这里有一个全局的 Header 参数叫 Authorizationvar scheme = new OpenApiSecurityScheme(){// Swagger UI 上显示的描述信息,告诉开发者怎么填写 TokenDescription = "在请求头中加入 Authorization 字段,例如:'Bearer 12345abcdef'",// 给这个 SecurityScheme 起一个引用ID,后面配置用Reference = new OpenApiReference{Type = ReferenceType.SecurityScheme,Id = "Authorization"},// Scheme 字段,这里用 "oauth2" 字符串(其实可以写任何字符串,Swagger 不校验这个)Scheme = "oauth2",// 参数名,Swagger UI 会自动生成这个 Header 字段Name = "Authorization",// 参数的位置:在 HTTP Header 中In = ParameterLocation.Header,// 声明类型是 API Key(Swagger 把 "Authorization" 这种 Header 参数用 ApiKey 类型)Type = SecuritySchemeType.ApiKey,};// 添加这个 Security 定义,名称叫 "Authorization",Swagger UI 会显示一个输入框c.AddSecurityDefinition("Authorization", scheme);// 创建一个全局安全要求:告诉 Swagger,每个接口默认都要带这个 SecuritySchemevar requirement = new OpenApiSecurityRequirement();// 给这个 requirement 加上刚才定义的 scheme,值是空列表(Swagger 需要这么写)requirement[scheme] = new List<string>();// 把这个全局安全要求加到 Swagger 配置里c.AddSecurityRequirement(requirement);});

首先我们要先利用前面的登录接口获取一个JWT,然后通过这个按钮将JWT传入,此时你就可以访问那些需要认证的接口了.

 

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

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

相关文章

优化Cereal宏 一行声明序列化函数

Cereal序列化库中宏递归展开的优化方案及技术解析 未优化&#xff1a;参考nlohmann json设计Cereal宏 一行声明序列化函数 宏实现 #include <cereal/cereal.hpp>// 强制二次展开 #define CEREAL_EXPAND( x ) x// 获取宏参数的数量&#xff0c;对应的CEREAL_PASTEn宏NAME…

14-C#的弹出的窗口输入与输出

C#的弹出的窗口输入与输出 1.文件名输入 string fileName Interaction.InputBox("输入保存的文件名", "保存");2.弹窗信息输出 MessageBox.Show("请选择轮询!", "Error", MessageBoxButtons.OK);catch (Exception ex){MessageBox.S…

多模态大语言模型arxiv论文略读(141)

Mini-InternVL: A Flexible-Transfer Pocket Multimodal Model with 5% Parameters and 90% Performance ➡️ 论文标题&#xff1a;Mini-InternVL: A Flexible-Transfer Pocket Multimodal Model with 5% Parameters and 90% Performance ➡️ 论文作者&#xff1a;Zhangwei …

VScode使用usb转网口远程开发rk3588

我使用的是鲁班猫的板&#xff0c;只有一个网口&#xff0c;需要接雷达&#xff0c;因此另外弄了一个usb转网口来连接电脑开发。 在使用vscode或MobaXterm连接板子时&#xff0c;使用主机名与用户名来连接&#xff1a; ssh catlubancat rk那边就直接插入usb转网口以及网线&a…

AUTOSAR图解==>AUTOSAR_AP_EXP_SOVD

AUTOSAR服务导向车辆诊断详解 面向现代化车辆架构的诊断方案 目录 1. 引言 1.1 ASAM SOVD简介1.2 SOVD产生的动机 2. SOVD参考架构 2.1 SOVD网关2.2 诊断管理器2.3 SOVD到UDS转换2.4 后端连接 3. SOVD用例 3.1 SOVD和UDS的共同用例3.2 SOVD特定用例 3.2.1 访问权限3.2.2 软件更…

第八讲:STL简介

1. 什么是STL STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复的 组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。 2. STL的版本 a. 原始版本 Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本…

高弹性、高可靠!腾讯云 TDMQ RabbitMQ Serverless 版全新发布

导语 2025年6月起&#xff0c;腾讯云 TDMQ RabbitMQ 版正式推出 Serverless 版本&#xff0c;该版本基于自研的存算分离架构&#xff0c;兼容 AMQP 0-9-1 协议和开源 RabbitMQ 的各个组件与概念&#xff0c;且能够规避开源版本固有的不抗消息堆积、脑裂等稳定性缺陷&#xff0…

Linux 内存调优之 BPF 分析用户态小内存分配

写在前面 博文内容为 使用 BPF 工具跟踪 Linux 用户态小内存分配(brk,sbrk)理解不足小伙伴帮忙指正 😃,生活加油我看远山,远山悲悯 持续分享技术干货,感兴趣小伙伴可以关注下 _ brk 内存分配简单概述 一般来说,应用程序的数据存放于堆内存中,堆内存通过brk(2)系统调用进…

心理测评app心理测试系统框架设计

一、逻辑分析 用户管理逻辑 新用户注册&#xff1a;需要收集用户的基本信息&#xff0c;如用户名、密码、邮箱等&#xff0c;并且要对输入信息进行合法性校验&#xff0c;确保信息完整且符合格式要求。同时&#xff0c;为每个新用户生成唯一的标识符&#xff0c;方便后续数据管…

配置有nvlink的H20A800使用pytorch报错

背景 装有nvlink的h20机器上配置好驱动和cuda之后使用pytorch报错 A800机器同样 (pytorch2.4) rootxx-dev-H20:~# python Python 3.12.0 | packaged by Anaconda, Inc. | (main, Oct 2 2023, 17:29:18) [GCC 11.2.0] on linux Type “help”, “copyright”, “credits” or …

sql的语句执行过程

第一步&#xff1a;客户端把语句发给服务器端执行 当我们在客户端执行SQL语句时&#xff0c;客户端会把这条SQL语句发送给服务器端&#xff0c;让服务器端的进程来处理这语句。也就是说&#xff0c;Oracle 客户端是不会做任何的操作&#xff0c;他的主要任务就是把客户端产生的…

深度学习-分类

深度学习-分类方式 &#xff08;重点&#xff09;一、按数据类型与处理逻辑分类1. 序列数据&#xff08;时序/顺序相关&#xff09;2. 网格状数据&#xff08;空间相关&#xff09;3. 图结构数据&#xff08;非欧几里得结构&#xff09;4. 其他特殊类型数据 &#xff08;重点&a…

C语言---常见的字符函数和字符串函数介绍

目录 前言 1 字符分类函数 2 字符转换函数 3 strlen的使用和模拟实现 3.1 strlen的模拟实现 4 strcpy的使用和模拟实现 4.1 strcpy的模拟实现 5 strcat的使用和模拟实现 5.1 strcat的模拟实现 6 strcmp的使用和模拟实现 6.1 strcmp的模拟实现 7 strncpy函数的使用…

Minio入门+适配器模式(实战教程)

一、安装Minio 1.1 拉取镜像 docker pull minio/minio docker images 1.2创建挂载目录 1.2.1 创建数据目录 mkdir -p /docker-minio/data 1.2.2 创建配置文件目录 mkdir -p /docker-minio/config 1.2.3 设置权限 chmod -R 777 /docker-minio/data /docker-minio/config …

LLaMA-Factory 对 omnisql 进行 ppo dpo grpo nl2sql任务 实现难度 时间 全面对比

在LLaMA-Factory框架下&#xff0c;针对omnisql任务&#xff08;自然语言到SQL生成&#xff09;应用PPO、DPO、GRPO三种算法的实现难度、时间及全面对比如下&#xff1a; 一、实现难度对比 1. PPO&#xff08;近端策略优化&#xff09; 难度&#xff1a;★★☆☆☆&#xff…

Kingbase 数据库中的 sys_guid() 函数报错

解决 Kingbase 数据库中的 sys_guid() 函数报错问题 问题背景 Kingbase 数据库在迁移或使用过程中&#xff0c;可能会遇到 select sys_guid() 函数报错 , 提示函数不存在的情况&#xff0c;这通常是由于以下几种原因造成的&#xff1a; 函数未正确安装或未启用函数参数不符合…

零基础RT-thread第五节:电容按键(2)

上一章的电容按键完全使用的HAL库的代码&#xff0c;并没有使用线程。这里尝试使用线程来控制电容按键。 依旧是 F767 本来以为会很容易实现&#xff0c;没想到尝试了很久&#xff0c;电容按键一直没有反应。 static rt_uint32_t measure_charge_time(void) {// 步骤1: 放电 …

华为云Flexus+DeepSeek征文|单机部署 与 CCE 高可用部署下 Dify 性能实测

引言 在当今的 AI 应用开发领域&#xff0c;选择合适的部署方式对于应用的性能表现、资源利用和成本控制至关重要。华为云为开发者提供了多样化的部署选择&#xff0c;其中基于单机 Flexus 实例的基础版部署和基于 CCE 容器的高可用版部署是两种常见的方式。本文将深入对比这两…

钉钉小程序框架:Pinia 状态管理与持久化存储封装

上一篇文章完成了 Pinia 在钉钉小程序中的引入与基础配置 文章地址&#xff1a;钉钉小程序框架引入 Pinia 状态管理-CSDN博客 本文将深入探讨如何通过Pinia 结合持久化存储 实现用户状态 在上一章节中&#xff0c;我们已经完成了 Pinia 在钉钉小程序中的引入与基础配置。本章将…

云计算产业链

一、云计算定义与分类体系 本质特征 按需服务模式&#xff1a;以网络化方式提供可配置的计算资源共享池&#xff08;网络/服务器/存储/应用&#xff09;。核心能力&#xff1a;快速弹性扩容、资源池化共享、按使用量付费、低管理开销。技术原理&#xff1a;通过分布式计算将大型…