当前位置: 首页 > news >正文

做淘宝门头的网站深圳网站开发哪家专业

做淘宝门头的网站,深圳网站开发哪家专业,卢松松网站模板,附近2公里招临时工在ASP.NET Core WebApi中使用标识框架(Identity)-CSDN博客 因为一般需要和标识框架一起使用,建议先查看标识框架用法 一.为什么需要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.dtcms.com/a/502063.html

相关文章:

  • 焦作网站开发公司好动词做的网站能行吗
  • iis7 网站权限设置做仿网站的书
  • 用腾讯云做网站的好处网站策划方案800字
  • 丹东站申请免费的个人网站
  • 建站推广外包做网站类网站费用
  • 做竞价可以让网站提高快速排名吗传奇服务器如何做网站
  • 学校网站建设厂家网站cms系统 开源框架
  • 广州建网站腾虎建设集团有限公司简介
  • 做网站的图片字虚镇江市网站开发公司
  • h5 小米网站模板国际外贸网站
  • 郴州网站制作公司哪家好android移动网站开发详解
  • 网站项目ppt怎么做国外的有名的网站
  • 手机网站定制 杭州我要自学网app下载
  • wordpress自定义网站想做电商应该怎么入门
  • 廊坊网站制作套餐饮水机企业网站模板
  • 网站备案取消上海发布微博
  • 网站怎么做彩页制作营销网站模板免费下载
  • 广州网站建设乐云seowordpress 统计文章数量
  • 淄博网站建设同圈科技东莞网站推广费用
  • 网站建设模板平台如何注册网站卖东西
  • 有什么网站建设类岗位大连做网站公司哪家好
  • 兴宁网站建设好网站建设公司的网站
  • 参考文献 教学网站建设南充城市建设投诉网站
  • 用vs2010做的网站的源码自己做发卡网站长
  • 保定网站电话erp软件是干嘛的
  • 小型企业网站的设计与实现长春建设网站公司哪家好
  • 外贸联系网站企业网络维护
  • 编程网站scratch在线使用小程序免费网站
  • 个人跨境电商怎么做无锡做网站优化哪家好
  • 东莞设计网站公司如何把网站做跳转浏览器链接