经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
造轮子之asp.net core identity
来源:cnblogs  作者:饭勺oO  时间:2023/10/9 11:12:14  对本文有异议

在前面我们完成了应用最基础的功能支持以及数据库配置,接下来就是我们的用户角色登录等功能了,在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这几个基类,我们可以直接使用这些,也可以通过继承来灵活扩展我们的表结构。当然,可以按照约定不使用继承的方式,创建类添加必要的属性字段也可。
这里我们选择把所有的类都继承一遍,方便以后扩展。

  1. namespace Wheel.Domain.Identity
  2. {
  3. public class User : IdentityUser, IEntity<string>
  4. {
  5. public virtual DateTimeOffset CreationTime { get; set; }
  6. public virtual ICollection<UserClaim> Claims { get; set; }
  7. public virtual ICollection<UserLogin> Logins { get; set; }
  8. public virtual ICollection<UserToken> Tokens { get; set; }
  9. public virtual ICollection<UserRole>? UserRoles { get; set; }
  10. }
  11. }
  1. namespace Wheel.Domain.Identity
  2. {
  3. public class Role : IdentityRole, IEntity<string>
  4. {
  5. /// <summary>
  6. /// 角色类型,0管理台角色,1客户端角色
  7. /// </summary>
  8. public RoleType RoleType { get; set; }
  9. public Role(string roleName, RoleType roleType) : base (roleName)
  10. {
  11. RoleType = roleType;
  12. }
  13. public Role(string roleName) : base (roleName)
  14. {
  15. }
  16. public Role() : base ()
  17. {
  18. }
  19. public virtual ICollection<UserRole> UserRoles { get; set; }
  20. public virtual ICollection<RoleClaim> RoleClaims { get; set; }
  21. }
  22. }

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

修改DbContext

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

  1. namespace Wheel.EntityFrameworkCore
  2. {
  3. public class WheelDbContext : IdentityDbContext<User, Role, string, UserClaim, UserRole, UserLogin, RoleClaim, UserToken>
  4. {
  5. private StoreOptions? GetStoreOptions() => this.GetService<IDbContextOptions>()
  6. .Extensions.OfType<CoreOptionsExtension>()
  7. .FirstOrDefault()?.ApplicationServiceProvider
  8. ?.GetService<IOptions<IdentityOptions>>()
  9. ?.Value?.Stores;
  10. public WheelDbContext(DbContextOptions<WheelDbContext> options) : base(options)
  11. {
  12. }
  13. protected override void OnModelCreating(ModelBuilder builder)
  14. {
  15. base.OnModelCreating(builder);
  16. ConfigureIdentity(builder);
  17. }
  18. void ConfigureIdentity(ModelBuilder builder)
  19. {
  20. var storeOptions = GetStoreOptions();
  21. var maxKeyLength = storeOptions?.MaxLengthForKeys ?? 0;
  22. builder.Entity<User>(b =>
  23. {
  24. b.HasKey(u => u.Id);
  25. b.HasIndex(u => u.NormalizedUserName).HasDatabaseName("UserNameIndex").IsUnique();
  26. b.HasIndex(u => u.NormalizedEmail).HasDatabaseName("EmailIndex");
  27. b.ToTable("Users");
  28. b.Property(u => u.ConcurrencyStamp).IsConcurrencyToken();
  29. b.Property(u => u.Id).HasMaxLength(36);
  30. b.Property(u => u.UserName).HasMaxLength(256);
  31. b.Property(u => u.NormalizedUserName).HasMaxLength(256);
  32. b.Property(u => u.Email).HasMaxLength(256);
  33. b.Property(u => u.NormalizedEmail).HasMaxLength(256);
  34. b.Property(u => u.CreationTime).HasDefaultValue(DateTimeOffset.Now);
  35. b.HasMany(e => e.Claims)
  36. .WithOne(e => e.User)
  37. .HasForeignKey(uc => uc.UserId)
  38. .IsRequired();
  39. b.HasMany(e => e.Logins)
  40. .WithOne(e => e.User)
  41. .HasForeignKey(ul => ul.UserId)
  42. .IsRequired();
  43. b.HasMany(e => e.Tokens)
  44. .WithOne(e => e.User)
  45. .HasForeignKey(ut => ut.UserId)
  46. .IsRequired();
  47. b.HasMany(e => e.UserRoles)
  48. .WithOne(e => e.User)
  49. .HasForeignKey(ur => ur.UserId)
  50. .IsRequired();
  51. });
  52. builder.Entity<UserClaim>(b =>
  53. {
  54. b.HasKey(uc => uc.Id);
  55. b.ToTable("UserClaims");
  56. });
  57. builder.Entity<UserLogin>(b =>
  58. {
  59. b.HasKey(l => new { l.LoginProvider, l.ProviderKey });
  60. if (maxKeyLength > 0)
  61. {
  62. b.Property(l => l.LoginProvider).HasMaxLength(maxKeyLength);
  63. b.Property(l => l.ProviderKey).HasMaxLength(maxKeyLength);
  64. }
  65. b.ToTable("UserLogins");
  66. });
  67. builder.Entity<UserToken>(b =>
  68. {
  69. b.HasKey(t => new { t.UserId, t.LoginProvider, t.Name });
  70. if (maxKeyLength > 0)
  71. {
  72. b.Property(t => t.LoginProvider).HasMaxLength(maxKeyLength);
  73. b.Property(t => t.Name).HasMaxLength(maxKeyLength);
  74. }
  75. b.ToTable("UserTokens");
  76. });
  77. builder.Entity<Role>(b =>
  78. {
  79. b.HasKey(r => r.Id);
  80. b.HasIndex(r => r.NormalizedName).HasDatabaseName("RoleNameIndex").IsUnique();
  81. b.ToTable("Roles");
  82. b.Property(u => u.Id).HasMaxLength(36);
  83. b.Property(r => r.ConcurrencyStamp).IsConcurrencyToken();
  84. b.Property(u => u.Name).HasMaxLength(256);
  85. b.Property(u => u.NormalizedName).HasMaxLength(256);
  86. b.HasMany(e => e.UserRoles)
  87. .WithOne(e => e.Role)
  88. .HasForeignKey(ur => ur.RoleId)
  89. .IsRequired();
  90. b.HasMany(e => e.RoleClaims)
  91. .WithOne(e => e.Role)
  92. .HasForeignKey(rc => rc.RoleId)
  93. .IsRequired();
  94. });
  95. builder.Entity<RoleClaim>(b =>
  96. {
  97. b.HasKey(rc => rc.Id);
  98. b.ToTable("RoleClaims");
  99. });
  100. builder.Entity<UserRole>(b =>
  101. {
  102. b.HasKey(r => new { r.UserId, r.RoleId });
  103. b.ToTable("UserRoles");
  104. });
  105. }
  106. }
  107. }

执行数据库迁移命令

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

  1. Add-Migration Init
  2. Update-Database

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

  1. dotnet ef migrations add Init
  2. dotnet ef database update

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

配置Identity

在Program中添加下面代码:

  1. builder.Services.AddIdentityCore<User>()
  2. .AddRoles<Role>()
  3. .AddEntityFrameworkStores<WheelDbContext>()
  4. .AddApiEndpoints();

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

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

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

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

这里需要注意的是,如果不先MapGroup,则我们的请求路径只直接从/开始的,MapGroup("api/identity")则是指定从/api/identity开始。WithTags则是指定我们Swagger生成API的Tag显示名称。
下面两图可以看到区别:
image.png
image.png
直接调用register和login方法即可完成注册登录,这里只贴上一个登录返回的截图,可以看到我们成功拿到了accessToken以及refreshToken。
image.png
使用Post带上token请求/api/identity/manage/info。成功拿到用户信息。
image.png

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

轮子仓库地址https://github.com/Wheel-Framework/Wheel
欢迎进群催更。

image.png

原文链接:https://www.cnblogs.com/fanshaoO/p/17751027.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号