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

Azure Functions声明检查:前置钩子、属性授权等实现方案问询

当然可以搞定这些需求!我帮你梳理下具体的实现方案,完全符合你要的前置钩子、属性授权和直接访问声明的要求:

1. 用自定义属性实现授权逻辑

首先,你可以创建一个自定义授权过滤器属性,把所有的认证、角色检查、Graph令牌获取逻辑封装进去,这样就能通过给函数加属性的方式实现授权。

这里要注意,因为你已经配置了Azure AD认证,Functions会自动帮你解析Bearer令牌并填充ClaimsPrincipal,所以不用手动解析JWT,直接用现成的用户主体就行。如果需要额外调用Graph,用On-Behalf-Of流获取令牌即可。

示例代码:

using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Identity.Client;
using System.Security.Claims;

public class ValidateAzureADRolesAttribute : AuthorizationFilterAttribute
{
    private readonly string[] _requiredRoles;

    // 构造函数传入需要验证的角色
    public ValidateAzureADRolesAttribute(params string[] requiredRoles)
    {
        _requiredRoles = requiredRoles;
    }

    public override async Task OnAuthorizationAsync(AuthorizationFilterContext context)
    {
        var httpContext = context.HttpContext;
        var principal = httpContext.User;

        // 第一步:检查用户是否已通过Azure AD认证
        if (!principal.Identity.IsAuthenticated)
        {
            context.Result = new Microsoft.AspNetCore.Mvc.UnauthorizedResult();
            return;
        }

        // 第二步:验证用户是否拥有要求的角色
        var hasValidRole = _requiredRoles.Any(role => principal.IsInRole(role));
        if (!hasValidRole)
        {
            context.Result = new Microsoft.AspNetCore.Mvc.ForbidResult();
            return;
        }

        // 第三步:如果需要调用Microsoft Graph获取额外数据,这里获取Graph令牌
        var userToken = httpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
        var graphToken = await GetGraphAccessToken(userToken);
        
        // 把Graph令牌存入HttpContext,方便后续函数直接使用
        httpContext.Items["GraphAccessToken"] = graphToken;

        // 验证通过,继续执行函数逻辑
    }

    private async Task<string> GetGraphAccessToken(string userBearerToken)
    {
        // 从环境变量读取Azure AD应用配置(建议不要硬编码)
        var clientId = Environment.GetEnvironmentVariable("AzureAD_ClientId");
        var clientSecret = Environment.GetEnvironmentVariable("AzureAD_ClientSecret");
        var tenantId = Environment.GetEnvironmentVariable("AzureAD_TenantId");

        var confidentialClient = ConfidentialClientApplicationBuilder
            .Create(clientId)
            .WithClientSecret(clientSecret)
            .WithAuthority($"https://login.microsoftonline.com/{tenantId}")
            .Build();

        // 使用On-Behalf-Of流获取Graph令牌
        var result = await confidentialClient.AcquireTokenOnBehalfOf(
            new[] { "https://graph.microsoft.com/.default" },
            new UserAssertion(userBearerToken))
            .ExecuteAsync();

        return result.AccessToken;
    }
}
2. 全局注册“前置钩子”(让所有HTTP函数自动生效)

如果不想给每个函数手动加属性,可以通过Startup类全局注册过滤器,这样所有HTTP触发的函数都会自动经过这个授权检查。

假设你用的是In-Process模型的Azure Functions(v2+),创建Startup类:

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Mvc.Filters;

[assembly: FunctionsStartup(typeof(YourNamespace.MyFunctionsStartup))]
namespace YourNamespace
{
    public class MyFunctionsStartup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            // 注册全局授权过滤器,比如要求所有用户必须是"AuthenticatedUser"角色
            builder.Services.AddMvc(options =>
            {
                options.Filters.Add(new ValidateAzureADRolesAttribute("AuthenticatedUser"));
            });
        }
    }
}

这样一来,你的DoIt和DoSomethingElse函数不用加任何属性,都会自动触发这个前置检查。

3. 直接访问ClaimsIdentity的内容

不管是在自定义过滤器里,还是在函数方法内部,都可以直接获取ClaimsIdentity来访问用户声明:

在函数方法里的示例:

[FunctionName("DoIt")]
// 如果用了全局过滤器,这里可以不用加属性
public static async Task<HttpResponseMessage> DoIt(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]HttpRequestMessage req, 
    TraceWriter log)
{
    // 获取ClaimsIdentity
    var identity = req.GetRequestContext().Principal.Identity as ClaimsIdentity;
    
    // 直接访问声明信息
    var userName = identity.Name;
    var userEmail = identity.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
    var userRoles = identity.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value);

    // 如果之前在过滤器里存了Graph令牌,直接从HttpContext取
    var graphToken = req.GetRequestContext().HttpContext.Items["GraphAccessToken"] as string;

    // 你的函数逻辑...
    return req.CreateResponse(System.Net.HttpStatusCode.OK);
}

注意事项

  • 如果你用的是Functions v1版本(就是你代码里用TraceWriter的版本),全局过滤器的方式可能不支持,这时候只能给每个函数手动加自定义属性。
  • 调用Graph的On-Behalf-Of流需要你的Azure AD应用有相应的权限,并且要在Azure门户里配置API权限。
  • 建议把Azure AD的配置项(客户端ID、密钥等)存在Functions的应用设置里,不要硬编码。

内容的提问来源于stack exchange,提问作者Justin Dearing

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 06:47:05