使用 Visual Studio 2022 创建 ASP.NET Core Web API 可以从 Visual Studio 2022 中选择 ASP.NET Core Web API 或 ASP.NET Core gRPC模板
安装依赖库,可以使用NuGet安装或使用DotNet CLI
1 2 3 4 dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 6.0.33 dotnet add package Microsoft.EntityFrameworkCore.Tools --version 6.0.33 dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore --version 6.0.33 dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 6.0.33
在appsettings.json
中添加配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 { "Logging" : { "LogLevel" : { "Default" : "Information" , "Microsoft.AspNetCore" : "Warning" } } , "AllowedHosts" : "*" , "Database" : { "Driver" : "SqlServer" , "Host" : "127.0.0.1" , "Port" : 6543 , "DbName" : "SAMPLE" , "User" : "postgres" , "Password" : "postgres" } , "Jwt" : { "Audience" : "" , "Issuer" : "" , "Secret" : "" } }
准备Model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 using Microsoft.AspNetCore.Identity;namespace Samples.Identity.Model public class Role : IdentityRole <int >{ } public class RoleClaim : IdentityRoleClaim <int >{ } public class User : IdentityUser <int >{ } public class UserClaim : IdentityUserClaim <int >{ } public class UserLogin : IdentityUserLogin <int >{ } public class UserRole : IdentityUserRole <int >{ } public class UserToken : IdentityUserToken <int >{ }
Fluent API重定义数据库表名,字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 using Sample.Identity.Model;using Microsoft.EntityFrameworkCore;using Microsoft.EntityFrameworkCore.Metadata.Builders;namespace Samples.Identity.Configurations ;public class RoleClaimConfiguration : IEntityTypeConfiguration <RoleClaim >{ public void Configure (EntityTypeBuilder<RoleClaim> builder ) { builder.ToTable("SYS_ROLE_CLAIM" ).HasKey(x => x.Id); builder.Property(x => x.Id).HasColumnName("ID" ).ValueGeneratedOnAdd(); builder.Property(x => x.RoleId).HasColumnName("ROLE_ID" ); builder.Property(x => x.ClaimType).HasColumnName("CLAIM_TYPE" ).HasMaxLength(50 ); builder.Property(x => x.ClaimType).HasColumnName("CLAIM_VALUE" ).HasMaxLength(50 ); } } public class RoleConfiguration : IEntityTypeConfiguration <Role >{ public void Configure (EntityTypeBuilder<Role> builder ) { builder.ToTable("SYS_ROLE" ).HasKey(x => x.Id); builder.Property(x => x.Id).HasColumnName("ID" ).ValueGeneratedOnAdd(); builder.Property(x => x.Name).HasColumnName("NAME" ).HasMaxLength(50 ); builder.Property(x => x.NormalizedName).HasColumnName("NORMALIZED_NAME" ).HasMaxLength(50 ); builder.Property(x => x.ConcurrencyStamp).HasColumnName("CONCURRENCY_STAMP" ).HasMaxLength(50 ); builder.HasData(new Role { Id = 1 , Name = "SuperAdmin" , NormalizedName = "超级管理员" }); builder.HasData(new Role { Id = 2 , Name = "Admin" , NormalizedName = "管理员" }); builder.HasData(new Role { Id = 3 , Name = "Operator" , NormalizedName = "操作员" }); } } public class UserClaimConfiguration : IEntityTypeConfiguration <UserClaim >{ public void Configure (EntityTypeBuilder<UserClaim> builder ) { builder.ToTable("SYS_USER_CLAIM" ).HasKey(x => x.Id); builder.Property(x => x.Id).HasColumnName("ID" ).ValueGeneratedOnAdd(); builder.Property(x => x.UserId).HasColumnName("USER_ID" ); builder.Property(x => x.ClaimType).HasColumnName("CLAIM_TYPE" ).HasMaxLength(50 ); builder.Property(x => x.ClaimValue).HasColumnName("CLAIM_VALUE" ).HasMaxLength(50 ); } } public class UserConfiguration : IEntityTypeConfiguration <User >{ public void Configure (EntityTypeBuilder<User> builder ) { builder.ToTable("SYS_USER" ).HasKey(x => x.Id); builder.Property(x => x.Id).HasColumnName("ID" ).ValueGeneratedOnAdd(); builder.Property(x => x.UserName).HasColumnName("USERNAME" ).HasMaxLength(20 ); builder.Property(x => x.NormalizedUserName).HasColumnName("NORMALIZED_USERNAME" ).HasMaxLength(20 ); builder.Property(x => x.Email).HasColumnName("EMAIL" ).HasMaxLength(50 ); builder.Property(x => x.NormalizedEmail).HasColumnName("NORMALIZED_EMAIL" ).HasMaxLength(50 ); builder.Property(x => x.EmailConfirmed).HasColumnName("EMAIL_CONFIRMED" ); builder.Property(x => x.PasswordHash).HasColumnName("PASSWORD_HASH" ).HasMaxLength(256 ); builder.Property(x => x.SecurityStamp).HasColumnName("SECURITY_STAMP" ).HasMaxLength(256 ); builder.Property(x => x.ConcurrencyStamp).HasColumnName("CONCURRENCY_STAMP" ).HasMaxLength(256 ); builder.Property(x => x.PhoneNumber).HasColumnName("PHONE_NUMBER" ).HasMaxLength(15 ); builder.Property(x => x.PhoneNumberConfirmed).HasColumnName("PHONE_NUMBER_CONFIRMED" ); builder.Property(x => x.TwoFactorEnabled).HasColumnName("TWO_FACTOR_ENABLED" ); builder.Property(x => x.LockoutEnd).HasColumnName("LOCKOUT_END" ); builder.Property(x => x.LockoutEnabled).HasColumnName("LOCKOUT_ENABLED" ); builder.Property(x => x.AccessFailedCount).HasColumnName("ACCESS_FAILED_COUNT" ); builder.HasData(new User { Id = 1 , UserName = "admin" , NormalizedUserName = "ADMIN" , PasswordHash = "AQAAAAEAACcQAAAAELR93lThWhjLUaJtEMPGJXUR88rGK9RjjZytUhr0Jfy3J7JaObJCZAcu5MhPl39erg==" , SecurityStamp = "LA4OVIYIUDB7CB44WR4CTS6FCY4VRWSO" , }); } } public class UserLoginConfiguration : IEntityTypeConfiguration <UserLogin >{ public void Configure (EntityTypeBuilder<UserLogin> builder ) { builder.ToTable("SYS_USER_LOGIN" ); builder.Property(x => x.LoginProvider).HasColumnName("LOGIN_PROVIDER" ).HasMaxLength(20 ); builder.Property(x => x.ProviderKey).HasColumnName("PROVIDER_KEY" ).HasMaxLength(20 ); builder.Property(x => x.ProviderDisplayName).HasColumnName("PROVIDER_DISPLAY_NAME" ).HasMaxLength(20 ); builder.Property(x => x.UserId).HasColumnName("USER_ID" ); } } public class UserRoleConfiguration : IEntityTypeConfiguration <UserRole >{ public void Configure (EntityTypeBuilder<UserRole> builder ) { builder.ToTable("SYS_USER_ROLE" ); builder.Property(x => x.UserId).HasColumnName("USER_ID" ); builder.Property(x => x.RoleId).HasColumnName("ROLE_ID" ); } } public class UserTokenConfiguration : IEntityTypeConfiguration <UserToken >{ public void Configure (EntityTypeBuilder<UserToken> builder ) { builder.ToTable("SYS_USER_TOKEN" ); builder.Property(x => x.UserId).HasColumnName("USER_ID" ); builder.Property(x => x.LoginProvider).HasColumnName("LOGIN_PROVIDER" ).HasMaxLength(20 ); builder.Property(x => x.Name).HasColumnName("NAME" ).HasMaxLength(50 ); builder.Property(x => x.Value).HasColumnName("VALUE" ).HasMaxLength(256 ); } }
新建DataContext
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 using Microsoft.AspNetCore.Identity.EntityFrameworkCore;using Microsoft.AspNetCore.Identity;using Microsoft.EntityFrameworkCore;namespace Samples.Identity ;public class DataContext : IdentityDbContext <User >{ public DataContext (DbContextOptions<DataContext> options ) : base (options ) { } protected override void OnModelCreating (ModelBuilder builder ) { base .OnModelCreating(builder); builder.ApplyConfiguration(new UserConfiguration()); builder.ApplyConfiguration(new RoleConfiguration()); builder.ApplyConfiguration(new UserClaimConfiguration()); builder.ApplyConfiguration(new UserRoleConfiguration()); builder.ApplyConfiguration(new UserLoginConfiguration()); builder.ApplyConfiguration(new RoleClaimConfiguration()); builder.ApplyConfiguration(new UserTokenConfiguration()); } }
添加ViewModel
1 2 3 4 5 6 7 8 9 10 11 12 using System.ComponentModel.DataAnnotations;namespace Sample.Identity.ViewModels ;public class LoginViewModel { [Required(ErrorMessage = "用户名不能为空" ) ] public string ? Username { get ; set ; } [Required(ErrorMessage = "密码不能为空" ) ] public string ? Password { get ; set ; } }
添加Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 using Sample.Identity.ViewModels;using Microsoft.AspNetCore.Identity;using Microsoft.AspNetCore.Mvc;using Microsoft.AspNetCore.Tokens;using System.IdentityModel.Tokens.Jwt;using System.Security.Claims;using System.Text;namespace Sample.Identity.Controllers ;[Route("api/[controller]" ) ] [ApiController ] public class AuthenticateController : ControllerBase { private readonly UserManager<User> m_userManager; private readonly RoleManager<Role> m_roleManager; private readonly IConfiguration m_configuration; private readonly JwtOption m_jwtOptions; public AuthenticateController (UserManager<User> userManager, RoleManager<Role> roleManager, IConfiguration configuration ) { m_userManager = userManager; m_roleManager = roleManager; m_configuration = configuration; m_jwtOptions = m_configuration.GetSection("" ).Get<JwtOption>(); } [HttpPost ] [Route("login" ) ] public async Task<IActionResult> Login ([FromBody] LoginViewModel model ) { var user = await m_userManager.FindByNameAsync(model.Username); if (user!=null && await m_userManager.CheckPasswordAsync(user, model.Password)) { var roles = await m_userManager.GetRolesAsync(user); var claims = new List<Claim> { new Claim(ClaimTypes.Name, user.Username); new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) } foreach (var role in roles) { claims.Add(new Claim(ClaimTypes.Role, role)); } var token = GenerateToken(claims); return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token), expiration = token.ValidTo }); } return Unauthorized(); } private JwtSecurityToken GetToken (List<Claim> authClaims ) { var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(m_jwtOptions.Secret)); var token = new JwtSecurityToken( issuer: _configuration["JWT:ValidIssuer" ], audience: _configuration["JWT:ValidAudience" ], expires: DateTime.Now.AddHours(3 ), claims: authClaims, signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256) ); return token; } }
修改Program中添加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 using Sample.Identity; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using System.Text; var builder = WebApplication.CreateBuilder(args); ConfigurationManager configuration = builder.Configuration; // Add services to the container. // For Entity Framework builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(configuration.GetConnectionString("ConnStr"))); // For Identity builder.Services.AddIdentity<User, Role>() //optinos => { //options.Password.RequireDigit = false; //options.Password.RequireLowercase = false; //options.Password.RequireUppercase = false; //options.Password.RequireNonAlphanumeric = false; //options.Password.RequiredLength = 8; //options.Password.RequiredUniqueChars = 1; //options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(10); //options.Lockout.MaxFailedAccessAttempts = 5; //options.Lockout.AllowedForNewUsers = true; //} .AddEntityFrameworkStores<DataContext>() .AddDefaultTokenProviders(); var jwtOptions = builder.Configuration.GetSection("JWT").Get<JwtOption>(); // Adding Authentication builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; }) // Adding Jwt Bearer .AddJwtBearer(options => { options.SaveToken = true; options.RequireHttpsMetadata = false; options.TokenValidationParameters = new TokenValidationParameters() { ValidateIssuer = true, ValidateAudience = true, ValidAudience = jwtOptions.Audience, ValidIssuer = jwtOptions.Issuer, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.Secret)) }; }); builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); // Authentication & Authorization app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run();
执行数据迁移
1 2 add-migration L0 update-database