使用共享访问签名控制对 IoT 中心的访问

IoT 中心使用共享访问签名 (SAS) 令牌对设备和服务进行身份验证,以避免在网络上发送密钥。 可以使用 SAS 令牌向设备和服务授予限时访问 IoT 中心特定功能的权限。 若要获取授权连接到 IoT 中心,设备和服务必须发送使用共享访问或对称密钥进行签名的 SAS 令牌。 对称密钥与设备标识一起存储在标识注册表中。

本文介绍了以下内容:

  • 可以向客户端授予的访问 IoT 中心的不同权限。
  • IoT 中心用于验证权限的令牌。
  • 如何限定凭据的作用域,以限制对特定资源的访问。
  • 使用现有的设备标识注册表或身份验证方案的自定义设备身份验证机制。

注意

本文中提到的某些功能(例如云到设备消息传递、设备孪生、设备管理)仅在 IoT 中心的标准层中提供。 有关 IoT 中心基本层和标准/免费层的详细信息,请参阅选择适合你的解决方案的 IoT 中心层

IoT 中心使用 权限 向每个 IoT 中心终结点授予访问权限。 权限可根据功能限制对 IoT 中心的访问。 必须具有适当的权限,才能访问任何 IoT 中心终结点。 例如,设备必须随它发送到 IoT 中心的每条消息提供包含安全凭据的令牌。 但是,签名密钥(如设备对称密钥)永远不会通过网络发送。

身份验证和授权

身份验证是证明你自己的身份的过程。 身份验证向 IoT 中心验证用户身份或设备标识。 它有时缩写为 AuthN。 授权是确认 IoT 中心上经过身份验证的用户或设备的权限的过程。 它指定允许访问的资源和命令,以及可使用这些资源和命令执行的操作。 授权有时缩写为 AuthZ。

本文介绍使用共享访问签名进行身份验证和授权,使你可以对权限进行分组,并使用访问密钥和签名的安全令牌将权限授予应用程序。 还可使用对称密钥或共享访问密钥通过 IoT 中心对设备进行身份验证。 SAS 令牌通过将对称密钥关联到每个调用,为设备向 IoT 中心发出的每个调用提供身份验证。

访问控制和权限

对 IoT 中心级访问使用共享访问策略,并使用单个设备凭据将访问范围限制于该设备。

IoT 中心级别的共享访问策略

共享访问策略可以授予任意权限组合。 可使用 IoT 中心资源 REST API 或使用 Azure CLI az iot hub policy 命令以编程方式在 Azure 门户中定义策略。 新建的 IoT 中心有以下默认策略:

共享访问策略 权限
iothubowner 所有权限
服务 ServiceConnect 权限
设备 DeviceConnect 权限
registryRead RegistryRead 权限
registryReadWrite RegistryReadRegistryWrite 权限

可使用以下权限控制对 IoT 中心的访问:

  • 后端云服务使用 ServiceConnect 权限,并授予以下访问权限

    • 对面向云服务的通信和监视终结点的访问权限。
    • 接收设备到云消息、发送云到设备消息以及检索相应传送确认的权限。
    • 检索文件上传的传送确认的权限。
    • 访问孪生以更新标记和所需属性、检索报告属性和运行查询的权限。
  • 设备使用 ServiceConnect 权限,并授予以下访问权限

    • 对面向设备的终结点的访问权限。
    • 发送设备到云消息和接收云到设备消息的权限。
    • 执行文件上载的权限。
    • 接收设备孪生所需属性通知和更新设备孪生报告属性的权限。
  • 后端云服务使用 RegistryRead 权限,并授予以下访问权限

    • 对标识注册表的读取访问权限。 有关详细信息,请参阅标识注册表
  • 后端云服务使用 RegistryReadWrite 权限,并授予以下访问权限

    • 对标识注册表的读取和写入访问权限。 有关详细信息,请参阅标识注册表

每个设备的安全凭据

每个 IoT 中心都有一个标识注册表,存储允许连接到 IoT 中心的设备和模块的相关信息。 IoT 中心的标识注册表中必须先有设备或模块的条目,然后该设备或模块才能连接到 IoT 中心。 设备或模块基于标识注册表中存储的凭据向 IoT 中心进行身份验证。

注册设备以使用 SAS 令牌身份验证时,该设备将获取两个对称密钥。 对称密钥为关联设备标识授予 DeviceConnect 权限

使用来自服务的 SAS 令牌

服务可使用共享访问策略生成 SAS 令牌,该策略定义相应的权限,如前面的访问控制和权限部分所述。

例如,使用名为 registryRead 的预创建共享访问策略的服务会使用以下参数创建令牌

  • 资源 URI: {IoT hub name}.azure-devices.net
  • 签名密钥: registryRead 策略的密钥之一,
  • 策略名称: registryRead
  • 任何过期时间。

例如,以下代码会在 Node.js 中创建 SAS 令牌:

var endpoint = "myhub.azure-devices.net";
var policyName = 'registryRead';
var policyKey = '...';

var token = generateSasToken(endpoint, policyKey, policyName, 60);

授予读取标识注册表中所有设备标识的权限的结果为:

SharedAccessSignature sr=myhub.azure-devices.net&sig=JdyscqTpXdEJs49elIUCcohw2DlFDR3zfH5KqGJo4r4%3D&se=1456973447&skn=registryRead

有关更多示例,请参阅生成 SAS 令牌

对于服务,SAS 令牌仅在 IoT 中心级别授予权限。 也就是说,使用基于服务策略的令牌进行身份验证的服务将能够执行 ServiceConnect 权限授予的所有操作。 这些操作包括接收设备到云的消息、发送云到设备的消息等。 若要向服务授予更精细的访问权限,例如,将服务限制为仅发送云到设备的消息,可使用 Microsoft Entra ID。 若要了解详细信息,请参阅使用 Microsoft Entra ID 进行身份验证

使用来自设备的 SAS 令牌

有两种方法可以使用 SAS 令牌来获取 IoT 中心的 DeviceConnect 权限:使用标识注册表中的对称设备密钥,或者使用共享访问密钥

可从设备访问的所有功能都故意显示在前缀为 /devices/{deviceId} 的终结点上。

面向设备的终结点包括(无论任何协议):

端点 功能
{iot hub name}/devices/{deviceId}/messages/events 发送设备到云的消息。
{iot hub name}/devices/{deviceId}/messages/devicebound 接收云到设备的消息。

使用标识注册表中的对称密钥

使用设备标识的对称密钥生成令牌时,将省略令牌的 policyName (skn) 元素。

例如,创建的用于访问所有设备功能的令牌应具有以下参数:

  • 资源 URI: {IoT hub name}.azure-devices.net/devices/{device id}
  • 签名密钥: {device id} 标识的任何对称密钥,
  • 无策略名称;
  • 任何过期时间。

例如,以下代码会在 Node.js 中创建 SAS 令牌:

var endpoint ="myhub.azure-devices.net/devices/device1";
var deviceKey ="...";

var token = generateSasToken(endpoint, deviceKey, null, 60);

授权访问设备 1 的所有功能的安全令牌是:

SharedAccessSignature sr=myhub.azure-devices.net%2fdevices%2fdevice1&sig=13y8ejUk2z7PLmvtwR5RqlGBOVwiq7rQR3WZ5xZX3N4%3D&se=1456971697

有关更多示例,请参阅生成 SAS 令牌

使用共享访问策略代表设备访问

使用共享访问策略创建令牌时,将 skn 字段设置为策略名称。 此策略必须授予 DeviceConnect 权限。

使用共享访问策略访问设备功能的两个主要方案是:

由于共享访问策略可潜在授权访问任何连接设备的权限,因此创建 SAS 令牌时必须使用正确的资源 URI。 这对令牌服务尤其重要,它必须使用资源 URI 将令牌的范围限定到特定设备。 这一点与协议网关的关系不大,因为协议网关是对所有设备的通信进行调节。

例如,使用名为 device 的预创建共享访问策略的令牌服务所创建的令牌会包含以下参数:

  • 资源 URI: {IoT hub name}.azure-devices.net/devices/{device id}
  • 签名密钥: device 策略的密钥之一,
  • 策略名称: device
  • 任何过期时间。

例如,以下代码会在 Node.js 中创建 SAS 令牌:

var endpoint ="myhub.azure-devices.net/devices/device1";
var policyName = 'device';
var policyKey = '...';

var token = generateSasToken(endpoint, policyKey, policyName, 60);

授权访问设备 1 的所有功能的安全令牌是:

SharedAccessSignature sr=myhub.azure-devices.net%2fdevices%2fdevice1&sig=13y8ejUk2z7PLmvtwR5RqlGBOVwiq7rQR3WZ5xZX3N4%3D&se=1456971697&skn=device

协议网关可以对所有设备使用相同的令牌,只需将资源 URI 设置为 myhub.azure-devices.net/devices

有关更多示例,请参阅生成 SAS 令牌

创建令牌服务以集成现有设备

可使用 IoT 中心标识注册表,通过令牌配置每个设备/模块的安全凭据和访问控制。 如果 IoT 解决方案已经具有自定义标识注册表和/或身份验证方案,可考虑通过创建“令牌服务”,将此基础结构与 IoT 中心集成。 这样,便可以在解决方案中使用其他 IoT 功能。

令牌服务是自定义云服务。 它使用包含 DeviceConnect 权限的 IoT 中心共享访问策略创建设备范围的 或模块范围的令牌。 这些令牌可让设备或模块连接到 IoT 中心。

Diagram that shows the steps of the token service pattern.

下面是令牌服务模式的主要步骤:

  1. 为 IoT 中心创建包含 DeviceConnect 权限的 IoT 中心共享访问策略。 可以在 Azure 门户中或以编程方式创建此策略。 令牌服务使用此策略为它创建的令牌签名。

  2. 当设备或模块需要访问 IoT 中心时,将向令牌服务请求已签名的令牌。 设备/模块可以使用自定义标识注册表/身份验证方案来进行身份验证,以确定令牌服务用来创建令牌的设备/模块标识。

  3. 令牌服务返回令牌。 创建令牌时,使用 /devices/{deviceId}/devices/{deviceId}/modules/{moduleId} 作为 resourceURI,使用 deviceId 作为要进行身份验证的设备,以及使用 moduleId 作为要进行身份验证的模块。 令牌服务使用共享访问策略来构造令牌。

  4. 设备/模块直接通过 IoT 中心使用令牌。

注意

可以使用 .NET 类 SharedAccessSignatureBuilder 或 Java 类 IotHubServiceSasToken 在令牌服务中创建令牌。

令牌服务可以根据需要设置令牌过期日期。 令牌过期时,IoT 中心将断开设备/模块连接。 然后,设备/模块必须向令牌服务请求新令牌。 到期时间过短会增加设备/模块和令牌服务上的负载。

为了让设备/模块连接到中心,仍必须将它添加到 IoT 中心标识注册表,即使设备/模块使用令牌而不是密钥来连接。 因此,可通过在识别注册表中启用或禁用设备/模块标识,来继续使用基于设备/模块的访问控制。 此方法可以减轻使用较长到期时间令牌的风险。

与自定义网关的比较

令牌服务模式是使用 IoT 中心实现自定义标识注册表/身份验证方案的建议方式。 建议使用这种模式是因为 IoT 中心继续处理大部分解决方案流量。 但是,如果自定义身份验证方案与协议过度交织,可能需要自定义网关来处理所有流量。 使用传输层安全性 (TLS) 和预共享密钥 (PSK) 就是这种情况的例子。 有关详细信息,请参阅如何将 IoT Edge 设备用作网关

生成 SAS 令牌

Azure IoT SDK 自动生成令牌,但某些场景确实需要直接生成和使用 SAS 令牌,包括:

  • MQTT、AMQP 或 HTTPS 曲面的直接使用。

  • 令牌服务模式的实现,如创建令牌服务中所述。

使用共享访问密钥进行签名的令牌可授权访问与共享访问策略权限相关的所有功能。 使用设备标识的对称密钥进行签名的令牌只能向相关设备标识授予 DeviceConnect 权限。

本部分提供有关以不同代码语言生成 SAS 令牌的示例。 还可使用 CLI 扩展命令 az iot hub generate-sas-token用于 Visual Studio Code 的 Azure IoT 中心扩展来生成 SAS 令牌。

SAS 令牌结构

SAS 令牌采用以下格式:

SharedAccessSignature sig={signature-string}&se={expiry}&skn={policyName}&sr={URL-encoded-resourceURI}

以下是预期值:

说明
{signature} HMAC-SHA256 签名字符串的格式为: {URL-encoded-resourceURI} + "\n" + expiry重要说明:密钥是从 base64 解码得出的,用作执行 HMAC-SHA256 计算的密钥。
{resourceURI} 使用此令牌可以访问的终结点的 URI 前缀(根据分段)以 IoT 中心的主机名开头(无协议)。 授予后端服务的 SAS 令牌的范围限定为 IoT 中心级别;例如 myHub.azure-devices.net。 授予设备的 SAS 令牌必须限定为单个设备;例如,myHub.azure-devices.net/devices/device1
{expiry} 从纪元 1970 年 1 月 1日 00:00:00 UTC 时间至今秒数的 UTF8 字符串。
{URL-encoded-resourceURI} 小写资源 URI 的小写 URL 编码
{policyName} 此令牌所引用的共享访问策略名称。 如果此令牌引用设备注册表凭据,则空缺。

URI 前缀是按分段而不是按字符计算的。 例如,/a/b/a/b/c 的前缀,而不是 /a/bc 的前缀。

以下代码使用资源 URI、签名密钥、策略名称和过期期限生成 SAS 令牌。 以下各节将详细讲解如何初始化不同令牌用例的不同输入。

var generateSasToken = function(resourceUri, signingKey, policyName, expiresInMins) {
    resourceUri = encodeURIComponent(resourceUri);

    // Set expiration in seconds
    var expires = (Date.now() / 1000) + expiresInMins * 60;
    expires = Math.ceil(expires);
    var toSign = resourceUri + '\n' + expires;

    // Use crypto
    var hmac = crypto.createHmac('sha256', Buffer.from(signingKey, 'base64'));
    hmac.update(toSign);
    var base64UriEncoded = encodeURIComponent(hmac.digest('base64'));

    // Construct authorization string
    var token = "SharedAccessSignature sr=" + resourceUri + "&sig="
    + base64UriEncoded + "&se=" + expires;
    if (policyName) token += "&skn="+policyName;
    return token;
};

协议详情

每个支持的协议(如 MQTT、AMQP 和 HTTPS)以不同方式传输令牌。

使用 MQTT 时,CONNECT 包将 deviceId 用作 ClientId,“用户名”字段中为 {iothubhostname}/{deviceId};在“密码”字段中为 SAS 令牌。 {iothubhostname} 应为 IoT 中心的完整 CName(例如,myhub.azure-devices.net)。

使用 AMQP 时,IoT 中心支持 SASL PLAINAMQP 基于声明的安全性

如果使用 AMQP 基于声明的安全性,该标准指定如何传输这些令牌。

对于 SASL PLAIN, 用户名 可以是:

  • {policyName}@sas.root.{iothubName} (若使用 IoT 中心级别的令牌)。
  • {deviceId}@sas.{iothubname} (若为设备范围的令牌)。

在这两种情况下,密码字段都包含令牌,如 SAS 令牌结构中所述。

HTTPS 通过在 Authorization 请求标头中包含有效的令牌来实施身份验证。

例如,用户名(DeviceId 区分大小写):iothubname.azure-devices.net/DeviceId

密码(可使用 CLI 扩展命令 az iot hub generate-sas-token用于 Visual Studio Code 的 Azure IoT 中心扩展来生成 SAS 令牌):

SharedAccessSignature sr=iothubname.azure-devices.net%2fdevices%2fDeviceId&sig=kPszxZZZZZZZZZZZZZZZZZAhLT%2bV7o%3d&se=1487709501

注意

Azure IoT SDK 在连接到服务时自动生成令牌。 某些情况下,Azure IoT SDK 不支持部分协议或身份验证方法。

有关 SASL PLAIN 的特殊注意事项

将 SASL PLAIN 用于 AMQP 时,连接到 IoT 中心的客户端可为每个 TCP 连接使用单个令牌。 当令牌过期时,TCP 连接将与服务断开连接,并触发重新连接。 此行为虽然不会对后端应用造成问题,但对设备应用不利,原因如下:

  • 网关通常代表许多设备连接。 使用 SASL PLAIN 时,它们必须针对连接到 IoT 中心的每个设备创建不同的 TCP 连接。 此方案会大幅提高电源与网络资源的消耗并增大每个设备连接的延迟。

  • 在每个令牌过期后,增加使用要重新连接的资源通常会对资源受限的设备造成不良影响。

后续步骤

既然已了解如何控制对 IoT 中心的访问,可能有兴趣了解以下 IoT 中心开发人员指南主题: