You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 13:14:53