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

【ASP.NET Core】身份认证——Identity标识框架指南

文章目录

  • 前言
  • * 身份认证与授权
  • 一、ASP.NET Core中的Identity
  • 二、Identity框架架构
    • 2.1 Identity上下文
      • 2.1.1 IdentityUserContext
        • 2.1.1.1 IdentityUserContext泛型类
        • 2.1.1.2 核心属性
        • 2.1.1.3 模型创建
      • 2.1.2 IdentityDbContext
        • 2.1.2.1 IdentityDbContext泛型类
        • 2.1.2.2 核心属性
        • 2.1.2.3 模型创建
    • 2.2 Identity Manager
  • 三、Identity的使用
    • 3.1 Nuget引入
    • 3.2 创建用户类和角色类
    • 3.3 dbContext的引用
    • 3.4 Program.cs注册服务
    • 3.5 Controller通过identity manager调用identity实体数据
  • 总结


前言

在ASP.NET Core 中,有两道非常重要的安全访问机制,分别是认证(Authentication)和授权(Authorization)。本篇文章主要介绍在ASP.NET Core中通过官方的Identity身份认证框架来实现访问用户的认证,授权的部分后面会通过另一篇关于JWT的实现来介绍使用。

* 身份认证与授权

在ASP.NET Core 中,身份认证(Authentication)和授权(Authorization)是我们绕不开的用于保障应用的两道安全机制。无论是我们传统开发中基于session或者是现代开发中流行的JWT,本质上还是通过先认证,后授权的方式。

身份认证(Authentication):比方说在登录流程中或使用客户端验证信息验证身份,这都属于身份认证(Authentication)的范畴:

  • 首次登录:用户提交用户名和密码,服务器端验证登录凭据
    • 使用session的情况:服务器生成一个唯一的SessionID,并将用户信息(如身份、权限等)存储在服务器端,将SessionID返回给客户端保存;
    • 使用JWT的情况:服务器生成包含用户身份信息的 JWT 令牌返回给客户端。
  • 完成登录后请求:客户端携带保存的cookie或者JWT
    • 使用session的情况:客户端将唯一SessionID通过cookie传给服务器用于验证身份;
    • 使用JWT的情况:客户端将JWT放在header发送到服务器,服务器验证是否被串改验证身份,解析payload。

授权(Authorization):而在身份已确认的基础上,判断该用户是否有权限访问请求的资源。这些都是属于授权(Authorization)的范畴

  • 访问后台管理员页面:通过上一步身份认证来验证当前用户后,鉴别此用户是否具有管理员权限访问。

换句话说,Authentication就是指系统用于验证当前访问的用户身份,通过用户名密码、cookie、令牌等确认是否是合法用户,也就是解析当前用户是谁。而Authorization是用来确定这个用户是否有权限去访问请求的资源,也就是判断用户能做什么。


一、ASP.NET Core中的Identity

ASP.NET Core Identity是一个管理用户、密码、配置文件数据、角色、声明、令牌、电子邮件确认等的身份验证系统,是微软官方提供的身份认证框架。

比起我们日常工作里自己去设计用户表、密码加密,ASP.NET Core Identity提供了一套契合实际工作生产的身份认证机制,比如封装了登录、注册、重置等常用方法。在一些安全性上也包含了密码哈希加密,账号锁定等策略。并且我们也可以自定义属性用于扩展登录上的一些操作。

  • 标识(ldentity)框架:采用基于角色的访问控制(Role-Based Access Control,简称RBAC)策略,内置了对用户、角色等表的管理以及相关的接口,支持外部登录、2FA等,
  • 标识框架使用EF Core对数据库进行操作,因此标识框架支持几乎所有数据库。
    ASP.NET Core Identity框架提供了一组开箱即用的api,帮助我们去管理身份验证。这里通过webapi的方式搭建一个包含登录和注册和重置密码的操作,下面让我们开始了解并且使用这个官方的身份认证框架。

二、Identity框架架构

2.1 Identity上下文

Identity框架本质上是还是一个基于EF Core的框架,通过两个核心的上下文(IdentityDbContext ,IdentityUserContext )对用户、角色、权限等核心数据结构进行操作。属于数据访问的底层支撑,负责将实体模型映射到数据库,并提供基础的数据操作能力。

其中IdentityUserContext继承自DbContext,IdentityDbContext继承自IdentityUserContext。这两个上下文的区别在于:

  • IdentityUserContext: 作为一个基础上下文,处理用户及用户关联数据,比如用户TUser、用户声明TUserClaim、用户登录TUserLogin、用户令牌TUserToken。简而言之,它适用于仅需用户认证的简单系统,IdentityUserContext只包含对用户相关数据操作。
  • IdentityDbContext:是一个完整的Identity上下文,它继承了IdentityUserContext,并且包含了角色及角色关联的数据。比如角色,角色声明、用户和角色关联关系。它使用于基于角色的访问控制(Role-Based Access Control,简称RBAC)策略的复杂系统。

2.1.1 IdentityUserContext

2.1.1.1 IdentityUserContext泛型类

IdentityUserContext的核心是一个名为IdentityUserContext的抽象泛型类,它接收User相关的类作为构造函数。也包含各种默认的构造方法。

IdentityUserContext< TUser>和 public class IdentityUserContext< TUser, TKey>最后都是通过内部的base关键字,调用抽象类IdentityUserContext< TUser, TKey, TUserClaim, TUserLogin, TUserToken>。

抽象类IdentityUserContext< TUser, TKey, TUserClaim, TUserLogin, TUserToken>继承自DbContext,通过内部base关键字调用DbContext构造函数。

这些泛型类的不同版本,都默认有一个空构造函数。主要是为了兼容EFCore的DbContext的空构造函数,实现约定大于配置。并且在通过工具迁移的时候能够至少保证有无参构造函数供调用。

IdentityUserContext源码

	public class IdentityUserContext<TUser> : IdentityUserContext<TUser, string> where TUser : IdentityUser{/// <summary>/// Initializes a new instance of <see cref="IdentityUserContext{TUser}"/>./// </summary>/// <param name="options">The options to be used by a <see cref="DbContext"/>.</param>public IdentityUserContext(DbContextOptions options) : base(options) { }/// <summary>/// Initializes a new instance of the <see cref="IdentityUserContext{TUser}" /> class./// </summary>protected IdentityUserContext() { }}/// <summary>/// Base class for the Entity Framework database context used for identity./// </summary>/// <typeparam name="TUser">The type of user objects.</typeparam>/// <typeparam name="TKey">The type of the primary key for users and roles.</typeparam>public class IdentityUserContext<TUser, TKey> : IdentityUserContext<TUser, TKey, IdentityUserClaim<TKey>, IdentityUserLogin<TKey>, IdentityUserToken<TKey>>where TUser : IdentityUser<TKey>where TKey : IEquatable<TKey>{/// <summary>/// Initializes a new instance of the db context./// </summary>/// <param name="options">The options to be used by a <see cref="DbContext"/>.</param>public IdentityUserContext(DbContextOptions options) : base(options) { }/// <summary>/// Initializes a new instance of the class./// </summary>protected IdentityUserContext() { }}/// <summary>/// Base class for the Entity Framework database context used for identity./// </summary>/// <typeparam name="TUser">The type of user objects.</typeparam>/// <typeparam name="TKey">The type of the primary key for users and roles.</typeparam>/// <typeparam name="TUserClaim">The type of the user claim object.</typeparam>/// <typeparam name="TUserLogin">The type of the user login object.</typeparam>
/// <typeparam name="TUserToken">The type of the user token object.</typeparam>
public abstract class IdentityUserContext<TUser, TKey, TUserClaim, TUserLogin, TUserToken> : DbContextwhere TUser : IdentityUser<TKey>where TKey : IEquatable<TKey>where TUserClaim : IdentityUserClaim<TKey>where TUserLogin : IdentityUserLogin<TKey>where TUserToken : IdentityUserToken<TKey>
{/// <summary>/// Initializes a new instance of the class./// </summary>/// <param name="options">The options to be used by a <see cref="DbContext"/>.</param>public IdentityUserContext(DbContextOptions options) : base(options) { }/// <summary>/// Initializes a new instance of the class./// </summary>protected IdentityUserContext() { }
}
2.1.1.2 核心属性

IdentityUserContext包含四个核心属性,对应 Identity 框架中存储用户相关数据的数据库表。主要用于管理用户身份信息。

属性被vritual修饰,允许子类重写,实现自定义逻辑。比如添加一些操作审计日志之类的。

IdentityUserContext方法

 /// <summary>/// Gets or sets the <see cref="DbSet{TEntity}"/> of Users./// </summary>public virtual DbSet<TUser> Users { get; set; } = default!;/// <summary>/// Gets or sets the <see cref="DbSet{TEntity}"/> of User claims./// </summary>public virtual DbSet<TUserClaim> UserClaims { get; set; } = default!;/// <summary>/// Gets or sets the <see cref="DbSet{TEntity}"/> of User logins./// </summary>public virtual DbSet<TUserLogin> UserLogins { get; set; } = default!;/// <summary>/// Gets or sets the <see cref="DbSet{TEntity}"/> of User tokens./// </summary>public virtual DbSet<TUserToken> UserTokens { get; set; } = default!;
2.1.1.3 模型创建

IdentityUserContext通过一个OnModelCreating重载,实现了TUser,TUserClaim,TUserLogin,TUserToken模型的配置。具体的初始化方法在OnModelCreating调用的OnModelCreatingVersion里。值得注意的是OnModelCreatingVersion1和OnModelCreatingVersion2都是虚方法,方便子类重写。

protected override void OnModelCreating(ModelBuilder builder)
{var version = GetStoreOptions()?.SchemaVersion ?? IdentitySchemaVersions.Version1;OnModelCreatingVersion(builder, version);
}internal virtual void OnModelCreatingVersion(ModelBuilder builder, Version schemaVersion)
{if (schemaVersion >= IdentitySchemaVersions.Version2){OnModelCreatingVersion2(builder);}else{OnModelCreatingVersion1(builder);}
}internal virtual void OnModelCreatingVersion2(ModelBuilder builder)
{
}internal virtual void OnModelCreatingVersion1(ModelBuilder builder)
{}

2.1.2 IdentityDbContext

2.1.2.1 IdentityDbContext泛型类

和IdentityUserContext类似,IdentityDbContext的核心也是一个名为IdentityDbContext的抽象泛型类,它接收User和Role相关的类(TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken)作为构造函数。也包含各种默认的构造方法。

IdentityDbContext, IdentityDbContext< TUser>和 IdentityDbContext< TUser, TRole, TKey>最后都是通过内部的base关键字,调用抽象类IdentityDbContext< TUser, TKey, TUserClaim, TUserLogin, TUserToken>。

IdentityDbContext< TUser, TKey, TUserClaim, TUserLogin, TUserToken>继承自IdentityUserContext,使用IdentityUserContext<TUser, TKey, TUserClaim, TUserLogin, TUserToken>的构造函数。在内部通过base关键字调用IdentityUserContext的构造函数。

IdentityUserContext源码

public class IdentityDbContext : IdentityDbContext<IdentityUser, IdentityRole, string>
{public IdentityDbContext(DbContextOptions options) : base(options) { }protected IdentityDbContext() { }
}public class IdentityDbContext<TUser> : IdentityDbContext<TUser, IdentityRole, string> where TUser : IdentityUser
{public IdentityDbContext(DbContextOptions options) : base(options) { }protected IdentityDbContext() { }
}public class IdentityDbContext<TUser, TRole, TKey> : IdentityDbContext<TUser, TRole, TKey, IdentityUserClaim<TKey>, IdentityUserRole<TKey>, IdentityUserLogin<TKey>, IdentityRoleClaim<TKey>, IdentityUserToken<TKey>>where TUser : IdentityUser<TKey>where TRole : IdentityRole<TKey>where TKey : IEquatable<TKey>
{public IdentityDbContext(DbContextOptions options) : base(options) { }protected IdentityDbContext() { }
}public abstract class IdentityDbContext<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken> : IdentityUserContext<TUser, TKey, TUserClaim, TUserLogin, TUserToken>where TUser : IdentityUser<TKey>where TRole : IdentityRole<TKey>where TKey : IEquatable<TKey>where TUserClaim : IdentityUserClaim<TKey>where TUserRole : IdentityUserRole<TKey>where TUserLogin : IdentityUserLogin<TKey>where TRoleClaim : IdentityRoleClaim<TKey>where TUserToken : IdentityUserToken<TKey>
{public IdentityDbContext(DbContextOptions options) : base(options) { }protected IdentityDbContext() { }
}
2.1.2.2 核心属性

比起IdentityUserContext已经包含的用户属性,IdentityDbContext内部有三个角色相关的属性——UserRoles,Roles和RoleClaims。对应 Identity 框架中存储角色相关数据的数据库表。主要用于管理角色,角色声明和用户角色绑定信息。

属性同样被vritual修饰,允许子类重写,实现自定义逻辑。

IdentityDbContext方法

 /// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of User roles.
/// </summary>
public virtual DbSet<TUserRole> UserRoles { get; set; } = default!;/// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of roles.
/// </summary>
public virtual DbSet<TRole> Roles { get; set; } = default!;/// <summary>
/// Gets or sets the <see cref="DbSet{TEntity}"/> of role claims.
/// </summary>
public virtual DbSet<TRoleClaim> RoleClaims { get; set; } = default!;
2.1.2.3 模型创建

IdentityUserContext通过一个OnModelCreating重载,实现了TUser(指定外键关系),TRole,TRoleClaim,TUserRole模型的配置。(具体方法在OnModelCreating调用的OnModelCreatingVersion里)


protected override void OnModelCreating(ModelBuilder builder)
{base.OnModelCreating(builder);
}

前面我们提到,IdentityUserContext里的OnModelCreatingVersion虚方法。这里IdentityDbContext继承自IdentityUserContext,重写了OnModelCreatingVersion虚方法。通过base关键字调用父类的OnModelCreatingVersion2方法,初始化User相关的配置,然后在下面继续编写Role相关的配置。

internal override void OnModelCreatingVersion2(ModelBuilder builder)
{base.OnModelCreatingVersion2(builder);// Current no differences between Version 2 and Version 1builder.Entity<TUser>(b =>{b.HasMany<TUserRole>().WithOne().HasForeignKey(ur => ur.UserId).IsRequired();});builder.Entity<TRole>(b =>{/.../ });builder.Entity<TRoleClaim>(b =>{/.../ });builder.Entity<TUserRole>(b =>{/.../ });
}

2.2 Identity Manager

Identity框架中,Managers可以理解为一个业务逻辑的封装,封装身份认证的核心逻辑。Manager 组件有UserManager、SignInManager,RoleManager。

  • UserManager< TUser>:提供用于在持久性存储中管理用户的 API,负责用户的创建、查询、更新、删除、密码管理、角色分配等操作
    • 创建用户 CreateAsync()
    • 删除用户 DeleteAsync()
    • 验证密码 CheckPasswordAsync()
    • 修改密码 ChangePasswordAsync()
    • 分配角色 AddToRoleAsync()
    • 移除角色 RemoveFromRoleAsync()
    • 添加用户声明 AddClaimAsync()
    • 获取用户声明 GetClaimsAsync()
    • 锁定用户 SetLockoutEndDateAsync() 【直到指定的结束日期过去。 设置过去结束日期会立即解锁用户 】
    • 完整API: 微软官方文档
  • SignInManager< TUser>:提供用于用户登录的 API,负责用户登录、注销、身份验证等会话管理操作
    • 密码登录 PasswordSignInAsync()
    • 外部登录用户 ExternalLoginSignInAsync()
    • 注销 SignOutAsync()
    • 验证用户是否已登录 IsSignedIn()
    • 双因素认证 TwoFactorRecoveryCodeSignInAsync()
    • 完整API: 微软官方文档
  • RoleManager< TRole>:提供用于管理持久性存储区中角色的 API,用于角色的创建、查询、更新、删除等管理操作
    • 创建角色 CreateAsync()
    • 删除角色 DeleteAsync()
    • 检查角色是否存在 RoleExistsAsync()
    • 为角色添加声明 AddClaimAsync()
    • 为角色移除声明 RemoveClaimAsync()
    • 完整API: 微软官方文档

这类Manager不直接与上文提到的Identity上下文的直接依赖,而是通过IUserStore或者IRoleStore间接访问上下文。UserManager通过IUserStore访问IdentityUserContext上下文,RoleManager通过IRoleStore访问IdentityDbContext上下文,SignInManager依赖UserManager。

这样一来,我们就能在Controller里,通过依赖注入各类identity Manager,调用identity实体数据。

3.5小结有关于UserManager,RoleManager的使用示例

三、Identity的使用

3.1 Nuget引入

dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore --version 9.0.8

3.2 创建用户类和角色类

ldentity框架采用的基于角色的访问控制策略,我们需要建立两个基础类来作为实际数据的映射,分别是用户类和角色类。它们需要各种继承自一个IdentityUser和IdentityRole的泛型

IdentityUser和IdentityRole的泛型类型必须一致,也就是User类和Role类的主键类型必须一致,不然后续对IdentityDbContext的操作会无法编译通过

public class User: IdentityUser<long>
{public string? NickName { get; set; }public long? RowVersion { get; set; }
}public class Role : IdentityRole<long>
{
}

3.3 dbContext的引用

这里我们引用完整的Identity上下文——IdentityDbContext。
IdentityDbContext支持传入自定义User和Role写法的构造方法,第三个参数是全部Identity相关实体的主键。

public class CoreDbContext : IdentityDbContext<User, Role, long>
{public CoreDbContext(DbContextOptions<CoreDbContext> options) : base(options) { }protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){//base.OnConfiguring(optionsBuilder);}protected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);}
}

3.4 Program.cs注册服务

builder.Services里注册Identity相关的配置。

// 配置数据保护服务,用于加密和解密数据
builder.Services.AddDataProtection();
// 配置Identity身份验证服务,设置用户和角色管理选项
builder.Services.AddIdentity<User,Role>(options =>
{// 设置密码策略选项options.Password.RequireDigit = false;           // 密码不要求包含数字options.Password.RequiredLength = 6;             // 密码最小长度为6位options.Password.RequireLowercase = false;       // 密码不要求包含小写字母options.Password.RequireNonAlphanumeric = false; // 密码不要求包含非字母数字字符options.Password.RequireUppercase = false;       // 密码不要求包含大写字母// 设置令牌提供程序选项options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider;      // 密码重置令牌使用默认邮件提供程序options.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider;  // 邮箱确认令牌使用默认邮件提供程序
})
// 配置EntityFramework存储提供程序,使用CoreDbContext作为数据上下文
.AddEntityFrameworkStores<CoreDbContext>()
// 添加默认令牌提供程序用于生成安全令牌
.AddDefaultTokenProviders();

3.5 Controller通过identity manager调用identity实体数据

完整的登录,注册,重置和登出方法。
邮箱服务可以参考我的另一篇博客
链接: 【ASP.NET Core】基于MailKit(SMTP 协议)实现邮件发送

    [Route("api/[controller]/[action]")][ApiController]public class AuthController : ControllerBase{private readonly ILogger<AuthController> _logger;private readonly IWebHostEnvironment _webHostEnvironment;private readonly IJWTService _jwtService;private readonly UserManager<User> _userManager;private readonly RoleManager<Role> _roleManager;private readonly SignInManager<User> _signInManager;private readonly EmailService _emailService;public AuthController(ILogger<AuthController> logger, IJWTService jwtService, UserManager<User> userManager, RoleManager<Role> roleManager, IWebHostEnvironment webHostEnvironment = null, EmailService emailService = null){_logger = logger;_jwtService = jwtService;_userManager = userManager;_roleManager = roleManager;_webHostEnvironment = webHostEnvironment;_emailService = emailService;}/// <summary>/// 登录/// </summary>/// <param name="loginUser"></param>/// <returns></returns>[HttpPost]public async Task<ActionResult<LoginResponse>> Login([FromBody] LoginUserReq loginUser){if (loginUser.UserName == null && loginUser.Email == null){return Ok("请输入用户名或邮箱");}User? user = await _userManager.FindByNameAsync(loginUser.UserName);if (user == null){user = await _userManager.FindByEmailAsync(loginUser.Email);if (user == null){if (_webHostEnvironment.IsDevelopment()){return Ok("用户不存在");}else{return Ok("账号或密码错误");}}}if (await _userManager.IsLockedOutAsync(user)){return Ok($"用户被锁,{user.LockoutEnd}");}if (!await _userManager.CheckPasswordAsync(user, loginUser.Password)){if (_webHostEnvironment.IsDevelopment()){return Ok("登录密码错误");}else{return BadRequest("账号或密码错误");}}HttpUser httpUser = new HttpUser(){UserId = user.Id,Name = user.UserName,NickName = user.NickName,RoleList = (await _userManager.GetRolesAsync(user)).ToList(),};var token = _jwtService.GenerateToken(httpUser);return new LoginResponse(){Token = token};}/// <summary>/// 发送重置密码验证码/// </summary>/// <param name="userName"></param>/// <returns></returns>[HttpGet]public async Task<ActionResult> SendRestPasswordCode(string userName){User? user = await _userManager.FindByNameAsync(userName);if (user == null){if (_webHostEnvironment.IsDevelopment()){return Ok("用户不存在");}return Ok("执行有误");}if (user.Email == null || user.Email == ""){return Ok("用户没有邮箱");}var token = await _userManager.GeneratePasswordResetTokenAsync(user);try{await _emailService.SendEmailAsync(user.Email,"重置密码",$"<h1>验证码如下</h1><p>{token}</p>");return Content("邮件发送成功");}catch (Exception ex){return Content($"邮件发送失败: {ex.Message}");}}/// <summary>/// 重置密码/// </summary>/// <param name="resetPasswordReq"></param>/// <returns></returns>[HttpPost]public async Task<ActionResult> ResetPassword(ResetPasswordReq resetPasswordReq){var user = await _userManager.FindByNameAsync(resetPasswordReq.UserName);if (user == null){if (_webHostEnvironment.IsDevelopment()){return Ok("用户不存在");}return Ok("执行有误");}var result = await _userManager.ResetPasswordAsync(user, resetPasswordReq.Token, resetPasswordReq.Password);if (result.Succeeded){return Ok("密码重置成功");}return Ok("密码重置失败");}/// <summary>/// 注册/// </summary>/// <param name="registerUser"></param>/// <returns></returns>[HttpPost]public async Task<ActionResult> Register([FromBody] RegisterUserReq registerUser){if (!ModelState.IsValid){return BadRequest(ModelState);}var user = new User(){UserName = registerUser.UserName,Email = registerUser.Email,EmailConfirmed = false, // 初始设置为未验证};var result = await _userManager.CreateAsync(user, registerUser.Password);if (result.Succeeded){//生产邮箱验证码var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));try{await _emailService.SendEmailAsync(user.Email,"账号注册",$"<h1>验证链接如下</h1><p>https://localhost:7154/api/auth/ConfirmRegister/{user.Id}/{code}</p>");return Content("邮件发送成功");}catch (Exception ex){return Content($"邮件发送失败: {ex.Message}");}}foreach (var error in result.Errors){ModelState.AddModelError(string.Empty, error.Description);}return BadRequest(ModelState);}/// <summary>/// 确认注册/// </summary>/// <param name="userId"></param>/// <param name="code"></param>/// <returns></returns>[HttpGet("{userId}/{code}")]public async Task<ActionResult> ConfirmRegister(string userId, string code){if (userId == null || code == null){return BadRequest("请输入合适的参数");}var user = await _userManager.FindByIdAsync(userId);if (user == null){return NotFound($"用户名无效。");}// 解码令牌var decodedCode = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));// 确认邮箱var result = await _userManager.ConfirmEmailAsync(user, decodedCode);if (result.Succeeded){var created = await _userManager.AddToRoleAsync(user, "admin");if (!created.Succeeded){return Ok("用户添加角色失败");}return Ok("验证成功");}else{return BadRequest("验证失败");}}/// <summary>/// 登出/// </summary>/// <returns></returns>public async Task<ActionResult> LoginOut(string userId){await _signInManager.SignOutAsync();return Ok("登出成功");}}

总结

以上就是如何使用Identity 框架快速实现用户管理功能,当然Identity标识框架的功能不止于此,后续我会大家总结各种特殊的用法。

http://www.dtcms.com/a/394845.html

相关文章:

  • [Nodejs+LangChain+Ollama] 2.提示词实践
  • i.MX6ULL移植内核6.6(二)GPIO子系统和LED子系统
  • VLC Media取消视频文件名前置显示
  • 在unity urp项目中 通过图片创建材质(透明光晕)
  • OneSignal PHP SDK v2 官方资源
  • 如何透过批次模拟加速3D模型建立
  • PDF清晰度提升工具,让模糊文档变清晰
  • 设计模式六大原则
  • QML 多路 RTSP 视频流实时预览实现
  • glTF/glb:现在和未来
  • 构建以ERP为核心的智能制造运营中心(MOM)
  • Java:toArray(new String[0])
  • Trilium Notes+cpolar:打造随身个人知识库的智能中枢
  • 无人机图传技术详解:为何云望图传信号传输能力远超WiFi?,无人机wifi图传是什么意思
  • 水题记录2.1
  • 企业智能工作流的无界解决方案由CherryStudio+cpolar解决
  • Nginx高级用法案例汇总
  • Python开发:使用FastAPI创建后端服务
  • Nginx配置中location和proxy_pass指令尾部是否带斜杠的区别
  • Nginx核心配置
  • 医院不良事件管理系统:提升医疗安全的智能化解决方案
  • 【$.post回调函数未被执行的原因分析】,第048篇
  • 远程连接服务器的远程重启办法shutdown -r -t 0
  • 【js】关于JWT的前端存储新思路
  • Unity官方Dots范例工程学习——Jobs101
  • 如何在SQLite中实现事务处理?
  • 广东省省考备考(第一百零四天9.22)——判断推理(强化训练)
  • k8s 常用命令
  • windows远程桌面服务安全加固的配置指南
  • datawhale玩转通义四大新模型 202509 第4次作业