.NET 6.0 WebAPI 使用JWT生成Token的验证授权

1.引入相关程序包JwtBearer注意版本:

2.配置文件appsettings.json写相关配置参数(也可不写,写在程序里面,数据库读取也是一样的)

, //JWT加密"JWTToken": {"SecretKey": "jsaduwqe6asdjewejdue7dfmsdfu0sdfmwmsd8wfsd6", //密钥"Issuer": "ZYP", //发行者"Audience": "simple", //拥护者//"ExpireMinutes": 240 //过期时间}

3.在Program配置相关服务。

#region JWT
//获取配置文件
var configuration = builder.Configuration;
string Issuer = configuration["JWTToken:Issuer"];
string Audience = configuration["JWTToken:Audience"];
string SecretKey = configuration["JWTToken:SecretKey"];
//注入jwt
builder.Services.AddAuthentication(options =>
{options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{options.TokenValidationParameters = new TokenValidationParameters(){//过期时间容错值,解决服务器端时间不同步问题(秒)//允许服务器时间偏移量30秒,即我们配置的过期时间加上这个允许偏移的时间值,才是真正过期的时间(过期时间 + 偏移值)你也可以设置为0,ClockSkew = TimeSpan.ZeroClockSkew = TimeSpan.FromSeconds(30),//要求Token的Claims中必须包含ExpiresRequireExpirationTime = true,//是否在令牌期间验证签发者ValidateIssuer = true,//发行人IssuerValidIssuer = Issuer, //是否验证接收者ValidateAudience = true,//是否验证失效时间ValidateLifetime = true,//是否验证签名SecurityKeyValidateIssuerSigningKey = true,//接收者ValidAudience = Audience,//密钥SecurityKeyIssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey)), };
});
//注入JwtHelper
builder.Services.AddSingleton(new JwtHelper(configuration));
#endregion//注入Swagger,注入这个才能在调试接口时输入token
builder.Services.AddSwaggerGen(options =>
{options.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement{{new OpenApiSecurityScheme{Reference=new OpenApiReference{Id="Bearer",Type=ReferenceType.SecurityScheme},},Array.Empty<string>()}});options.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme{Description = "请输入文字'Bearer '后面跟空格和token格式  Bearer {token}",Name = "Authorization",In = Microsoft.OpenApi.Models.ParameterLocation.Header,Type = Microsoft.OpenApi.Models.SecuritySchemeType.ApiKey});
});//配置跨域
builder.Services.AddCors(policy =>
{policy.AddPolicy("CorsPolicy", opt => opt.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().WithExposedHeaders("X-Pagination"));
});//调用中间件:UseAuthentication(认证),
//必须在所有需要身份认证的中间件前调用,比如 UseAuthorization(授权)。
app.UseAuthentication();
//调用中间件:UseAuthorization(授权)。
app.UseAuthorization();

4.相关配置结束后,我们得生成Token,这时我们创建一个专门生成Token的类里面有两个生成Token的方法,想用哪个用哪个。该类在Program里有引用。

 /// <summary>/// Token生成类/// </summary>public class JwtHelper{/// <summary>/// 配置文件信息/// </summary>private readonly IConfiguration _configuration;public JwtHelper(IConfiguration configuration){_configuration = configuration;}/// <summary>/// 创建一个使用控制器方法授权的Token/// </summary>/// <returns></returns>public string CreatePermissionToken(string UserName, string RoleName, string AppId, Claim[] claims){// 1. 定义需要使用到的Claimsif (claims == null){claims = new[]{new Claim(ClaimTypes.Name, UserName), //HttpContext.User.Identity.Namenew Claim(ClaimTypes.Role, RoleName), //HttpContext.User.IsInRole("r_admin")new Claim(JwtRegisteredClaimNames.Jti, AppId),//分配给订阅着的特定Idnew Claim("Permission", Permissions.UserCreate),new Claim("Permission", Permissions.UserDelete),new Claim("Permission", Permissions.UserUpdate),new Claim("Permission", Permissions.UserSelect)//new Claim("Username", "Admin"),//其他荷载信息};}// 2. 从 appsettings.json 中读取密钥SecretKeyvar secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWTToken:SecretKey"]));// 3. 选择加密算法var algorithm = SecurityAlgorithms.HmacSha256;// 4. 生成Credentialsvar signingCredentials = new SigningCredentials(secretKey, algorithm);// 5. 根据以上,生成tokenvar Token = new JwtSecurityToken(issuer: _configuration["JWTToken:Issuer"], //发布者audience: _configuration["JWTToken:Audience"], //接收者claims: claims, //存放的用户信息notBefore: DateTime.Now, //发布时间expires: System.DateTime.Now.AddHours(48), //有效期设置为48小时signingCredentials: signingCredentials //数字签名,用于生成Token的Header,其余都是荷载数据);// 6. 将token变为stringvar token = new JwtSecurityTokenHandler().WriteToken(Token);return token;}/// <summary>/// 创建一个使用账号密码授权验证的Token/// </summary>/// <param name="UserName"></param>/// <param name="RoleName"></param>/// <param name="AppId"></param>/// <param name="Account"></param>/// <param name="PassWord"></param>/// <returns></returns>public string CreateLoginToken(string UserName, string RoleName, string AppId, string Account, string PassWord){// 1. 定义需要使用到的Claimsvar claims = new[]{new Claim(ClaimTypes.Name, UserName),new Claim(ClaimTypes.Role, RoleName),new Claim(JwtRegisteredClaimNames.Jti, AppId),//分配给订阅着的特定Idnew Claim("Account", Account),//账号new Claim("PassWord", PassWord)//密码,可以要求使用特定加密技术加密//new Claim("Username", "Admin"),//其他荷载信息};// 2. 从 appsettings.json 中读取密钥SecretKeyvar secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWTToken:SecretKey"]));// 3. 选择加密算法var algorithm = SecurityAlgorithms.HmacSha256;// 4. 生成Credentialsvar signingCredentials = new SigningCredentials(secretKey, algorithm);// 5. 根据以上,生成tokenvar Token = new JwtSecurityToken(issuer: _configuration["JWTToken:Issuer"], //发布者audience: _configuration["JWTToken:Audience"], //接收者claims: claims, //存放的用户信息notBefore: DateTime.Now, //发布时间expires: System.DateTime.Now.AddHours(48), //有效期设置为48小时signingCredentials: signingCredentials //数字签名,用于生成Token的Header,其余都是荷载数据);// 6. 将token变为stringvar token = new JwtSecurityTokenHandler().WriteToken(Token);return token;}}

5.这时可以生成Token了,我们来新建一个控制器来生成一个试试:

[Route("api/[controller]")]
[ApiController]
public class GetTokenController : ControllerBase
{private readonly JwtHelper _jwtHelper;public GetTokenController(JwtHelper jwtHelper){_jwtHelper = jwtHelper;}[HttpPost][Route("Token")]public Task<JsonResult> GetToken(UserToken token){string thetoken =  _jwtHelper.CreateLoginToken(token.Name, "User", token.AppId, token.Account, token.PassWord);var result = new{success = true,msg = "OK",//消息token = thetoken};return Task.FromResult(new JsonResult(result));}
}

控制器的参数类(根据自己需要修改) 

public class UserToken
{/// <summary>/// 给需要访问本系统的程序的唯一标识/// </summary>public string AppId { get; set; } = string.Empty;/// <summary>/// 需要访问本系统的程序的名称/// </summary>public string Name { get; set; } = string.Empty;/// <summary>/// 分配给需要访问本系统的程序的账号/// </summary>public string Account { get; set; } = string.Empty;/// <summary>/// 分配给需要访问本系统的程序的密码/// </summary>public string PassWord { get; set; } = string.Empty;
}

启动程序测试,生成成功!:

6.既然可以生成Token了,那么就该给控制器授权了,总不能让每个携带Token的用户能访问系统所以的API吧,那样会出现垂直越权的情况,渗透测试过不了哦。

相关标签:Authorize 和 AllowAnonymous

授权方式:介绍三种授权方式(Policy、Role、Scheme)

此处着重说Policy方式,对Role方法感兴趣的可以看我前面的Cookie方式验证。

6.1首先新建一个JwtAuthorizationRequirement类(类名不固定)用于继承IAuthorizationRequirement

public class JwtAuthorizationRequirement : IAuthorizationRequirement
{//这里可以扩展一些其他的角色或者需要的东西.//txt参数是在使用策略授权时传入进来的,例如:Authorize(Policy= "MyPolicy")的MyPolicypublic JwtAuthorizationRequirement(string name){Name = name;}public string? Name { get; set; }
}

 6.2然后新建一个JwtAuthorizationHandler类继承AuthorizationHandler<JwtAuthorizationRequirement>

/// <summary>
/// 检验策略,相当于.NET MVC继承AuthorizeAttribute实现JWT的拦截器的效果。
/// </summary>
public class JwtAuthorizationHandler : AuthorizationHandler<JwtAuthorizationRequirement>
{protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, JwtAuthorizationRequirement requirement){if (context.User == null){context.Fail();return Task.CompletedTask;}//requirement.Name 就是在添加策略授权时传入的值 string? Account = context.User.Claims.FirstOrDefault(p => p.Type == requirement.Name)?.Value;string? PassWord = context.User.Claims.FirstOrDefault(p => p.Type == "PassWord")?.Value;//这里时数据库或者其他方式校验 if (Account=="1234"){context.Succeed(requirement);}else{context.Fail();}return Task.CompletedTask;}
}

6.3然后将该策略在Program下注入

//注入授权策略(非必要,仅有需要自定义授权规则时使用)
builder.Services.AddSingleton<IAuthorizationHandler, JwtAuthorizationHandler>();
//账号密码验证策略
builder.Services.AddAuthorization(p =>
{p.AddPolicy("Account", t =>{t.Requirements.Add(new JwtAuthorizationRequirement("Account"));});
});

6.4 使用:

 [Authorize(policy: "Account")]//主要是这个public IEnumerable<WeatherForecast> Get(){return Enumerable.Range(1, 5).Select(index => new WeatherForecast{Date = DateTime.Now.AddDays(index),TemperatureC = Random.Shared.Next(-20, 55),Summary = Summaries[Random.Shared.Next(Summaries.Length)]}).ToArray();}

策略授权基本就是这样了。

再贴一个策略授权代码(可以忽略):

  /// <summary>/// 规则授权参数/// </summary>public class Permissions{/// <summary>/// 规则受体/// </summary>public const string User = "User";/// <summary>/// 增权限/// </summary>public const string UserCreate = User + ".Create";/// <summary>/// 删权限/// </summary>public const string UserDelete = User + ".Delete";/// <summary>/// 改权限/// </summary>public const string UserUpdate = User + ".Update";/// <summary>/// 查权限/// </summary>public const string UserSelect = User + ".Select";}
public class PermissionAuthorizationRequirement : IAuthorizationRequirement
{/// <summary>/// 参数是在使用策略授权时传入进来的,例如:Authorize(Policy= "MyPolicy")的MyPolicy/// </summary>/// <param name="name">Authorize(Policy= "MyPolicy")的MyPolicy</param>public PermissionAuthorizationRequirement(string name){Name = name;}public string Name { get; set; }
}
    public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionAuthorizationRequirement>{protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionAuthorizationRequirement requirement){//取出当前用户的所有Permission的参数var permissions = context.User.Claims.Where(_ => _.Type == "Permission").Select(_ => _.Value).ToList();//是否满足授权,满足则运行 context.Succeed if (permissions.Any(_ => _.StartsWith(requirement.Name))){context.Succeed(requirement);}else{context.Fail();}return Task.CompletedTask;}}

Program

builder.Services.AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler>();
//控制器方法验证策略
builder.Services.AddAuthorization(options =>
{options.AddPolicy(Permissions.UserCreate, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserCreate)));options.AddPolicy(Permissions.UserUpdate, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserUpdate)));options.AddPolicy(Permissions.UserDelete, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserDelete)));options.AddPolicy(Permissions.UserSelect, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserSelect)));
});

7.最后.整个Program

var builder = WebApplication.CreateBuilder(args);// Add services to the container.builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//首先引用Microsoft.AspNetCore.Mvc.NewtonsoftJson包。再在builder.Services.AddControllers()后添加相应内容
builder.Services.AddControllers().AddNewtonsoftJson(options =>
{//设置JSON返回数据格式大小写与Model一致options.SerializerSettings.ContractResolver = new DefaultContractResolver();//设置一般API的日期格式options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
});#region JWT
//获取配置文件
var configuration = builder.Configuration;
string Issuer = configuration["JWTToken:Issuer"];
string Audience = configuration["JWTToken:Audience"];
string SecretKey = configuration["JWTToken:SecretKey"];
//注入jwt
builder.Services.AddAuthentication(options =>
{options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{options.TokenValidationParameters = new TokenValidationParameters(){//过期时间容错值,解决服务器端时间不同步问题(秒)//允许服务器时间偏移量30秒,即我们配置的过期时间加上这个允许偏移的时间值,才是真正过期的时间(过期时间 + 偏移值)你也可以设置为0,ClockSkew = TimeSpan.ZeroClockSkew = TimeSpan.FromSeconds(30),//要求Token的Claims中必须包含ExpiresRequireExpirationTime = true,//是否在令牌期间验证签发者ValidateIssuer = true,//发行人IssuerValidIssuer = Issuer, //是否验证接收者ValidateAudience = true,//是否验证失效时间ValidateLifetime = true,//是否验证签名SecurityKeyValidateIssuerSigningKey = true,//接收者ValidAudience = Audience,//密钥SecurityKeyIssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey)), };
});
//注入JwtHelper
builder.Services.AddSingleton(new JwtHelper(configuration));
//注入授权策略(非必要,仅有需要自定义授权规则时使用)
builder.Services.AddSingleton<IAuthorizationHandler, JwtAuthorizationHandler>();
//账号密码验证策略
builder.Services.AddAuthorization(p =>
{p.AddPolicy("Account", t =>{t.Requirements.Add(new JwtAuthorizationRequirement("Account"));});
});builder.Services.AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler>();
//控制器方法验证策略
builder.Services.AddAuthorization(options =>
{options.AddPolicy(Permissions.UserCreate, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserCreate)));options.AddPolicy(Permissions.UserUpdate, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserUpdate)));options.AddPolicy(Permissions.UserDelete, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserDelete)));options.AddPolicy(Permissions.UserSelect, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserSelect)));
});
#endregion
//注入Swagger
builder.Services.AddSwaggerGen(options =>
{options.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement{{new OpenApiSecurityScheme{Reference=new OpenApiReference{Id="Bearer",Type=ReferenceType.SecurityScheme},},Array.Empty<string>()}});options.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme{Description = "请输入文字'Bearer '后面跟空格和token格式  Bearer {token}",Name = "Authorization",In = Microsoft.OpenApi.Models.ParameterLocation.Header,Type = Microsoft.OpenApi.Models.SecuritySchemeType.ApiKey});
});//配置跨域
builder.Services.AddCors(policy =>
{policy.AddPolicy("CorsPolicy", opt => opt.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().WithExposedHeaders("X-Pagination"));
});
var app = builder.Build();
//调用中间件:UseAuthentication(认证),
//必须在所有需要身份认证的中间件前调用,比如 UseAuthorization(授权)。
app.UseAuthentication();
//调用中间件:UseAuthorization(授权)。
app.UseAuthorization();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{app.UseSwagger();app.UseSwaggerUI();
}app.UseHttpsRedirection();app.MapControllers();app.Run();

参考链接:ASP.NET Core 6.0 添加 JWT 认证和授权 - 芦荟柚子茶 - 博客园

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

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

相关文章

Postman导出报告

一、下载node.js 导出测试报告我们需要用到一个工具叫做newman&#xff0c;它是node.js开发的一个插件&#xff0c;要使用他需要先下载node.js&#xff0c;安装包可以去官网下载&#xff0c;这里我分享一个 链接: https://pan.baidu.com/s/179yLzwTtLH3eihYs_yxrZA?pwd7bqt 提…

数据分析:主成分以及贡献变量解析

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍分析目的变量的loadings的含义加载依赖包导入数据数据预处理PCA计算PCA图主成分分布系统信息介绍 PCA分析,即主成分分析(Principal Component Analysis),是一种统计方法,用于…

吴恩达深度学习笔记:卷积神经网络(Foundations of Convolutional Neural Networks)2.3-2.4

目录 第四门课 卷积神经网络&#xff08;Convolutional Neural Networks&#xff09;第二周 深度卷积网络&#xff1a;实例探究&#xff08;Deep convolutional models: case studies&#xff09;2.3 残差网络(ResNets)(Residual Networks (ResNets))2.4 残差网络为什么有用&am…

在虚幻引擎中实时显示帧率

引擎自带了显示帧率的功能 但是只能在编辑器中显示 , 在游戏发布后就没有了 , 所以我们要自己做一个 创建一个控件蓝图 创建画布和文本 , 修改文本 文本绑定函数 , 点击创建绑定 添加一个名为 FPS 的变量 格式化文本 用大括号把变量包起来 {FPS Int} FPS 然后转到事件图表…

RHCS认证-Linux(RHel9)-Ansible

文章目录 一、ansible 简介二 、ansible部署三、ansible服务端测试四 、ansible 清单inventory五、Ad-hot 点对点模式六、YAML语言模式七、RHCS-Ansible附&#xff1a;安装CentOS-Stream 9系统7.1 ansible 执行过程7.2 安装ansible&#xff0c;ansible-navigator7.2 部署ansibl…

一看就会!PS2024下载安装教程详解

PS2024下载方法&#xff1a; PS2024安装教程&#xff1a; 1、右击【PS2024.zip】&#xff0c;选择【解压到PS2024】 2、右击【Set-up.exe】&#xff0c;选择【以管理员身份运行】 3、点击右下角灰色的小文件夹图标&#xff0c;选择【更改位置】 4、选择安装路径后&#xff0c;…

策略模式与工厂模式的区别

《策略模式与工厂模式的区别》 策略模式&#xff08;Strategy Pattern&#xff09; 和 工厂模式&#xff08;Factory Pattern&#xff09; 都是常见的设计模式&#xff0c;虽然它们在设计目标上有一些相似之处&#xff0c;如解耦代码、增强扩展性&#xff0c;但它们的应用场景和…

数字化转型中的供应链管理优化

在当今全球化和数字化的浪潮下&#xff0c;企业供应链管理面临着前所未有的挑战和机遇&#xff0c;企业在数字化转型过程中&#xff0c;如何优化供应链管理成为提升竞争力的关键。通过应用先进技术如RPA机器人流程自动化、大数据分析、物联网等&#xff0c;企业可以显著提高物流…

go解决引入私有包报错“Repository owner does not exist“的两种方式

当你写好引入的私有包,执行go mod tidy报错: Gogs: Repository owner does not exist fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists. 目前我的两种解决方案: 一、拉群整个…

基于WebServer的工业数据采集系统

一、项目框架及流程 二、http简介 HTTP协议是Hyper Text Transfer Protocol&#xff08;超文本传输协议&#xff09;的缩写&#xff0c;是用于Web Browser&#xff08;浏览器&#xff09;到Web Server&#xff08;服务器&#xff09;进行数据交互的传输协议。 HTTP是应用层协…

C/C++语言基础--C++构造函数、析构函数、深拷贝与浅拷贝等等相关知识讲解

本专栏目的 更新C/C的基础语法&#xff0c;包括C的一些新特性 前言 周末休息了&#xff0c;没有更新&#xff0c;请大家见谅哈&#xff1b;构造函数、析构函数可以说便随着C每一个程序&#xff0c;故学构造函数、析构函数是必要的&#xff1b;C语言后面也会继续更新知识点&am…

计算机的错误计算(一百零二)

摘要 探讨 的计算精度问题。 从计算机的错误计算&#xff08;九十九&#xff09;可知&#xff0c; 在IEEE 754-2019的列表中。因此&#xff0c;有必要分析其计算准确度。 例1. 已知 计算 若利用 Python的SciPy库中函数计算&#xff0c;则有&#xff1a; 若用Java的pow函…

通过 LabVIEW 正则表达式读取数值(整数或小数)

在LabVIEW开发中&#xff0c;字符串处理是一个非常常见的需求&#xff0c;尤其是在处理包含复杂格式的数字时。本文通过一个具体的例子来说明如何利用 Match Regular Expression Function 和 Match Pattern Function 读取并解析字符串中的数字&#xff0c;并重点探讨这两个函数…

毕业设计选题:基于ssm+vue+uniapp的英语学习激励系统小程序

开发语言&#xff1a;Java框架&#xff1a;ssmuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;M…

达梦-华为鲲鹏ARM架构下性能测试最佳实践

一、测试综述 1.1 测试目的 本次测试的目的是验证达梦数据库&#xff0c;在鲲鹏服务器下&#xff0c;不同服务器参数基于sysbench性能压力测试的表现。本次参数是根据为华为鲲鹏arm服务器调优十板斧内建议值调整 成长地图-鲲鹏开发套件开发文档-鲲鹏社区 1.2 通用指标 指标…

虚幻蓝图Ai随机点移动

主要函数: AI MoveTo 想要AI移动必须要有 导航网格体边界体积 (Nav Mesh Bounds Volume) , 放到地上放大 , 然后按P键 , 可以查看范围 然后创建一个character类 这样连上 AI就会随机运动了 为了AI移动更自然 , 取消使用控制器旋转Yaw 取消角色移动组件 的 使用控制器所需的…

关于Cursor使用的小白第一视角

最近看破局感觉洋哥总是提到cursor&#xff0c;感觉好火&#xff0c;所以打算学习一下怎么用Cursor&#xff0c;如果可以希望能做一个我自己的网站。 之前从来没用过Cursor。所以&#xff0c;这是一篇小白视角的Cursor使用教程。 如果你也是一个小白&#xff0c;并且对Cursor…

ArcGIS Desktop使用入门(三)图层右键工具——拓扑(上篇:地图拓扑)

系列文章目录 ArcGIS Desktop使用入门&#xff08;一&#xff09;软件初认识 ArcGIS Desktop使用入门&#xff08;二&#xff09;常用工具条——标准工具 ArcGIS Desktop使用入门&#xff08;二&#xff09;常用工具条——编辑器 ArcGIS Desktop使用入门&#xff08;二&#x…

Maven配置及使用

1. Maven简介和安装 1.1. Maven是一个依赖管理工具 问题&#xff1a; jar包的规模 随着使用框架越来越多&#xff0c;或框架的封装程度越来越高&#xff0c;项目中使用的jar包也越来越多。项目中&#xff0c;一个模块里用到上百个jar包是非常正常的jar包的来源 jar包所属技术…

LeetCode 面试经典150题 201.数字范围按位与

题目&#xff1a;给你两个整数 left 和 right &#xff0c;表示区间 [left, right] &#xff0c;返回此区间内所有数字 按位与 的结果&#xff08;包含 left 、right 端点&#xff09;。 提示&#xff1a;0 < left < right < 2^31 - 1 思路&#xff1a; 位与的特性…