随着.NET8发布,AOT的JWT Authentication也完成了,这样,构建一个基本的AOT API成为了可能,可以把AOT引入到一些简单的API项目中来了。关于AOT的好处,请参照:
下面是一增加JWT Authentication的一个简单Demo,时间仓促,仅供参考。
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security;
using System.Security.Claims;
using System.Text;
using System.Text.Json.Serialization;
var builder = WebApplication.CreateSlimBuilder(args);
builder.Services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
});
#region 策略
builder.Services
.AddAuthorization(options =>
{
//添加策略名称
options.AddPolicy("Permission", policyBuilder => policyBuilder.AddRequirements(new PermissionRequirement()));
})
.AddSingleton(new List<Permission> {
new Permission { RoleName = "admin", Url = "/Policy", Method = "get" },
new Permission { RoleName = "admin", Url = "/todos", Method = "get" },
})
.AddSingleton<IAuthorizationHandler, PermissionHandler>()
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}
).AddJwtBearer(opt =>
{
//token验证参数
opt.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("1234567890abcdefg1234567890abcdefg")),
ValidateIssuer = true,
ValidIssuer = "http://localhost:5274",
ValidateAudience = true,
ValidAudience = "http://localhost:5274",
ClockSkew = TimeSpan.Zero,
RequireExpirationTime = true,
};
});
#endregion
var app = builder.Build();
var sampleTodos = new Todo[] {
new(1, "Walk the dog"),
new(2, "Do the dishes", DateOnly.FromDateTime(DateTime.Now)),
new(3, "Do the laundry", DateOnly.FromDateTime(DateTime.Now.AddDays(1))),
new(4, "Clean the bathroom"),
new(5, "Clean the car", DateOnly.FromDateTime(DateTime.Now.AddDays(2)))
};
var todosApi = app.MapGroup("/todos");
todosApi.MapGet("/", () => sampleTodos).RequireAuthorization("Permission"); ;
todosApi.MapGet("/{id}", (int id) =>
sampleTodos.FirstOrDefault(a => a.Id == id) is { } todo
? Results.Ok(todo)
: Results.NotFound()).RequireAuthorization("Permission");
#region 策略
app.MapGet("/login", () =>
{
//用JWTSecurityTokenHandler生成token
return new JwtSecurityTokenHandler().WriteToken(
new JwtSecurityToken(
issuer: "http://localhost:5274",
audience: "http://localhost:5274",
claims: new Claim[] {
new Claim(ClaimTypes.Role, "admin"),
new Claim(ClaimTypes.Name, "桂素伟")
},
notBefore: DateTime.UtcNow,
expires: DateTime.UtcNow.AddSeconds(500000),
signingCredentials: new SigningCredentials(
new SymmetricSecurityKey(Encoding.ASCII.GetBytes("1234567890abcdefg1234567890abcdefg")),
SecurityAlgorithms.HmacSha256Signature)
)
);
});
app.MapGet("/policy", (ClaimsPrincipal user) => $"Hello 用户:{user.Identity?.Name}, 角色:{user.Claims?.Where(s => s.Type == ClaimTypes.Role).First().Value}. This is a policy!").RequireAuthorization("Permission");
#endregion
app.Run();
public record Todo(int Id, string? Title, DateOnly? DueBy = null, bool IsComplete = false);
[JsonSerializable(typeof(Todo[]))]
internal partial class AppJsonSerializerContext : JsonSerializerContext
{
}
#region 策略
public class PermissionRequirement : IAuthorizationRequirement
{
}
public class Permission
{
public string? RoleName { get; set; }
public string? Url { get; set; }
public string? Method { get; set; }
}
public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{
private readonly List<Permission> _userPermissions;
public PermissionHandler(List<Permission> permissions)
{
_userPermissions = permissions;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
if (context.Resource is DefaultHttpContext)
{
var httpContext = context.Resource as DefaultHttpContext;
var questPath = httpContext?.Request?.Path;
var method = httpContext?.Request?.Method;
var isAuthenticated = context?.User?.Identity?.IsAuthenticated;
if (isAuthenticated.HasValue && isAuthenticated.Value)
{
var role = context?.User?.Claims?.SingleOrDefault(s => s.Type == ClaimTypes.Role)?.Value;
if (_userPermissions.Where(w => w.RoleName == role && w.Method?.ToUpper() == method?.ToUpper() && w.Url?.ToLower() == questPath).Count() > 0)
{
context?.Succeed(requirement);
}
else
{
context?.Fail();
}
}
}
return Task.CompletedTask;
}
}
#endregion
下面是演示结果:
登录:
查看登录信息:
查数据接口:
还有很多模板不支持或不完全支持AOT,下面是当前.NET8发布的适配情况。
Feature | Fully Supported | Partially Supported | Not Supported |
---|---|---|---|
gRPC | ✅Fully supported | ||
Minimal APIs | ✅Partially supported | ||
MVC | ❌Not supported | ||
Blazor | ❌Not supported | ||
SignalR | ❌Not supported | ||
JWT Authentication | ✅Fully supported | ||
Other Authentication | ❌Not supported | ||
CORS | ✅Fully supported | ||
Health checks | ✅Fully supported | ||
Http logging | ✅Fully supported | ||
Localization | ✅Fully supported | ||
Output caching | ✅Fully supported | ||
Rate limiting | ✅Fully supported | ||
Request decompression | ✅Fully supported | ||
Response caching | ✅Fully supported | ||
Response compression | ✅Fully supported | ||
Rewrite | ✅Fully supported | ||
Session | ❌Not supported | ||
SPA | ❌Not supported | ||
Static files | ✅Fully supported | ||
WebSockets | ✅Fully supported |
声明:文中观点不代表本站立场。本文传送门:http://eyangzhen.com/374081.html