提问人:rebailey 提问时间:11/9/2023 最后编辑:rebailey 更新时间:11/9/2023 访问量:65
Azure SignalR 无服务器模式,通过 Azure 函数应用协商失败
Azure SignalR serverless mode, Negotiate fails through Azure function app
问:
我一直在尝试弄清楚是否有办法使用 Azure SignalR 无服务器模式通过不允许未经身份验证的请求的 Azure 函数应用进行协商。这里的关键问题是不允许未经身份验证的请求部分。关闭此功能可使此功能正常工作,但我目前已将此设置设置为通过我的身份提供商进行身份验证(出于安全原因)。当然,这在本地运行时是有效的,因为这不需要经过身份验证的请求。我认为这是从 Azure SignalR 服务返回的通信问题,没有正确的身份验证来与函数应用通信(以设置连接)。这也不是访问此函数应用程序的授权问题,因为我有 40 个其他函数,可以通过我的应用程序访问。
使用类似此代码链接的示例
我已经在 StackOverFlow 中搜索了有关此问题的其他问题,其他评论中的人也问过这个问题,但前几年没有关于类似问题的回复答案。有谁知道有一种方法可以让它以这种方式工作?
我尝试以两种方式将 azure 标识访问角色添加到服务中,看看这是否允许特定访问。这允许我的函数应用请求访问 SignalR 实例。但这不是初始访问的问题,因为对 SignalR 实例的访问也可以正常使用访问密钥。
我试图在 Kudu 控制台中查找日志记录,我唯一能找到的是实际通过的函数调用。由于这是函数应用上的身份验证设置阻止的 401 未经授权,因此我找不到这方面的日志,或者无法通过 kudu 控制台访问它们。(通过国际空间站程序文件以及其他文件进行搜索)。
使用了很多方法,但这是利用 Microsoft.Azure.Webjobs.extensions.SignalRService 的通用方法的一个示例。
正如一般注释中要求的代码:
public class SignalRTestHub : ServerlessHub
{
[FunctionName("negotiate")]
public SignalRConnectionInfo
Negotiate([HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequest
req)
{
return Negotiate(req.Headers["x-ms-signalr-user-id"],
GetClaims(req.Headers["Authorization"]));
}
[FunctionName(nameof(OnConnected))]
public async Task OnConnected([SignalRTrigger]InvocationContext
invocationContext, ILogger logger)
{
await Clients.All.SendAsync(NewConnectionTarget, new
NewConnection(invocationContext.ConnectionId));
logger.LogInformation($"{invocationContext.ConnectionId} has
connected");
}
[FunctionName(nameof(Broadcast))]
public async Task Broadcast([SignalRTrigger]InvocationContext
invocationContext, string message, ILogger logger)
{
await Clients.All.SendAsync(NewMessageTarget, new
NewMessage(invocationContext, message));
logger.LogInformation($"{invocationContext.ConnectionId}
broadcast {message}");
}
[FunctionName(nameof(OnDisconnected))]
public void OnDisconnected([SignalRTrigger]InvocationContext
invocationContext)
{
}
}
另一个例子:
[FunctionName("negotiate")]
public static SignalRConnectionInfo Negotiate(
[HttpTrigger(AuthorizationLevel.Anonymous, Route = "negotiate")]
HttpRequest req,
[SignalRConnectionInfo(HubName = AzureConstants.SignalRHub ,
UserId = "{headers.x-ms-client-principal-name}")]
SignalRConnectionInfo connectionInfo,
ILogger log
)
{
return connectionInfo;
}
当允许未经身份验证的请求时,这两种方法都适用于 Azure 函数,但当它设置为 not 时则不然。
!!!已解决:对于任何有此类问题的未来读者: 我有一些错误的假设,angular/signalr 包不像开箱即用的 HTTP 客户端那样工作,并且剥离了身份验证令牌。您需要针对 API 进行身份验证。仅使用来自 MSAL 的数据,可以通过传入带有标头的持有者令牌并设置 Access-Control-Allow-Headers: X-Auth-Token 来执行此操作。
给我的。。不确定这是否适用于其他人,但我会在这里放一些通用代码来解释这一点。我使用 angular MSAL 库通过访问 api 范围进行身份验证。然后,我从缓存中检查令牌,以查找我使用持有者令牌的令牌,这是我从登录到此特定 api 时收到的令牌。免责声明:不保证这是做到这一点的好方法。这只是一种通过提取从 Msal 接收的数据来使其工作的方法,假设你以某种方式设置了 Azure 环境。
export type MsalTokenClass =
{
cachedAt: number;
clientId: string;
credentialType: string;
environment: string;
expiresOn: number;
extendedExpiresOn: number;
homeAccountId: string;
realm: string;
secret : string;
target: string;
tokenType: string;
}
isMyType(o: any): o is MsalTokenClass {
return "cachedAt" in o && "clientId" in o && "credentialType"
in o && "environment" in o && "expiresOn" in o &&
"extendedExpiresOn" in o && "homeAccountId" in o && "realm" in o &&
"secret" in o && "target" in o && "tokenType" in o;
}
getAndSetApiToken()
{
let cache :any = this.authService.instance.getTokenCache();
let getStorage = cache.storage.browserStorage.windowStorage;
for(const key in getStorage)
{
let current = getStorage[key];
let parsed : any;
try
{
parsed = JSON.parse(current);
if(this.isMyType(parsed))
{
console.log("Current parsed = ", parsed);
console.log("Current parsed target = ",
parsed.target);
//this is like
this should mirror what you setup in the
msalinterceptorConfigFactory protectedResourceMap
scope
i.e.
https://youtenant.com/myapi/api.ReadWrite
if(parsed.target ===
environment.resourceIds.apiScope + api.ReadWrite
)
{
console.log("Current Bearer = ", parsed.secret);
this.currentBearerToken = parsed.secret;
}
}
else
{
console.log("not the right type");
}
}
catch{
console.log("Not Json type");
continue;
}
}
}
public startConnection = () =>
{
this.getAndSetApiToken();
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl(this.apiAddress,
{
headers:
{
'Authorization': 'Bearer '+ this.currentBearerToken,
'Access-Control-Allow-Headers':'X-Auth-Token'
},
})
.configureLogging(LogLevel.Trace)
.build();
this.hubConnection
.start()
.then(() =>
{
})
.catch(err => console.log('Error while starting connection:
' + err))
}
答: 暂无答案
评论