.NET 6隔离式Azure Functions中间件获取目标方法的更佳方案
.NET 6.0 隔离式Azure Function自定义JWT验证:替代反射获取函数信息的优化方案
我有一个.NET 6.0隔离式Azure Function应用,想要实现自定义JWT令牌验证。参考旧博客实现了验证用的Authentication中间件,但其中用反射获取目标函数信息的代码已是两年前的写法,想知道现在有没有更优的实现方式。
现有代码实现
中间件类
public class AuthenticationMiddleware : IFunctionsWorkerMiddleware { private readonly JwtSecurityTokenHandler _tokenValidator; private readonly TokenValidationParameters _tokenValidationParameters; public AuthenticationMiddleware() { _tokenValidator = new JwtSecurityTokenHandler(); _tokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = Environment.GetEnvironmentVariable("JWTIssuer"), ValidAudience = Environment.GetEnvironmentVariable("JWTAudience"), IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("JWTSecretKey"))) }; } public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next) { var targetMethod = GetTargetFunctionMethod(context); var attributes = GetFunctionMethodAttribute<AuthorizeAttribute>(targetMethod); if (attributes != null && attributes.Count > 0) { //仅在函数声明了[Authorize]特性时执行此逻辑 //令牌无效时返回401-Unauthorized状态码 await next(context); } else { //允许匿名访问 await next(context); } } private static List<T> GetFunctionMethodAttribute<T>(MethodInfo targetMethod) where T : Attribute { var methodAttributes = targetMethod.GetCustomAttributes<T>(); var classAttributes = targetMethod.DeclaringType.GetCustomAttributes<T>(); return methodAttributes.Concat(classAttributes).ToList(); } private static MethodInfo GetTargetFunctionMethod(FunctionContext context) { var assemblyPath = context.FunctionDefinition.PathToAssembly; var assembly = Assembly.LoadFrom(assemblyPath); var typeName = context.FunctionDefinition.EntryPoint.Substring(0, context.FunctionDefinition.EntryPoint.LastIndexOf('.')); var type = assembly.GetType(typeName); var methodName = context.FunctionDefinition.EntryPoint.Substring(context.FunctionDefinition.EntryPoint.LastIndexOf('.') + 1); var method = type.GetMethod(methodName); return method; } }
特性类
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class AuthorizeAttribute : Attribute { public string[] UserRoles { get; set; } = Array.Empty<string>(); }
HttpTrigger函数
[Function("AllowAnonymous")] public static HttpResponseData AllowAnonymous([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req, FunctionContext context) { var response = req.CreateResponse(HttpStatusCode.OK); response.WriteString("AllowAnonymous succeeded"); return response; } [Authorize] [Function("AllowAuthenticatedOnly")] public static HttpResponseData AllowAuthenticatedOnly([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req, FunctionContext context) { var response = req.CreateResponse(HttpStatusCode.OK); response.WriteString("AllowAuthenticatedOnly succeeded"); return response; }
Program类
public class Program { public static void Main() { var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults(builder => { builder.UseMiddleware<AuthenticationMiddleware>(); }) .Build(); host.Run(); } }
现有代码中,GetTargetFunctionMethod每次请求都会通过反射加载程序集、获取类型和方法,性能开销较大且存在重复操作。
优化方案:预缓存函数授权属性信息
1. 创建授权属性缓存服务
在应用启动时一次性扫描所有Function的AuthorizeAttribute信息,缓存到字典中,避免每次请求都执行反射操作:
public interface IFunctionAuthorizationCache { bool HasAuthorizeAttribute(string functionId); IEnumerable<AuthorizeAttribute> GetAuthorizeAttributes(string functionId); } public class FunctionAuthorizationCache : IFunctionAuthorizationCache { private readonly Dictionary<string, List<AuthorizeAttribute>> _attributeCache = new(); public FunctionAuthorizationCache(IEnumerable<FunctionDefinition> functionDefinitions) { foreach (var funcDef in functionDefinitions) { // 解析EntryPoint获取类型和方法名 var entryPointParts = funcDef.EntryPoint.Split('.'); var typeName = string.Join('.', entryPointParts.Take(entryPointParts.Length - 1)); var methodName = entryPointParts.Last(); // 加载程序集并获取方法属性 var assembly = Assembly.LoadFrom(funcDef.PathToAssembly); var targetType = assembly.GetType(typeName); var targetMethod = targetType.GetMethod(methodName); // 收集方法和类上的AuthorizeAttribute var attributes = targetMethod.GetCustomAttributes<AuthorizeAttribute>().ToList(); attributes.AddRange(targetType.GetCustomAttributes<AuthorizeAttribute>()); if (attributes.Any()) { _attributeCache[funcDef.Id] = attributes; } } } public bool HasAuthorizeAttribute(string functionId) { return _attributeCache.ContainsKey(functionId); } public IEnumerable<AuthorizeAttribute> GetAuthorizeAttributes(string functionId) { return _attributeCache.TryGetValue(functionId, out var attrs) ? attrs : Enumerable.Empty<AuthorizeAttribute>(); } }
2. 注册缓存服务到依赖注入容器
修改Program.cs,将缓存服务注册为单例:
public class Program { public static void Main() { var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults(builder => { builder.UseMiddleware<AuthenticationMiddleware>(); }) .ConfigureServices(services => { // 注册授权属性缓存服务 services.AddSingleton<IFunctionAuthorizationCache>(sp => { var functionDefinitions = sp.GetRequiredService<IEnumerable<FunctionDefinition>>(); return new FunctionAuthorizationCache(functionDefinitions); }); }) .Build(); host.Run(); } }
3. 修改中间件使用缓存
注入缓存服务,移除反射逻辑,直接从缓存中获取授权属性:
public class AuthenticationMiddleware : IFunctionsWorkerMiddleware { private readonly JwtSecurityTokenHandler _tokenValidator; private readonly TokenValidationParameters _tokenValidationParameters; private readonly IFunctionAuthorizationCache _authCache; // 通过构造函数注入缓存服务 public AuthenticationMiddleware(IFunctionAuthorizationCache authCache) { _authCache = authCache; _tokenValidator = new JwtSecurityTokenHandler(); _tokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = Environment.GetEnvironmentVariable("JWTIssuer"), ValidAudience = Environment.GetEnvironmentVariable("JWTAudience"), IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("JWTSecretKey"))) }; } public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next) { var functionId = context.FunctionDefinition.Id; var authorizeAttributes = _authCache.GetAuthorizeAttributes(functionId); if (authorizeAttributes.Any()) { // 执行JWT验证逻辑 if (!await ValidateTokenAsync(context)) { var response = context.GetHttpResponseData(); response.StatusCode = HttpStatusCode.Unauthorized; await response.WriteStringAsync("无效或缺失认证令牌"); return; } await next(context); } else { // 允许匿名访问 await next(context); } } private async Task<bool> ValidateTokenAsync(FunctionContext context) { var request = context.GetHttpRequestData(); if (!request.Headers.TryGetValues("Authorization", out var authHeaders)) { return false; } var authHeader = authHeaders.FirstOrDefault(); if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ")) { return false; } var token = authHeader.Substring("Bearer ".Length); try { _tokenValidator.ValidateToken(token, _tokenValidationParameters, out _); // 可选:将验证后的用户信息存入FunctionContext,供后续函数使用 // context.Items["AuthenticatedUser"] = validatedToken; return true; } catch { return false; } } }
额外补充:.NET 7+ 更集成化的方案
如果后续升级到.NET 7或更高版本的隔离式Function,可以使用Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore包,让应用支持ASP.NET Core的认证体系,直接复用原生的[Authorize]特性和JWT认证中间件,无需自行实现自定义中间件。
内容的提问来源于stack exchange,提问作者aksvinu
相关产品推荐
相关产品推荐

