如何在多层架构Blazor应用中将授权能力扩展至数据访问类库
实现方案
你可以通过以下两种方式将授权校验下沉到数据访问类库,和Blazor页面的[Authorize]特性使用逻辑保持一致:
前置准备
1. 类库安装依赖
给你的.NET Standard 2.0类库安装以下NuGet包:
Microsoft.AspNetCore.Authorization:提供[Authorize]特性定义Microsoft.AspNetCore.Http.Abstractions:提供身份信息访问的基础抽象- (如果用AOP方案)额外安装
AspectCore.Extensions.DependencyInjection
2. Blazor端补充注册
修改Blazor应用的Startup.cs,补充注册身份相关服务:
public void ConfigureServices(IServiceCollection services) { // 原有其他服务注册 services.AddHttpContextAccessor(); // Blazor服务端默认已注册AuthenticationStateProvider,无需额外添加 // 原有类库服务注册 services.AddTransient<ISqlDataAccess, SqlDataAccess>(); services.AddTransient<IPeopleData, PeopleData>(); }
如果采用AOP方案,需要修改Program.cs启用动态代理容器:
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseServiceProviderFactory(new DynamicProxyServiceProviderFactory()) // 新增这行 .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
方案1:手动权限校验(轻量无第三方依赖)
适合类库方法数量少的场景,不需要额外框架,直接在方法内嵌入校验逻辑:
改造数据访问类
以PeopleData.cs为例:
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; public class PeopleData : IPeopleData { private readonly ISqlDataAccess _db; private readonly IHttpContextAccessor _httpContextAccessor; // Blazor服务端可替换为 AuthenticationStateProvider 获取用户更稳定 // private readonly AuthenticationStateProvider _authStateProvider; public PeopleData(ISqlDataAccess db, IHttpContextAccessor httpContextAccessor) { _db = db; _httpContextAccessor = httpContextAccessor; } public async Task<List<PersonModel>> GetAllPeople() { // 权限校验逻辑 var user = _httpContextAccessor.HttpContext?.User; // Blazor服务端用以下代码取用户 // var authState = await _authStateProvider.GetAuthenticationStateAsync(); // var user = authState.User; if (user == null || !user.Identity.IsAuthenticated) { throw new UnauthorizedAccessException("用户未登录"); } if (!user.IsInRole("admin")) { throw new UnauthorizedAccessException("当前用户无管理员权限"); } string sql = "select * from dbo.People"; return await _db.LoadData<PersonModel, dynamic>(sql, new { }); } }
方案2:AOP自动拦截(和Blazor页面用法一致)
适合类库方法多的场景,自动识别类/方法上的[Authorize]特性完成校验,不需要每个方法手写校验逻辑:
1. 自定义授权拦截器
using AspectCore.DynamicProxy; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; public class AuthorizeInterceptor : AbstractInterceptor { private readonly IHttpContextAccessor _httpContextAccessor; // Blazor服务端替换为: // private readonly AuthenticationStateProvider _authStateProvider; public AuthorizeInterceptor(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public override async Task Invoke(AspectContext context, AspectDelegate next) { // 优先读取方法上的[Authorize]特性,没有则读取类上的 var authAttribute = context.ImplementationMethod.GetCustomAttribute<AuthorizeAttribute>() ?? context.ImplementationType.GetCustomAttribute<AuthorizeAttribute>(); if (authAttribute != null) { var user = _httpContextAccessor.HttpContext?.User; // Blazor服务端取用户代码: // var authState = await _authStateProvider.GetAuthenticationStateAsync(); // var user = authState.User; if (user == null || !user.Identity.IsAuthenticated) { throw new UnauthorizedAccessException("未登录用户禁止访问"); } // 校验角色 if (!string.IsNullOrEmpty(authAttribute.Roles)) { var roles = authAttribute.Roles.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); if (!roles.Any(user.IsInRole)) { throw new UnauthorizedAccessException("用户无对应角色权限"); } } // 若需要校验授权策略,可补充以下逻辑 // if (!string.IsNullOrEmpty(authAttribute.Policy)) // { // var authService = context.ServiceProvider.GetRequiredService<IAuthorizationService>(); // var authResult = await authService.AuthorizeAsync(user, authAttribute.Policy); // if (!authResult.Succeeded) throw new UnauthorizedAccessException("用户无对应策略权限"); // } } await next(context); } }
2. 注册拦截器
修改Startup.cs的服务注册部分,给需要拦截的类库服务加上拦截器:
// 注册拦截器 services.AddTransient<AuthorizeInterceptor>(); // 给类库服务配置动态代理,绑定拦截器 services.AddTransient<IPeopleData, PeopleData>().ConfigureDynamicProxy(p => p.AddInterceptor<AuthorizeInterceptor>()); // 其他类库服务也可以按同样方式绑定拦截器
3. 直接加特性使用
现在你就可以直接在类库的类或者方法上加[Authorize]特性,和Blazor页面的用法完全一致:
public class PeopleData : IPeopleData { private readonly ISqlDataAccess _db; public PeopleData(ISqlDataAccess db) { _db = db; } // 加在方法上仅对当前方法生效,加在类上对类所有方法生效 [Authorize(Roles = "admin")] public Task<List<PersonModel>> GetAllPeople() { string sql = "select * from dbo.People"; return _db.LoadData<PersonModel, dynamic>(sql, new { }); } }
内容的提问来源于stack exchange,提问作者jason
相关产品推荐
相关产品推荐

