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
相关产品推荐
相关产品推荐

