在本地开发 Azure 函数
Developing Azure functions locally
我领导着一大群 Azure 函数开发人员。因此,Microsoft 使用 azure web 界面引用的大多数示例对我来说都不起作用。我正在使用模拟器在本地开发 Azure 函数以节省一些成本。我通过 visual studio 将所有函数发布到我的集成环境中。
我正在开发一堆 azure 函数,这些函数需要 api 网关来处理使用 Azure AD B2C 的身份验证工作流。现在,没有 api 网关模拟器或 Azure AD B2C 模拟器,我可以在本地 运行。我的身份验证工作流涉及拦截对 api 的请求,将它们重定向到 AD B2C 进行身份验证,随后将 auth-token 添加到 http header,然后调用 http-triggered azure函数。
现在,问题变成了,我如何测试身份验证工作流?
我如何设置 api 网关以在 visual studio 本地注册我的函数 运行ning 作为我在云端的 api 网关的 api 端点?
我做了什么:
- 添加了一个 "authorize" API 来处理针对外国当局的通用授权。这个 API returns 我自己的 JWT 和我自己的自定义声明,持续时间有限。
- 更改了我所有其他 API 以使用我的自定义 JWT。
优点:
- 本地测试超级容易。我只是将 #if DEBUG 部分添加到授权 API 以跳过正常授权并给我设计的 JWT。
- 我可以在声明中放任何我想要的东西,所以我将它用作缓存以减少外部授权调用。
如果您正在开发通过 Easy Auth 使用 Azure-AD 或 Azure B2C 的 SPA,这是另一种选择,它将为您执行 JWT 令牌验证并让您执行以下操作:
您的 SPA 甚至会在本地获得令牌,因此请执行以下操作:
- 将 ClaimPrincipal 注入您的函数
- 检查用户是否经过身份验证(例如,principal.Identity.IsAuthenticated),如果未经过身份验证,则 returnUnauthorizedResult。
- 检查发行人声明。如果委托人有一个,它通过了 Express Auth。你的 JWT 令牌由它验证,你可以立即从它那里得到你的声明。
- 如果没有颁发者,则为本地开发,您可以转向 header 并自行提取 JWT 令牌并获得您的声明。您也可以将其 IFDEF 用于条件构建,以便您加倍确定它是本地开发。
下面是一些从 header 中提取 JWT 令牌的示例代码(HttpRequest 被注入到每个函数中):
private JwtSecurityToken ReadJwtTokenFromHeader(HttpRequest req)
{
if (req.Headers.ContainsKey("Authorization"))
{
var authHeader = req.Headers["Authorization"];
var headerValue = AuthenticationHeaderValue.Parse(authHeader);
var handler = new JwtSecurityTokenHandler();
return handler.ReadJwtToken(headerValue.Parameter);
}
return null;
}
注意:这需要 System.IdentityModel.Tokens.Jwt NuGet 包才能使用 JwtSecurityTokenHandler。
根据@David-Yates 的回答,我在 运行 本地
时替换了 Principal
module Debug = begin
open System.IdentityModel.Tokens.Jwt
open System.Net.Http.Headers
open System.Security.Claims
let setPrincipalFromBearerToken (log : ILogger) (req : HttpRequest) =
log.LogInformation ("Reading Authorization header")
let success, authHeader = req.Headers.TryGetValue("Authorization")
if not success
then log.LogWarning ("Authorization header missing")
else
match Seq.tryExactlyOne authHeader with
| None -> log.LogWarning ("Authorization header has 0 or more than 1 value")
| Some headerValue ->
let headerValue = AuthenticationHeaderValue.Parse(headerValue);
log.LogInformation ("Authorization header succesfully parsed")
let handler = new JwtSecurityTokenHandler();
let token = handler.ReadJwtToken(headerValue.Parameter);
log.LogInformation ("JWT succesfully parsed")
let identity =
ClaimsIdentity(
req.HttpContext.User.Identity,
token.Claims)//,
//Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme),
//"oid", "roles")
let principal = ClaimsPrincipal(identity)
req.HttpContext.User <- principal
let userIdClaim =
principal.Claims
|> Seq.where (fun c -> c.Type = "oid") // TODO: Use const from MS package if possible
|> Seq.head
log.LogInformation ("Principal succesfully updated, user ID '{0}'", userIdClaim.Value)
end
let isLocal = String.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID"))
if isLocal then Debug.setPrincipalFromBearerToken log req
我领导着一大群 Azure 函数开发人员。因此,Microsoft 使用 azure web 界面引用的大多数示例对我来说都不起作用。我正在使用模拟器在本地开发 Azure 函数以节省一些成本。我通过 visual studio 将所有函数发布到我的集成环境中。
我正在开发一堆 azure 函数,这些函数需要 api 网关来处理使用 Azure AD B2C 的身份验证工作流。现在,没有 api 网关模拟器或 Azure AD B2C 模拟器,我可以在本地 运行。我的身份验证工作流涉及拦截对 api 的请求,将它们重定向到 AD B2C 进行身份验证,随后将 auth-token 添加到 http header,然后调用 http-triggered azure函数。
现在,问题变成了,我如何测试身份验证工作流? 我如何设置 api 网关以在 visual studio 本地注册我的函数 运行ning 作为我在云端的 api 网关的 api 端点?
我做了什么:
- 添加了一个 "authorize" API 来处理针对外国当局的通用授权。这个 API returns 我自己的 JWT 和我自己的自定义声明,持续时间有限。
- 更改了我所有其他 API 以使用我的自定义 JWT。
优点:
- 本地测试超级容易。我只是将 #if DEBUG 部分添加到授权 API 以跳过正常授权并给我设计的 JWT。
- 我可以在声明中放任何我想要的东西,所以我将它用作缓存以减少外部授权调用。
如果您正在开发通过 Easy Auth 使用 Azure-AD 或 Azure B2C 的 SPA,这是另一种选择,它将为您执行 JWT 令牌验证并让您执行以下操作:
您的 SPA 甚至会在本地获得令牌,因此请执行以下操作:
- 将 ClaimPrincipal 注入您的函数
- 检查用户是否经过身份验证(例如,principal.Identity.IsAuthenticated),如果未经过身份验证,则 returnUnauthorizedResult。
- 检查发行人声明。如果委托人有一个,它通过了 Express Auth。你的 JWT 令牌由它验证,你可以立即从它那里得到你的声明。
- 如果没有颁发者,则为本地开发,您可以转向 header 并自行提取 JWT 令牌并获得您的声明。您也可以将其 IFDEF 用于条件构建,以便您加倍确定它是本地开发。
下面是一些从 header 中提取 JWT 令牌的示例代码(HttpRequest 被注入到每个函数中):
private JwtSecurityToken ReadJwtTokenFromHeader(HttpRequest req)
{
if (req.Headers.ContainsKey("Authorization"))
{
var authHeader = req.Headers["Authorization"];
var headerValue = AuthenticationHeaderValue.Parse(authHeader);
var handler = new JwtSecurityTokenHandler();
return handler.ReadJwtToken(headerValue.Parameter);
}
return null;
}
注意:这需要 System.IdentityModel.Tokens.Jwt NuGet 包才能使用 JwtSecurityTokenHandler。
根据@David-Yates 的回答,我在 运行 本地
时替换了 Principalmodule Debug = begin
open System.IdentityModel.Tokens.Jwt
open System.Net.Http.Headers
open System.Security.Claims
let setPrincipalFromBearerToken (log : ILogger) (req : HttpRequest) =
log.LogInformation ("Reading Authorization header")
let success, authHeader = req.Headers.TryGetValue("Authorization")
if not success
then log.LogWarning ("Authorization header missing")
else
match Seq.tryExactlyOne authHeader with
| None -> log.LogWarning ("Authorization header has 0 or more than 1 value")
| Some headerValue ->
let headerValue = AuthenticationHeaderValue.Parse(headerValue);
log.LogInformation ("Authorization header succesfully parsed")
let handler = new JwtSecurityTokenHandler();
let token = handler.ReadJwtToken(headerValue.Parameter);
log.LogInformation ("JWT succesfully parsed")
let identity =
ClaimsIdentity(
req.HttpContext.User.Identity,
token.Claims)//,
//Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme),
//"oid", "roles")
let principal = ClaimsPrincipal(identity)
req.HttpContext.User <- principal
let userIdClaim =
principal.Claims
|> Seq.where (fun c -> c.Type = "oid") // TODO: Use const from MS package if possible
|> Seq.head
log.LogInformation ("Principal succesfully updated, user ID '{0}'", userIdClaim.Value)
end
let isLocal = String.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID"))
if isLocal then Debug.setPrincipalFromBearerToken log req