如何编写异步策略处理程序,注入作用域服务
How to write an asynchronous Policy handler, injecting a scoped service
我正在尝试使用自定义身份存储提供程序为 ASP.NET Core 3.1 Web 应用程序编写自定义策略。
当我在 MSDN Article:
once you hold a reference to the user, you can always find the username from the claims and run a query against any database or external service
我开始编写自己的策略(截至目前是一个简单的角色要求),将 UserManager
注入构造函数:
public class RoleHandler : AuthorizationHandler<RoleRequirement>
{
private UserManager<AppUser> UserManager;
public RoleHandler(UserManager<AppUser> usermanager)
{
UserManager = usermanager;
}
}
现在我有几个问题:
在单例中注入作用域服务
策略应该在整个应用程序生命周期内持续存在,因此这将是一个单例:
services.AddSingleton<IAuthorizationHandler, RoleHandler>();
但是在策略服务器中注入的 UserManager 是一个范围内的服务,这是不允许的。解决方案非常简单,将策略服务的配置从单例更改为范围服务
services.AddScoped<IAuthorizationHandler, RoleHandler>();
但我不知道这是否会导致任何问题。
编写异步策略处理程序
这是我对 HandleRequirementAsync
方法的实现:
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleRequirement requirement)
{
AppUser user = UserManager.FindByIdAsync(context.User.Identity.Name).Result;
if (user != null)
{
bool result = UserManager.IsInRoleAsync(user, requirement.Role.ToString()).Result;
if (result) context.Succeed(requirement);
}
return Task.CompletedTask;
}
我使用了 Task.Result
但它阻塞了线程。我不能使用 await
,因为那样会使方法返回 Task<Task>
而不是 Task
,而且我无法更改它。我该如何解决这个问题?
不要 return Task.CompletedTask
.
当您将方法声明为 async
时,它 隐式 return 在第一个 await
被命中时 Task
:
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleRequirement requirement)
{
AppUser user = await UserManager.FindByIdAsync(context.User.Identity.Name);
if (user != null)
{
bool result = await UserManager.IsInRoleAsync(user, requirement.Role.ToString());
if (result) context.Succeed(requirement);
}
}
Task.CompletedTask
通常在需要同步实现 Task
returning 方法时使用,而你不是。
我的 HandleRequirementAsync 还调用 httpClient.GetAsync(Blazor 服务器,.NET 5),将异步添加到 HandleRequirementAsync 并执行 await hpptClient.GetAsync() 来破坏授权。使用带有延迟的异步方法,尝试在浏览器中输入路由地址,它会重定向到未授权的页面,即使 context.Succeed(requirement) 已执行。
对我来说可行的解决方案是保持 HandleRequirementAsync 不变,返回 Task.CompletedTask。对于我们需要调用的异步方法,只需要使用从非异步方法调用异步方法的模式即可。
我用的是
我的示例异步方法:
public async Task<IList<Permission>> GetGroupPermissions(int userId)
{
HttpResponseMessage response = await _httpClient.GetAsync(string.Format("Auth/GroupPermissions/{0}", userId));
try
{
var payload = await response.Content.ReadFromJsonAsync<List<Permission>>();
response.EnsureSuccessStatusCode();
return payload;
}
catch
{
return new List<Permission>();
}
}
HandleRequirementAsync:
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
var t2 = (Task.Run(() => GetGroupPermissions(userId)));
t2.Wait();
var userGroupPermissions = t2.Result;
if (!userGroupPermissions.Contains(requirement.Permission))
{
//context.Fail(); //no need to fail, other requirement might success
return Task.CompletedTask;
}
context.Succeed(requirement);
return Task.CompletedTask;
}
我正在尝试使用自定义身份存储提供程序为 ASP.NET Core 3.1 Web 应用程序编写自定义策略。
当我在 MSDN Article:
once you hold a reference to the user, you can always find the username from the claims and run a query against any database or external service
我开始编写自己的策略(截至目前是一个简单的角色要求),将 UserManager
注入构造函数:
public class RoleHandler : AuthorizationHandler<RoleRequirement>
{
private UserManager<AppUser> UserManager;
public RoleHandler(UserManager<AppUser> usermanager)
{
UserManager = usermanager;
}
}
现在我有几个问题:
在单例中注入作用域服务
策略应该在整个应用程序生命周期内持续存在,因此这将是一个单例:
services.AddSingleton<IAuthorizationHandler, RoleHandler>();
但是在策略服务器中注入的 UserManager 是一个范围内的服务,这是不允许的。解决方案非常简单,将策略服务的配置从单例更改为范围服务
services.AddScoped<IAuthorizationHandler, RoleHandler>();
但我不知道这是否会导致任何问题。
编写异步策略处理程序
这是我对 HandleRequirementAsync
方法的实现:
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleRequirement requirement)
{
AppUser user = UserManager.FindByIdAsync(context.User.Identity.Name).Result;
if (user != null)
{
bool result = UserManager.IsInRoleAsync(user, requirement.Role.ToString()).Result;
if (result) context.Succeed(requirement);
}
return Task.CompletedTask;
}
我使用了 Task.Result
但它阻塞了线程。我不能使用 await
,因为那样会使方法返回 Task<Task>
而不是 Task
,而且我无法更改它。我该如何解决这个问题?
不要 return Task.CompletedTask
.
当您将方法声明为 async
时,它 隐式 return 在第一个 await
被命中时 Task
:
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleRequirement requirement)
{
AppUser user = await UserManager.FindByIdAsync(context.User.Identity.Name);
if (user != null)
{
bool result = await UserManager.IsInRoleAsync(user, requirement.Role.ToString());
if (result) context.Succeed(requirement);
}
}
Task.CompletedTask
通常在需要同步实现 Task
returning 方法时使用,而你不是。
我的 HandleRequirementAsync 还调用 httpClient.GetAsync(Blazor 服务器,.NET 5),将异步添加到 HandleRequirementAsync 并执行 await hpptClient.GetAsync() 来破坏授权。使用带有延迟的异步方法,尝试在浏览器中输入路由地址,它会重定向到未授权的页面,即使 context.Succeed(requirement) 已执行。
对我来说可行的解决方案是保持 HandleRequirementAsync 不变,返回 Task.CompletedTask。对于我们需要调用的异步方法,只需要使用从非异步方法调用异步方法的模式即可。
我用的是
我的示例异步方法:
public async Task<IList<Permission>> GetGroupPermissions(int userId)
{
HttpResponseMessage response = await _httpClient.GetAsync(string.Format("Auth/GroupPermissions/{0}", userId));
try
{
var payload = await response.Content.ReadFromJsonAsync<List<Permission>>();
response.EnsureSuccessStatusCode();
return payload;
}
catch
{
return new List<Permission>();
}
}
HandleRequirementAsync:
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
var t2 = (Task.Run(() => GetGroupPermissions(userId)));
t2.Wait();
var userGroupPermissions = t2.Result;
if (!userGroupPermissions.Contains(requirement.Permission))
{
//context.Fail(); //no need to fail, other requirement might success
return Task.CompletedTask;
}
context.Succeed(requirement);
return Task.CompletedTask;
}