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

有状态Cookie

相比上面无状态模式,有状态模式需要额外加一些代码用来存储读写和过期清理。如果是单服务实例,可以直接存入本地内存,但需注意服务重启的话会导致票据丢失,用户直接退出登录了。如果服务多实例负载均衡的情况下需要引入分布式存储来共享,把票据存入Redis或者Memcached中。如果你坚持本地内存可能会破坏负载均衡策略。

集成思路:

第一步:将认证方案注入容器

将认证方案,例如 Cookie 和它的配置注入容器,跟无状态一模一样的套路,就不贴代码了,参照无状态第1、2步。

第二步:实现 ITicketStore 接口

如果需要使用Cookie的有状态模式,框架的CookieAuthenticationOptions选项提供了SessionStore配置来进行实现,他是ITicketStore类型,这个接口是框架提供给用户扩展的,主要用于认证票据存在哪,我们需要实现它。

public class MemoryTicketStore(private readonly IMemoryCache _cache) : ITicketStore { private const string KeyPrefix = "auth-session:"; // 存 public Task StoreAsync(AuthenticationTicket ticket) { var key = $"{KeyPrefix}{Guid.NewGuid():N}"; RenewAsync(key, ticket); return Task.FromResult(key); } // 更新 public Task RenewAsync(string key, AuthenticationTicket ticket) { var expires = ticket.Properties.ExpiresUtc ?? DateTimeOffset.UtcNow.AddHours(8); _cache.Set(key, ticket, new MemoryCacheEntryOptions { AbsoluteExpiration = expires }); return Task.CompletedTask; } // 查询 public Task RetrieveAsync(string key) => Task.FromResult(_cache.Get(key)); // 删除 public Task RemoveAsync(string key) { _cache.Remove(key); return Task.CompletedTask; } }

你同样可以自己实现Redis存储的逻辑,只需替换MemoryTicketStoreRedisTicketStore,然后修改ITicketStore的注册,根本不需要改动身份验证配置。为了方便我使用的内存缓存,接下来需要将实现注入容器,再将它配置起来。

第三步:注入并配置
// 注入组件 builder.Services.AddMemoryCache(); builder.Services.AddSingleton<ITicketStore, MemoryTicketStore>(); // 配置实例延迟执行,防止依赖的服务没注册 builder.Services.AddOptions<CookieAuthenticationOptions>(CookieAuthenticationDefaults.AuthenticationScheme) .Configure<ITicketStore>((options, store) => options.SessionStore = store);
第四步:在控制器中使用

在控制器中使用和无状态模式一样使用。


2.3 使用 CookieAuthentication 登录实现

Cookie认证的方式登录,不需要我们实现生成Cookie,框架完全控制会话生命周期,SignInAsync()触发标准化的Cookie生成流程,登录代码只需要构建principal就行。

  1. 构建ClaimsPrincipal(含用户标识和权限声明)
  2. 调用SignInAsync()传递该主体
public async Task Login([FromBody] LoginRequest request) { if (request.Username != "admin" || request.Password != "123456") { return Unauthorized(new { message = "用户名或密码错误" }); } var claims = new[] { new Claim(ClaimTypes.Name, request.Username), new Claim(ClaimTypes.Role, "User") }; var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); var principal = new ClaimsPrincipal(identity); await HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.AddHours(8) }); // 模式一:return Ok(new { message = "登录成功" }); return Ok(new { message = "登录成功,会话已保存在服务端" }); // 模式二(当前) }

3. 使用JWT的认证方式

3.1 集成JWT

接下来我们继续看看JWT认证的方式如何在NETCORE中集成和实现,其实jwt也是无状态的,通常服务器一旦颁发,在token过期之前后期是不可控的。

集成思路:

第一步:注册JWT服务

和集成Cookie认证一样,都需要先注册,然后启用认证中间件,而这里注册的是JWT相关的服务。

var authenticationBuilder = builder.Services.AddAuthentication(options => { // 将默认方案设置为JWT options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }); authenticationBuilder.AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, // 验证签发者,令牌中的 "iss" 字段必须与 ValidIssuer 完全匹配 ValidateAudience = true, // 是否验证受众,保证令牌是发给当前服务的 ValidateLifetime = true, // 是否验证令牌有效期 ValidateIssuerSigningKey = true, // 是否验证令牌签名 ValidIssuer = jwtIssuer, // 合法签发者标识 "https://鉴权服务.com" ValidAudience = jwtAudience, // 受众标识 // 令牌签名验证密钥,对称加密 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("JWTjiamimiyao!")) }; // JWT 认证流程中的事件钩子,未认证时触发 options.Events = new JwtBearerEvents { OnChallenge = context => { // 移除 WW-Authenticate 避免暴露敏感信息 context.HandleResponse(); context.Response.StatusCode = StatusCodes.Status401Unauthorized; context.Response.ContentType = "application/json"; return context.Response.WriteAsync(JsonSerializer.Serialize(new { error = "未认证", message = "Token无效或者过期." })); } }; });
第二步:在接口中使用

直接接口中使用[Authorize(AuthenticationSchemes = "Bearer")]就代表这一组接口都会启用jwt认证的方式来认证,框架就可以完成验签与claims的解析。

[ApiController] [Route("[controller]")] [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] public class ProductController : ControllerBase { private static readonly Product[] Products = [ new Product { Id = 1, Name = "机械键盘", Price = 599.00m } ]; [HttpGet("{id:int}", Name = "GetProductById")] public ActionResult GetById(int id) { var product = Products.FirstOrDefault(p => p.Id == id); if (product is null) { return NotFound(); } return product; } }

那么集成进来之后,当有接口请求时,就会按照如下流程来执行:

因为定义了未授权事件,一旦触发就会收到友好的提示:

http://www.jsqmd.com/news/1139798/

相关文章:

  • BOM双sheet分区表:代理商压货场景下 passive 与 IC 怎么拆、字段怎么填
  • MD5哈希查询工具原理与应用:从算法特性到安全实践
  • 机器人功能区域定位:任务驱动的可操作性接地方法
  • Pikachu靶场XSS漏洞实战:从原理到绕过与防御
  • Python音频音轨分离调整
  • 全球 AI 市场格局定型、物理世界模型商用、国产 AI 立法提速;3.2T 光 / 低轨卫星 / 5G-A 构筑新一代算力通信底座
  • AI产品化落地实践:从技术破局到价值创造
  • 河南郑州 GEO 搜索哪家性价比高?收费标准明细参考
  • 人形机器人展-2026江苏南京具身机器人展览会【官网】
  • Scikit-learn 1.5.0 数据预处理实战:6大核心方法对比与场景选择指南
  • 基础使用2.1 基础模型
  • 浅底金丝纹石英石|温柔干净的轻奢餐桌台面选材
  • 终极指南:STAR RNA-seq比对工具深度解析与实战应用
  • Deep Mutual Learning 算法复现:PyTorch 1.13 实现 CIFAR-100 分类精度提升 1.2%
  • 01_PaperSpotlight|合规科研传播
  • 河南 GEO 搜索引流哪家效果好?工厂 B 端客户获客方案
  • 5分钟掌握:全能型网页资源捕获神器
  • 2026年中小微企业国产无代码开发平台排行榜
  • CentOS企业网络管理课程学习总结与感悟
  • 核心概念总结
  • 影刀RPA新手教程:流程控制综合实战——用一个完整案例串起判断、循环、异常处理
  • Word制作图目录
  • 全球顶会AI论文代码导航站:MLNLP-World开源项目深度解析与从环境配置到学术复现的实战指南
  • brew tap用法
  • 终极指南:如何用哔咔漫画下载器快速批量下载离线漫画
  • 如何快速掌握STAR RNA-seq比对:新手的完整实战指南
  • 公司注销指南:登报清算公告怎么写?登报流程与要求详解
  • cadenceIC的库library,单独复制某个schematic电路图/symbol/layout等到另一个库,出现复制失败的问题
  • 2026最新:运营人视频链接提取文字,3款免费实用神器亲测可用
  • 计算机毕业设计之基于Web技术的家校交流平台