我正在尝试添加授权中间件,该中间件将用户设置在依赖注入接口中。我已经测试过了,并且是成功的。
然后,我想添加一个授权处理程序,用于检查用户角色与预期角色,例如,将api的certian操作限制为超级用户。
我已经在这里创建了授权处理程序,正如你所看到的,我依赖注入IUserProvider
,经过身份验证的用户是在这个方法的中间件中设置的。
public class RoleHandler : AuthorizationHandler<RoleRequirement>, IAuthorizationHandler
{
private readonly IUserProvider _userProvider;
public RoleHandler(IUserProvider userProvider)
{
_userProvider = userProvider;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleRequirement requirement)
{
var currentUser = _userProvider.GetAuthenticatedUser();
if (requirement.Roles.Contains(currentUser.RoleId))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
身份验证中间件:
public async Task InvokeAsync(HttpContext context, IUserRepository userRepository, IUserProvider userProvider, ApplicationContext applicationContext)
{
var email = context.User.Claims.FirstOrDefault(claim => claim.Type == "preferred_username");
if (email == null)
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return;
}
var user = await userRepository.FindByEmailAsync(email.Value);
if (user == null)
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return;
}
userProvider.SetAuthenticatedUser(user);
await _next.Invoke(context);
}
我可以看到AuthorizationHandler在AuthorizationMiddleware之前被调用,这就是为什么会发生这种情况。
然而,我已经尝试检查HandleRequirementAsync
中的上下文,这里的用户也是null。
这是我的用户提供商,你可以看到非常基本的:
public class UserProvider : IUserProvider
{
private static User AuthenticatedUser { get; set; } = default!;
public User GetAuthenticatedUser()
{
return AuthenticatedUser;
}
public void SetAuthenticatedUser(User user)
{
AuthenticatedUser = user;
}
}
我能做些什么来改变执行顺序吗?
编辑:
忘记添加我正在使用的控制器:
[Authorize(Policy = "Superuser")]
[Route("{id}"]
public async Task<User> UpdateAsync([FromBody] User user, int id)
{
return await _userService.UpdateAsync(user);
}
和程序.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Identity.Web;
using System.IdentityModel.Tokens.Jwt;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDataAccessLayer();
builder.Services.AddHttpContextAccessor();
builder.Services.AddDbContext<ApplicationContext>(options =>
{
options.UseSqlServer(builder.Configuration.GetConnectionString("Database")).EnableSensitiveDataLogging().EnableDetailedErrors();
});
builder.Services.AddScoped<IAuthorizationHandler, RoleHandler>();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Superuser", policy => policy.Requirements.Add(new RoleRequirement(Role.SUPER_USER)));
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<AuthenticationMiddleware>();
app.MapControllers();
app.Run();
我不得不解决相反的问题;中间件在我的AuthorizationHandler
s之前运行。
要使授权中间件在授权属性运行后运行,请创建一个实现IAuthorizationMiddlewareResultHandler
接口的类,如下所述。
附加说明:
对于那些浪费了一下午时间,偶然发现这个答案的人来说,他们正在寻找一种方法,让自定义授权中间件在基于资源的授权后运行(在调用AuthorizeAsync
方法后(,但你做不到。您将需要使用中间件或结果过滤器。