在前面我们完成了应用最基础的功能支持以及数据库配置,接下来就是我们的用户角色登录等功能了,在asp.net core中原生Identity可以让我们快速完成这个功能的开发,在.NET8中,asp.net core identity支持了WebApi的注册登录。这让我们在WebApi中可以更爽快的使用。

安装包

首先我们需要安装Microsoft.AspNetCore.Identity.EntityFrameworkCore这个包来创建我们的数据库结构

创建实体

在asp.net core identity中默认包含了IdentityUser,IdentityRole,IdentityRoleClaim,IdentityUserClaim,IdentityUserLogin,IdentityUserRole,IdentityUserToken这几个基类,我们可以直接使用这些,也可以通过继承来灵活扩展我们的表结构。当然,可以按照约定不使用继承的方式,创建类添加必要的属性字段也可。
这里我们选择把所有的类都继承一遍,方便以后扩展。

namespace Wheel.Domain.Identity
{
public class User : IdentityUser, IEntity<string>
{
public virtual DateTimeOffset CreationTime { get; set; }
public virtual ICollection<UserClaim> Claims { get; set; }
public virtual ICollection<UserLogin> Logins { get; set; }
public virtual ICollection<UserToken> Tokens { get; set; }
public virtual ICollection<UserRole>? UserRoles { get; set; }
}
}
namespace Wheel.Domain.Identity
{
public class Role : IdentityRole, IEntity<string>
{
/// <summary>
/// 角色类型,0管理台角色,1客户端角色
/// </summary>
public RoleType RoleType { get; set; } public Role(string roleName, RoleType roleType) : base (roleName)
{
RoleType = roleType;
} public Role(string roleName) : base (roleName)
{
} public Role() : base ()
{
} public virtual ICollection<UserRole> UserRoles { get; set; }
public virtual ICollection<RoleClaim> RoleClaims { get; set; }
}
}

这里主要展示一下User和Role,别的可自行查看代码仓库。

修改DbContext

在WheelDbContext继承IdentityDbContext,IdentityDbContext支持传入我们的泛型User,Role类型。

namespace Wheel.EntityFrameworkCore
{
public class WheelDbContext : IdentityDbContext<User, Role, string, UserClaim, UserRole, UserLogin, RoleClaim, UserToken>
{
private StoreOptions? GetStoreOptions() => this.GetService<IDbContextOptions>()
.Extensions.OfType<CoreOptionsExtension>()
.FirstOrDefault()?.ApplicationServiceProvider
?.GetService<IOptions<IdentityOptions>>()
?.Value?.Stores; public WheelDbContext(DbContextOptions<WheelDbContext> options) : base(options)
{ } protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder); ConfigureIdentity(builder);
} void ConfigureIdentity(ModelBuilder builder)
{
var storeOptions = GetStoreOptions();
var maxKeyLength = storeOptions?.MaxLengthForKeys ?? 0; builder.Entity<User>(b =>
{
b.HasKey(u => u.Id);
b.HasIndex(u => u.NormalizedUserName).HasDatabaseName("UserNameIndex").IsUnique();
b.HasIndex(u => u.NormalizedEmail).HasDatabaseName("EmailIndex");
b.ToTable("Users");
b.Property(u => u.ConcurrencyStamp).IsConcurrencyToken(); b.Property(u => u.Id).HasMaxLength(36);
b.Property(u => u.UserName).HasMaxLength(256);
b.Property(u => u.NormalizedUserName).HasMaxLength(256);
b.Property(u => u.Email).HasMaxLength(256);
b.Property(u => u.NormalizedEmail).HasMaxLength(256);
b.Property(u => u.CreationTime).HasDefaultValue(DateTimeOffset.Now); b.HasMany(e => e.Claims)
.WithOne(e => e.User)
.HasForeignKey(uc => uc.UserId)
.IsRequired(); b.HasMany(e => e.Logins)
.WithOne(e => e.User)
.HasForeignKey(ul => ul.UserId)
.IsRequired(); b.HasMany(e => e.Tokens)
.WithOne(e => e.User)
.HasForeignKey(ut => ut.UserId)
.IsRequired(); b.HasMany(e => e.UserRoles)
.WithOne(e => e.User)
.HasForeignKey(ur => ur.UserId)
.IsRequired();
});
builder.Entity<UserClaim>(b =>
{
b.HasKey(uc => uc.Id);
b.ToTable("UserClaims");
});
builder.Entity<UserLogin>(b =>
{
b.HasKey(l => new { l.LoginProvider, l.ProviderKey }); if (maxKeyLength > 0)
{
b.Property(l => l.LoginProvider).HasMaxLength(maxKeyLength);
b.Property(l => l.ProviderKey).HasMaxLength(maxKeyLength);
} b.ToTable("UserLogins");
});
builder.Entity<UserToken>(b =>
{
b.HasKey(t => new { t.UserId, t.LoginProvider, t.Name }); if (maxKeyLength > 0)
{
b.Property(t => t.LoginProvider).HasMaxLength(maxKeyLength);
b.Property(t => t.Name).HasMaxLength(maxKeyLength);
}
b.ToTable("UserTokens");
});
builder.Entity<Role>(b =>
{
b.HasKey(r => r.Id);
b.HasIndex(r => r.NormalizedName).HasDatabaseName("RoleNameIndex").IsUnique();
b.ToTable("Roles");
b.Property(u => u.Id).HasMaxLength(36);
b.Property(r => r.ConcurrencyStamp).IsConcurrencyToken(); b.Property(u => u.Name).HasMaxLength(256);
b.Property(u => u.NormalizedName).HasMaxLength(256); b.HasMany(e => e.UserRoles)
.WithOne(e => e.Role)
.HasForeignKey(ur => ur.RoleId)
.IsRequired(); b.HasMany(e => e.RoleClaims)
.WithOne(e => e.Role)
.HasForeignKey(rc => rc.RoleId)
.IsRequired();
}); builder.Entity<RoleClaim>(b =>
{
b.HasKey(rc => rc.Id);
b.ToTable("RoleClaims");
}); builder.Entity<UserRole>(b =>
{
b.HasKey(r => new { r.UserId, r.RoleId });
b.ToTable("UserRoles");
});
}
}
}

执行数据库迁移命令

接下来我们使用VS的程序包管理器控制台。
使用命令创建和执行迁移文件:

Add-Migration Init
Update-Database

这里也可以使用Dotnet EF命令:

dotnet ef migrations add Init
dotnet ef database update

执行完命令后我们连接数据库即可看到表成功创建。

配置Identity

在Program中添加下面代码:

builder.Services.AddIdentityCore<User>()
.AddRoles<Role>()
.AddEntityFrameworkStores<WheelDbContext>()
.AddApiEndpoints();

这里指定了Identity用户类型以及角色类型,并且指定EF操作的DbContext。
AddApiEndpoints则是注入WebAPI所需的服务,我们F12进去可以看到里面的配置。

/// <summary>
/// Adds configuration and services needed to support <see cref="IdentityApiEndpointRouteBuilderExtensions.MapIdentityApi{TUser}(IEndpointRouteBuilder)"/>
/// but does not configure authentication. Call <see cref="BearerTokenExtensions.AddBearerToken(AuthenticationBuilder, Action{BearerTokenOptions}?)"/> and/or
/// <see cref="IdentityCookieAuthenticationBuilderExtensions.AddIdentityCookies(AuthenticationBuilder)"/> to configure authentication separately.
/// </summary>
/// <param name="builder">The <see cref="IdentityBuilder"/>.</param>
/// <returns>The <see cref="IdentityBuilder"/>.</returns>
public static IdentityBuilder AddApiEndpoints(this IdentityBuilder builder)
{
ArgumentNullException.ThrowIfNull(builder); builder.AddSignInManager();
builder.AddDefaultTokenProviders();
builder.Services.TryAddTransient<IEmailSender, NoOpEmailSender>();
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<JsonOptions>, IdentityEndpointsJsonOptionsSetup>());
return builder;
}

接下来就是配置API了,在中间件中添加MapIdentityApi:

app.MapGroup("api/identity")
.WithTags("Identity")
.MapIdentityApi<User>();

这里需要注意的是,如果不先MapGroup,则我们的请求路径只直接从/开始的,MapGroup("api/identity")则是指定从/api/identity开始。WithTags则是指定我们Swagger生成API的Tag显示名称。
下面两图可以看到区别:


直接调用register和login方法即可完成注册登录,这里只贴上一个登录返回的截图,可以看到我们成功拿到了accessToken以及refreshToken。

使用Post带上token请求/api/identity/manage/info。成功拿到用户信息。

这样我们就轻轻松松完成了asp.net core identity对WebApi的集成了。

轮子仓库地址https://github.com/Wheel-Framework/Wheel

欢迎进群催更。

造轮子之asp.net core identity的更多相关文章

  1. IdentityServer(12)- 使用 ASP.NET Core Identity

    IdentityServer具有非常好的扩展性,其中用户及其数据(包括密码)部分你可以使用任何想要的数据库进行持久化. 如果需要一个新的用户数据库,那么ASP.NET Core Identity是你的 ...

  2. ASP.NET Core Identity Hands On(1)——Identity 初次体验

    ASP.NET Core Identity是用于构建ASP.NET Core Web应用程序的成员资格系统,包括成员资格.登录和用户数据存储 这是来自于 ASP.NET Core Identity 仓 ...

  3. ASP.NET Core Identity Hands On(2)——注册、登录、Claim

    上一篇文章(ASP.NET Core Identity Hands On(1)--Identity 初次体验)中,我们初识了Identity,并且详细分析了AspNetUsers用户存储表,这篇我们将 ...

  4. IdentityServer4 中文文档 -14- (快速入门)使用 ASP.NET Core Identity

    IdentityServer4 中文文档 -14- (快速入门)使用 ASP.NET Core Identity 原文:http://docs.identityserver.io/en/release ...

  5. IdentityServer4【QuickStart】之使用asp.net core Identity

    使用asp.net core Identity IdentityServer灵活的设计中有一部分是可以将你的用户和他们的数据保存到数据库中的.如果你以一个新的用户数据库开始,那么,asp.net co ...

  6. ASP.NET Core Identity 实战(2)——注册、登录、Claim

    上一篇文章(ASP.NET Core Identity Hands On(1)--Identity 初次体验)中,我们初识了Identity,并且详细分析了AspNetUsers用户存储表,这篇我们将 ...

  7. ASP.NET Core Identity 实战(4)授权过程

    这篇文章我们将一起来学习 Asp.Net Core 中的(注:这样描述不准确,稍后你会明白)授权过程 前情提要 在之前的文章里,我们有提到认证和授权是两个分开的过程,而且认证过程不属于Identity ...

  8. 用一个应用场景理解ASP.NET Core Identity是什么?

    目录 前言 基于声明的认证(Claims-based Authentication) 应用场景一 在ASP.NET Core 中Identity是如何实现的 类ClaimsPrincipal 考察另外 ...

  9. 用例子看ASP.NET Core Identity是什么?

    原文:用例子看ASP.NET Core Identity是什么? 目录 前言 基于声明的认证(Claims-based Authentication) Claim 在ASP.NET Core Iden ...

  10. ASP.NET Core Identity 迁移数据 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core Identity 迁移数据 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core Identity 迁移数据 上一章节中我们配置了 ...

随机推荐

  1. 前端vue实现页面加水印文字 单个页面所有页面加水印 水印颜色

    前端vue实现页面加水印文字, 可以实现系统所有页面加水印,也可以单个页面加水印, 可更改水印颜色, 下载完整代码请访问uni-app插件市场地址: https://ext.dcloud.net.cn ...

  2. decode php解密代码,方便好用,请收藏

    <?php //已经加密的文件内容 $a = "eval(gzinflate(base64_decode('tVRNb+IwEL3vr/AhwomU5WOPVHSF2lSg7QJK0j ...

  3. 零基础实现Java直播(一):集成

    从泛娱乐的秀场直播,到直播带货潮流中的电商直播,直播已经成为2020年最热的应用技术之一,越来越多的产品希望加入直播功能.下面我们基于即构Express Video SDK,来分享如何从零实现线上直播 ...

  4. Centos7 安装部署 Kubernetes(k8s) 高可用集群

    目录 一.系统环境 二.前言 三.Kubernetes(k8s)高可用简介 四.配置机器基本环境 五.部署haproxy负载均衡器 六.部署etcd集群 七.部署Kubernetes(k8s) mas ...

  5. Win32编程

    WIN32 malloc函数的底层实现是Win32API 字符编码 原始的ASCII编码最多能表示127个符号 0-7F(十六进制) 缺点:表示的符号太少了 ASCII编码的扩展:GB2312或GB2 ...

  6. 正交实例二(不规则数据的测试:也就是因子数和水平数不是正好满足正交表)allpairs的使用即下载

    allpairs工具的使用 作用可以针对不规则的数据生成用例 下载地址: 链接:https://pan.baidu.com/s/1SgvciN427z_WRzA5QG5eJg ** 提取码:52yj* ...

  7. debezium同步postgresql数据至kafka笔记

    实验环境 全部部署于本地虚拟机 debezium docker部署 postgresql.kafka本机部署 1 postgresql 1.1 配置 设置postgres密码为123 仿照exampl ...

  8. 为什么Python是数据科学家的首选语言

    这篇文章全面探讨了Python作为数据科学领域首选语言的原因.从Python的历史.特性,到在数据科学中的应用实例,再到与其他数据科学语言的比较,以及在实际企业中的应用,我们深入剖析了Python的优 ...

  9. java读取txt文件解决乱码问题

    说明:由于txt文件有bom和不同的编码方式,导致导入数据时产生乱码,以下代码完美解决乱码问题. 参考他人代码,结合自己的业务加工完成,费了大半天功夫完成,希望对大家有点用处. 废话不多说,直接上代码 ...

  10. 关于 Task 简单梳理(C#)【并发编程系列】

    〇.前言 Task 是微软在 .Net 4.0 时代推出来的,也是微软极力推荐的一种多线程的处理方式. 在 Task 之前有一个高效多线程操作类 ThreadPool,虽然线程池相对于 Thread, ...