此问题是我here报道的ASP.NET Core hosted Blazor
问题的后续内容。
使用{.1}项目模板,使用.NET Core 3.0的当前预览(6),创建以下三个项目:
我想在服务器项目(以保护端点)以及客户端项目(以动态显示或隐藏视图或视图的一部分)中启用授权。
对于该用例,我在ASP.NET Core hosted Blazor
项目中创建了策略,然后可以在客户端项目中使用它们。但是,它们在服务器项目中不可用。
问题:
MyProject.Shared
定义的Microsoft.AspNetCore.Authorization.AuthorizationPolicy
。<FrameworkReference Include="Microsoft.AspNetCore.App" />
定义的Microsoft.AspNetCore.Authorization.AuthorizationPolicy
。<PackageReference Include="Microsoft.AspNetCore.Authorization" [...] />
构造函数需要一组身份验证方案。 AuthorizationPolicy
定义的Microsoft.AspNetCore.Identity.IdentityConstants
中。<FrameworkReference Include="Microsoft.AspNetCore.App" />
定义的Microsoft.AspNetCore.Identity.IdentityConstants
中。但是,该软件包将无法提供比<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
更高的版本,例如.NET Core 3.0版本中的Microsoft decided to discontinue certain packages,而是将这些类移至2.2.0
中。 我的问题:
在服务器和客户端项目中使用相同策略而不为.NET Standard和.NET Core定义两次的正确方法是什么?
<FrameworkReference Include="Microsoft.AspNetCore.App" />
与NuGet软件包Microsoft.AspNetCore.App
之间具有兼容性?Microsoft.AspNetCore.Authorization (3.0.0)
与NuGet软件包Microsoft.AspNetCore.App
之间具有兼容性? 编辑:为了在Microsoft.AspNetCore.Identity (2.2.0)
项目中正确设置共享策略,我必须引用MyProject.Shared
程序包-包括Microsoft.AspNetCore.Identity (2.2.0)
。
但是,这会导致IdentityConstants.ApplicationScheme
的构建失败:
MyProject.Client
随后显示此错误消息:
2>Processing embedded resource linker descriptor: mscorlib.xml
2>Duplicate preserve in resource mscorlib.xml in mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e of System.Threading.WasmRuntime (All). Duplicate uses (All)
2>Type Mono.ValueTuple has no fields to preserve
2>Type System.Reflection.Assembly has no fields to preserve
2>Fatal error in IL Linker
2>
2>Unhandled Exception: Mono.Linker.MarkException: Error processing method: 'System.Void System.Security.Permissions.PrincipalPermission::Demand()' in assembly: 'System.Security.Permissions.dll' ---> Mono.Cecil.ResolutionException: Failed to resolve System.Security.Principal.IPrincipal System.Threading.Thread::get_CurrentPrincipal()
2> at Mono.Linker.Steps.MarkStep.HandleUnresolvedMethod(MethodReference reference)
2> at Mono.Linker.Steps.MarkStep.MarkMethod(MethodReference reference)
2> at Mono.Linker.Steps.MarkStep.MarkInstruction(Instruction instruction)
2> at Mono.Linker.Steps.MarkStep.MarkMethodBody(MethodBody body)
2> at Mono.Linker.Steps.MarkStep.ProcessMethod(MethodDefinition method)
2> at Mono.Linker.Steps.MarkStep.ProcessQueue()
2> --- End of inner exception stack trace ---
2> at Mono.Linker.Steps.MarkStep.ProcessQueue()
2> at Mono.Linker.Steps.MarkStep.ProcessPrimaryQueue()
2> at Mono.Linker.Steps.MarkStep.Process()
2> at Mono.Linker.Steps.MarkStep.Process(LinkContext context)
2> at Mono.Linker.Pipeline.ProcessStep(LinkContext context, IStep step)
2> at Mono.Linker.Pipeline.Process(LinkContext context)
2> at Mono.Linker.Driver.Run(ILogger customLogger)
2> at Mono.Linker.Driver.Execute(String[] args, ILogger customLogger)
2> at Mono.Linker.Driver.Main(String[] args)
可以找到示例项目on GitHub。
答案 0 :(得分:1)
我建议您使用内置于Blazor框架中的身份验证和授权系统。参见https://github.com/aspnet/AspNetCore/tree/master/src/Components/Components/src/Auth
您可以使用支持基于策略的授权的组件
示例:服务器端或客户端Blazor
<AuthorizeView Policy="content-editor">
You can only see this if you satify the "content-editor" policy.
</AuthorizeView>
使用[Authorize]属性
@page "/"
@attribute [Authorize(Policy = "content-editor")]
You can only see this if you satisfy the 'content-editor' policy.
上面的这两个代码段是为了满足您对dynamically show or hide views or parts of views
的需求
当然,您需要在服务器上执行授权检查,以保护客户端应用程序访问的API端点
在文档中查找更多详细信息和示例。这是一个了不起的系统,您可以使用它轻松地对用户进行身份验证和授权。
希望这对您有帮助...
答案 1 :(得分:0)
阅读Chris Sainty's recent article about policy-based authorization后,我想到的正确方法是从类型化策略切换为使用其他重载,从而允许配置策略。
我没有为每个需要AuthenticationScheme
的策略创建一个类,而是使用AuthorizationOptions.AddPolicy(string name, AuthorizationPolicy policy)
方法,而是实现了一个扩展方法,该扩展方法调用AuthorizationOptions.AddPolicy(string name, Action<AuthorizationPolicyBuilder> configurePolicy)
。
Microsoft.AspNetCore.Authorization
的NuGet软件包3.0.0
。在共享项目中,创建一个名为Policies
的类,您可以在其中使用配置方法定义所有策略。配置方法应使用您的策略名称-例如:
public static class Policies
{
// This policy is internal and required for the extension method explained further below.
// You CANNOT use this policy in your controllers or pages.
internal static void PolicyConfigurationFailedFallback(AuthorizationPolicyBuilder builder)
=> builder.RequireAssertion(context => false);
public static void TimeMustBeEvening(AuthorizationPolicyBuilder builder)
=> builder.RequireAuthenticatedUser()
.RequireAssertion(context => DateTime.UtcNow.Hour >= 18);
}
此扩展方法自动将public
方法中的Policies
类中的所有 AuthorizationOptions
策略应用于services.AddAuthorizationCore
的{{1}}(请参阅步骤#3 ):
public static class AuthorizationOptionsExtension
{
public static void AddSharedPolicies(this AuthorizationOptions options,
ILogger logger)
{
logger.LogInformation($"{nameof(AuthorizationPolicy)} Configuration started ...");
var policies = FindPolicies();
options.TryToAddPolicies(policies, logger);
logger.LogInformation($"{nameof(AuthorizationPolicy)} Configuration completed.");
}
private static IEnumerable<PolicyInformation> FindPolicies()
{
var policyProvider = typeof(Policies);
return from method in policyProvider.GetMethods(BindingFlags.Public | BindingFlags.Static)
// The method should configure the policy builder, not return a built policy.
where method.ReturnType == typeof(void)
let methodParameter = method.GetParameters()
// The method has to accept the AuthorizationPolicyBuilder, and no other parameter.
where methodParameter.Length == 1 && methodParameter[0].ParameterType == typeof(AuthorizationPolicyBuilder)
select new PolicyInformation(method.Name, method);
}
private static void TryToAddPolicies(this AuthorizationOptions options,
IEnumerable<PolicyInformation> policies,
ILogger logger)
{
foreach (var policy in policies)
{
try
{
options.AddPolicy(policy.Name, builder => { policy.Method.Invoke(null, new object[] {builder}); });
logger.LogInformation($"Policy '{policy.Name}' was configured successfully.");
}
catch (Exception e)
{
options.AddPolicy(policy.Name, Policies.PolicyConfigurationFailedFallback);
logger.LogCritical(e, $"Failed to configure policy '{policy.Name}'. Using fallback policy instead.");
}
}
}
private class PolicyInformation
{
internal string Name { get; }
internal MethodInfo Method { get; }
internal PolicyInformation(string name,
MethodInfo method)
{
Name = name;
Method = method;
}
}
}
第1步中的PolicyConfigurationFailedFallback
策略将阻止对您受保护资源的访问,同时仍为您期望的策略名称提供策略。这样,您就不会最终将浏览器或Visual Studio控制台与“未找到策略”消息群集在一起。
当然,您可以随时取消记录。
使用扩展方法,两者 ConfigureServices
类(客户端和服务器)中的Startup
方法都非常缩小:
public void ConfigureServices(IServiceCollection services)
{
/* ... */
// For the server-side Startup class you can get the ILogger from the constructor instead.
// For the client-side Startup class you have to get the ILogger here.
var logger = services.BuildServiceProvider().GetService<ILogger<Startup>>();
services.AddAuthorizationCore(options => options.AddSharedPolicies(logger));
/* ... */
}
简单明了,无需定义包含策略名称的另一个属性,只需使用nameof()
运算符即可获取策略名称:
[Authorize(nameof(Policies.TimeMustBeEvening))]
[Route("api/[controller]")]
public class SampleDataController : Controller
{
/* ... */
}
类似于将策略应用于控制器的方式,可以在Blazor页面和组件中使用nameof()
运算符。
整个Blazor页面:
@page "/fetchdata"
@attribute [Authorize(Policy = nameof(Policies.NameMustBeBlazorAdmin))]
或者对于使用AuthorizeView
组件的单个零件:
<AuthorizeView Policy="@nameof(Policies.NameMustBeBlazorAdmin)">
<th>Temp. (F)</th>
<th>Summary</th>
</AuthorizeView>