确保从头到尾传递消息对于确保系统之间传输的敏感信息的保密性、完整性和可信度至关重要。 你信任从远程系统接收的信息的能力和意愿依赖于提供其标识的发件人。 呼叫自动化有两种方式来传达可保护的事件:一种是通过 Azure 事件网格发送共享的 IncomingCall 事件,另一种是呼叫自动化平台通过 Webhook 发送的通话过程中发生的所有其他事件。
传入呼叫事件
Azure 通信服务依赖于 Azure 事件网格订阅来传送 IncomingCall 事件。 可以咨询 Azure 事件网格团队,获取他们关于如何保护 Webhook 订阅的文档。
呼叫自动化 Webhook 事件
呼叫自动化事件 将发送到在接听呼叫或发起新出站呼叫时指定的 Webhook 回调 URI。 回叫 URI 必须是具有有效 HTTPS 证书、DNS 名称和 IP 地址的公共终结点,而且必须打开正确的防火墙端口以便通话自动化能够访问它。 如果不采取必要的步骤来保护它免受未经授权的访问,此匿名公共 Web 服务器可能会造成安全风险。
提高此安全性的一种常见方法是实现 API 密钥机制。 Web 服务器可以在运行时生成密钥,并在应答或创建调用时将其作为查询参数在回调 URI 中提供。 Web 服务器可以先验证来自通话自动化的 Webhook 回叫中的密钥,然后再允许访问。 某些客户需要更多的安全措施。 在这些情况下,外围网络设备可以独立于 Web 服务器或应用程序本身来验证入站 Webhook。 仅 API 密钥机制可能是不够的。
改进通话自动化 Webhook 回拨安全设置
在入站 HTTPS 请求的身份验证标头中,通话自动化发送的每个通话中 Webhook 回叫都使用已签名的 JSON Web 令牌 (JWT)。 可以使用标准 Open ID Connect (OIDC) JWT 验证技术来确保令牌的完整性,如下所示。 JWT 的生存期为五 (5) 分钟,发送到回叫 URI 的每个事件都创建有一个新令牌。
- 获取 Open ID 配置 URL:https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration
- 安装 Microsoft.AspNetCore.Authentication.JwtBearer NuGet 包。
- 将应用程序配置为使用 NuGet 包和 Azure 通信服务资源的配置来验证 JWT。 需要值
audience
,因为它存在于 JWT 有效负载中。 - 验证颁发者、受众和 JWT。
- 受众是你用于设置通话自动化客户端的 Azure 通信服务资源 ID。 请参阅 此处 ,了解如何获取它。
- OpenId 配置中的 JSON Web 密钥集(JWKS)终结点包含用于验证 JWT 的密钥。 当签名有效且令牌未过期(在生成后的 5 分钟内),客户端可以使用令牌进行授权。
此示例代码演示如何使用 Microsoft.IdentityModel.Protocols.OpenIdConnect
来验证 Webhook 数据载荷
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Add Azure Communication Services CallAutomation OpenID configuration
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
builder.Configuration["OpenIdConfigUrl"],
new OpenIdConnectConfigurationRetriever());
var configuration = configurationManager.GetConfigurationAsync().Result;
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Configuration = configuration;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = builder.Configuration["AllowedAudience"]
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.MapPost("/api/callback", (CloudEvent[] events) =>
{
// Your implementation on the callback event
return Results.Ok();
})
.RequireAuthorization()
.WithOpenApi();
app.UseAuthentication();
app.UseAuthorization();
app.Run();
改进通话自动化 Webhook 回拨安全设置
在入站 HTTPS 请求的身份验证标头中,通话自动化发送的每个通话中 Webhook 回叫都使用已签名的 JSON Web 令牌 (JWT)。 可以使用标准 Open ID Connect (OIDC) JWT 验证技术来确保令牌的完整性,如下所示。 JWT 的生存期为五 (5) 分钟,发送到回叫 URI 的每个事件都创建有一个新令牌。
- 获取 Open ID 配置 URL:https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration
- 以下示例使用 Spring 框架,使用 Spring initializr 和 Maven 作为项目生成工具创建。
- 在你的
pom.xml
中添加以下依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>
- 将应用程序配置为验证 JWT 和 Azure 通信服务资源的配置。 需要值
audience
,因为它存在于 JWT 有效负载中。 - 验证颁发者、受众和 JWT。
- 受众是你用于设置通话自动化客户端的 Azure 通信服务资源 ID。 请参阅 此处 ,了解如何获取它。
- OpenId 配置中的 JSON Web 密钥集(JWKS)终结点包含用于验证 JWT 的密钥。 当签名有效且令牌未过期(在生成后的 5 分钟内),客户端可以使用令牌进行授权。
此示例代码演示如何配置 OIDC 客户端以使用 JWT 验证 Webhook 有效负载
package callautomation.example.security;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import org.springframework.security.oauth2.jwt.*;
@EnableWebSecurity
public class TokenValidationConfiguration {
@Value("ACS resource ID")
private String audience;
@Value("https://acscallautomation.communication.azure.com")
private String issuer;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers("/api/callbacks").permitAll()
.anyRequest()
.and()
.oauth2ResourceServer()
.jwt()
.decoder(jwtDecoder());
return http.build();
}
class AudienceValidator implements OAuth2TokenValidator<Jwt> {
private String audience;
OAuth2Error error = new OAuth2Error("invalid_token", "The required audience is missing", null);
public AudienceValidator(String audience) {
this.audience = audience;
}
@Override
public OAuth2TokenValidatorResult validate(Jwt token) {
if (token.getAudience().contains(audience)) {
return OAuth2TokenValidatorResult.success();
} else {
return OAuth2TokenValidatorResult.failure(error);
}
}
}
JwtDecoder jwtDecoder() {
OAuth2TokenValidator<Jwt> withAudience = new AudienceValidator(audience);
OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
OAuth2TokenValidator<Jwt> validator = new DelegatingOAuth2TokenValidator<>(withAudience, withIssuer);
NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) JwtDecoders.fromOidcIssuerLocation(issuer);
jwtDecoder.setJwtValidator(validator);
return jwtDecoder;
}
}
改进通话自动化 Webhook 回拨安全设置
在入站 HTTPS 请求的身份验证标头中,通话自动化发送的每个通话中 Webhook 回叫都使用已签名的 JSON Web 令牌 (JWT)。 可以使用标准 Open ID Connect (OIDC) JWT 验证技术来确保令牌的完整性,如下所示。 JWT 的有效期为5分钟,每次发送事件到回调 URI 时都会创建一个新令牌。
- 获取 Open ID 配置 URL:https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration
- 安装以下包:
npm install express jwks-rsa jsonwebtoken
- 将应用程序配置为验证 JWT 和 Azure 通信服务资源的配置。 需要值
audience
,因为它存在于 JWT 有效负载中。 - 验证颁发者、受众和 JWT。
- 受众是您用于配置呼叫自动化客户端的 Azure 通信服务资源 ID。 请参阅 此处 ,了解如何获取它。
- OpenId 配置中的 JSON Web 密钥集(JWKS)终结点包含用于验证 JWT 的密钥。 当签名有效且令牌未过期(在生成后的 5 分钟内),客户端可以使用令牌进行授权。
此示例代码演示如何配置 OIDC 客户端以使用 JWT 验证 Webhook 有效负载
import express from "express";
import { JwksClient } from "jwks-rsa";
import { verify } from "jsonwebtoken";
const app = express();
const port = 3000;
const audience = "ACS resource ID";
const issuer = "https://acscallautomation.communication.azure.com";
app.use(express.json());
app.post("/api/callback", (req, res) => {
const token = req?.headers?.authorization?.split(" ")[1] || "";
if (!token) {
res.sendStatus(401);
return;
}
try {
verify(
token,
(header, callback) => {
const client = new JwksClient({
jwksUri: "https://acscallautomation.communication.azure.com/calling/keys",
});
client.getSigningKey(header.kid, (err, key) => {
const signingKey = key?.publicKey || key?.rsaPublicKey;
callback(err, signingKey);
});
},
{
audience,
issuer,
algorithms: ["RS256"],
});
// Your implementation on the callback event
res.sendStatus(200);
} catch (error) {
res.sendStatus(401);
}
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
改进通话自动化 Webhook 回拨安全设置
在入站 HTTPS 请求的身份验证标头中,通话自动化发送的每个通话中 Webhook 回叫都使用已签名的 JSON Web 令牌 (JWT)。 可以使用标准 Open ID Connect (OIDC) JWT 验证技术来确保令牌的完整性,如下所示。 JWT 的生命周期为五(5)分钟,并且为每个发送到回调 URI 的事件创建一个新令牌。
- 获取 OpenID 配置的 URL:https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration
- 安装以下包:
pip install flask pyjwt
- 将应用程序配置为验证 JWT 和 Azure 通信服务资源的配置。 需要值
audience
,因为它存在于 JWT 有效负载中。 - 验证颁发者、受众和 JWT。
- 受众是你用于设置通话自动化客户端的 Azure 通信服务资源 ID。 请参阅 此处 ,了解如何获取它。
- OpenId 配置中的 JSON Web 密钥集(JWKS)终结点包含用于验证 JWT 的密钥。 当签名有效且令牌未过期(在生成后的 5 分钟内),客户端可以使用令牌进行授权。
此示例代码演示如何配置 OIDC 客户端以使用 JWT 验证 Webhook 有效负载
from flask import Flask, jsonify, abort, request
import jwt
app = Flask(__name__)
@app.route("/api/callback", methods=["POST"])
def handle_callback_event():
token = request.headers.get("authorization").split()[1]
if not token:
abort(401)
try:
jwks_client = jwt.PyJWKClient(
"https://acscallautomation.communication.azure.com/calling/keys"
)
jwt.decode(
token,
jwks_client.get_signing_key_from_jwt(token).key,
algorithms=["RS256"],
issuer="https://acscallautomation.communication.azure.com",
audience="ACS resource ID",
)
# Your implementation on the callback event
return jsonify(success=True)
except jwt.InvalidTokenError:
print("Token is invalid")
abort(401)
except Exception as e:
print("uncaught exception" + e)
abort(500)
if __name__ == "__main__":
app.run()
后续步骤
- 详细了解如何使用通话自动化操控通话。