设置Scalar
创建web api项目,从.net9开始移除了默认对SWagger的支持,建议使用Scalar,直接安装nuget包:Scalar.AspNetCore,
简单使用只需要:
1 2 3 4 5 6 7 8
| builder.Services.AddOpenApi(); if (app.Environment.IsDevelopment()) { app.MapOpenApi(); app.MapScalarApiReference(); }
|
如果需要JWT等的自动认证可以这样设置
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
|
builder.Services.AddOpenApi(opt => { opt.AddDocumentTransformer((document, context, cancellationToken) => { document.Components ??= new OpenApiComponents(); document.Components.SecuritySchemes ??= new Dictionary<string, IOpenApiSecurityScheme>(); document.Security ??= [];
document.Components.SecuritySchemes["Bearer"] = new OpenApiSecurityScheme { Type = SecuritySchemeType.Http, Scheme = "bearer", BearerFormat = "JWT", Description = "请输入 JWT Token,不需要手动输入 Bearer,直接粘贴 token 即可,无需空格。" };
document.Security.Add(new OpenApiSecurityRequirement { [new OpenApiSecuritySchemeReference("Bearer", document, null)] = [] });
return Task.CompletedTask; }); });
|
为了项目打开后自动打开scalar的界面,打开launchSettings.json,对里面的内容进行配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| { "$schema": "https://json.schemastore.org/launchsettings.json", "profiles": { "http": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, "launchUrl": "scalar", "applicationUrl": "http://localhost:5204", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } }
|
配置数据库
- 使用ORM进行数据库的管理,要安装nuget包
Microsoft.EntityFrameworkCoreMicrosoft.EntityFrameworkCore.ToolsPomelo.EntityFrameworkCore.MySql,这个根据需要,用什么数据库,就安装什么就可以Microsoft.AspNetCore.Identity.EntityFrameworkCore ,一般直接使用Identity框架
- 根据需要建立相关的Model类,要涉及到一对一、一对多、多对多的情况,根据需要进行配置
- 创建DataContext
1 2 3 4 5 6 7 8 9
|
public class DataContext(DbContextOptions<DataContext> options) : IdentityDbContext<ApplicationUser>(options) { public DbSet<Article> Articles { get; set; } }
|
- 注入到容器中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
var connectString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<DataContext>(opt => { opt.UseMySql(connectString, ServerVersion.AutoDetect(connectString)); });
|
- 可以打开程序包管理控制台,执行
add-migration 等迁移命令
配置授权与鉴权
- 注册 ASP.NET Core Identity 的核心服务
1 2 3 4 5 6
|
builder.Services.AddIdentityCore<ApplicationUser>() .AddRoles<IdentityRole>() .AddEntityFrameworkStores<DataContext>();
|
- 注测认证,如果用JWT,需要安装nuget包
Microsoft.AspNetCore.Authentication.JwtBearer
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
|
var jwtKey = builder.Configuration["Jwt:Key"]!; var jwtIssuer = builder.Configuration["Jwt:Issuer"]!; var jwtAudience = builder.Configuration["Jwt:Audience"]!;
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(opt => { opt.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = jwtIssuer,
ValidateAudience = true, ValidAudience = jwtAudience,
ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)),
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(2),
NameClaimType = ClaimTypes.Name,
RoleClaimType = ClaimTypes.Role }; });
|
- 注册授权,有时候需要对不同角色等进行权限控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
builder.Services.AddAuthorization(config => { config.AddPolicy(AppRoles.Admin, p => { p.RequireRole(AppRoles.Admin); }); config.AddPolicy(AppRoles.User, p => { p.RequireRole(AppRoles.User, AppRoles.Admin); }); });
|
- 把认证和授权加入到管道,注意顺序
1 2 3 4 5 6 7
|
app.UseAuthentication();
app.UseAuthorization();
|
注册和登录
配置好上文后,下面实现注册和登录
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
|
var group = router.MapGroup("/api/auth");
group.MapPost("/register", Register) .WithDisplayName(nameof(Register)) .WithSummary("注册") .WithTags("注册Tag");
static async Task<Results<Ok, BadRequest<IEnumerable<IdentityError>>>> Register(Model.RegisterRequest register, UserManager<ApplicationUser> userManager,RoleManager<IdentityRole> roleManager) { var user = new ApplicationUser { UserName = register.UserName, };
var result = await userManager.CreateAsync(user, register.Password); if (!result.Succeeded) { return TypedResults.BadRequest(result.Errors); }
if (!await roleManager.RoleExistsAsync(register.Role)) { await roleManager.CreateAsync(new IdentityRole(register.Role)); }
await userManager.AddToRoleAsync(user,register.Role);
return TypedResults.Ok();
}
|
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
|
group.MapPost("/login", Login) .WithDisplayName(nameof(Login)) .WithTags("登录");
static async Task<Results<Ok<string>,UnauthorizedHttpResult>> Login(Model.LoginRequest request,UserManager<ApplicationUser> userManager,IConfiguration configuration) { var user =await userManager.FindByNameAsync(request.UserName); if (user is null) { return TypedResults.Unauthorized(); }
var pwdValid = await userManager.CheckPasswordAsync(user, request.Password); if (!pwdValid) { return TypedResults.Unauthorized(); }
var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier,user.Id), };
var roles = await userManager.GetRolesAsync(user); foreach (var role in roles) { claims.Add(new Claim(ClaimTypes.Role, role)); }
var jwtKey = configuration["Jwt:Key"]!; var jwtIssuer = configuration["Jwt:Issuer"]!; var jwtAudience = configuration["Jwt:Audience"]!;
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey));
var credentials =new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var jwtToken = new JwtSecurityToken( issuer:jwtIssuer, audience:jwtAudience, claims:claims, expires:DateTime.UtcNow.AddHours(2), signingCredentials: credentials );
string token = new JwtSecurityTokenHandler().WriteToken(jwtToken); return TypedResults.Ok(token); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
group.MapGet("adminonly", () => { return TypedResults.Ok("我是管理员"); }) .WithTags("我是管理员") .RequireAuthorization(AppRoles.Admin);
group.MapGet("useronly", () => { return TypedResults.Ok("我是注册用户"); }) .WithTags("我是注册用户") .RequireAuthorization(AppRoles.User);
|