
# 自定义鉴权使用说明
#### 使用说明
图1自定义鉴权操作流程
![](https://support.huaweicloud.com/usermanual-iothub/figure/zh-cn_image_0000001802335530.png "点击放大")
#### 操作步骤
1. 配置自定义鉴权函数：用户通过调用函数服务（[FunctionGraph](https://support.huaweicloud.com/usermanual-functiongraph/functiongraph_01_0110.html)）创建自定义鉴权函数。
   
   图2函数列表-创建函数   
   ![](https://support.huaweicloud.com/usermanual-iothub/figure/zh-cn_image_0000001970149349.png "点击放大")
   图3创建函数-参数信息
   ![](https://support.huaweicloud.com/usermanual-iothub/figure/zh-cn_image_0000001970160949.png "点击放大")
   
   
2. 创建自定义鉴权：用户可以通过Console配置自定义鉴权信息，IoTDA负责自定义鉴权信息存库和进行相应的管理维护。用户最多支持配置10个自定义鉴权器，其中最多可以设置1个默认鉴权器。
   
   图4自定义鉴权-创建鉴权
   ![](https://support.huaweicloud.com/usermanual-iothub/figure/zh-cn_image_0000001942772362.png "点击放大")
   表1自定义鉴权参数信息 
   | 参数名称       | 是否必选 | 描述                                                                                                                                                                  |
   |:---|:---|:---|
   | 鉴权名称       | 是    | **参数说明：**自定义鉴权器名称。                                                                                                                                                  |
   | 鉴权函数       | 是    | **参数说明：**自定义鉴权器对应的函数名称。 **取值范围：** 从[1]中FunctionGraph已经创建的函数列表中选取。 |
   | 状态         | 是    | **参数说明：**激活停用参数，用于表示该鉴权是否为激活状态。若需使用该鉴权器，则应激活后才能生效。 **默认值：**停用状态。                                                     |
   | 签名认证       | 是    | **参数说明：**启动签名认证后，不满足签名要求的鉴权信息将被拒绝，以减少无效的函数调用。 **默认值：**开启。                                                              |
   | token值     | 否    | **参数说明：**签名校验的token值，开启签名校验时使用，用于认证设备携带的签名信息是否正确。                                                                                                                   |
   | 公钥         | 否    | **参数说明：**签名校验的公钥，开启签名校验时使用，用于认证设备携带的签名信息是否正确。                                                                                                                       |
   | 是否默认自定义鉴权器 | 是    | **参数说明：**开启后，如果设备发起鉴权时的username没有携带authorizer_name参数，则默认使用此鉴权器。 **默认值：**不开启。                                          |
   | 是否缓存       | 是    | **参数说明：**缓存开关，用于开启缓存FunctionGraph的鉴权结果，缓存时间为300分钟\~1天。 **默认值：**不开启。                                                  |
      
   
   
3. 设备发起鉴权请求：设备通过MQTT协议发起的CONNECT请求需携带username参数，该参数应包含自定义鉴权的相关可选参数。
   
   - username格式如下。参数传值时需去掉大括号"{}"，username的每个参数需采用分隔符"\|"分隔，各参数具体内容不要出现"\|"，否则可能导致鉴权失败。
     ```
     {device-identifier}|authorizer-name={authorizer-name}|authorizer-signature={token-signature}|signing-token={token-value}
     示例：
     659b70a0bd3f665a471e5ec9_auth|authorizer-name=Test_auth_1|authorizer-signature=***|signing-token=tokenValue
     ```
     表2username参数信息 
     | 参数名称                 | 是否必选 | 描述                                                                                                                                                                                                                     |
     |:---|:---|:---|
     | device-identifier    | 是    | **参数说明：**设备标识符，建议设定为设备id。                                                                                                                                                                                              |
     | authorizer-name      | 否    | **参数说明：**自定义鉴权器名称，需要同用户配置的鉴权器一致。不携带鉴权器名称时，如用户配置了默认的自定义鉴权器，则使用自定义鉴权，否则使用原有的密钥/证书鉴权方式。                                                                                                                                   |
     | authorizer-signature | 否    | **参数说明：**签名校验功能开启时，需要携带该参数。该参数由私钥和signing-token进行加密后获取。 **取值范围：** 该参数由私钥和signing-token进行加密后获取，需与[2]中创建自定义鉴权时填写的鉴权名称保持一致。 |
     | signing-token        | 否    | **参数说明：**签名校验功能开启时，需要携带该参数。 **取值范围：** 该参数为签名校验的token，[2]中创建自定义鉴权时填写的鉴权名称需与其保持一致。                                        |
        
     
   
   - authorizer-signature通过如下命令行获取：
     ```
     echo -n "123456" | openssl dgst -sha256 -sign key.pem -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 | openssl base64
     ```
     表3命令行参数解析 
     | 参数名称                       | 描述                                                                                                                                                       |
     |:---|:---|
     | echo -n {signing-token}    | **参数说明：** 使用echo命令将签名令牌（signing-token）的值输出，并使用-n参数去掉末尾的换行符。signing-token需与[2]中创建自定义鉴权时填写的**token值**保持一致。 |
     | openssl dgst -sha256 -sign | **参数说明：**使用 SHA256 算法对输入的数据进行哈希处理。                                                                                                                       |
     | {private key}              | **参数说明：**使用 RSA 算法加密的私钥，可传入.pem/.key格式的私钥文件。                                                                                                             |
     | rsa_padding_mode:pss       | **参数说明：**指定使用 PSS 填充模式。                                                                                                                                  |
     | -sigopt rsa_pss_saltlen:-1 | **参数说明：**设置盐值长度为最大值（根据私钥的大小自动确定）。                                                                                                                        |
     | openssl base64             | **参数说明：**将签名结果进行Base64编码，以便于传输和存储。                                                                                                                       |
        
     
   
   
   
   
4. IoTDA处理鉴权请求：IoTDA收到鉴权请求时，会根据username的参数信息和自定义鉴权的配置来决策是否使用自定义鉴权方式。 
   1. 判断username是否携带了自定义鉴权名称，如果有携带，则根据平台的自定义鉴权名称，去匹配鉴权器处理函数。如果没有携带，则使用用户配置的默认自定义鉴权器去匹配鉴权处理函数，若无匹配到则使用原有的密钥/证书鉴权方式。
   
   2. 检查用户是否开启了签名校验，如果开启了签名校验，则校验username携带的签名信息是否可以校验成功，校验失败则直接返回鉴权失败。
   
   3. 匹配到处理函数后，携带设备的鉴权信息（即[5]的入参event）并通过函数urn向FunctionGraph发起鉴权请求。
   
   
   
   
5. 用户实现鉴权逻辑：用户在[1]中通过FunctionGraph创建的处理函数中进行开发，函数的返回结果有要求，函数使用示例及返回的JSON格式要求如下：
   
   ```
   exports.handler = async (event, context) => {
       console.log("username=" + event.username);
       // 此处编写您的校验逻辑
       
       // 返回的JSON格式（格式固定不变）
       const authRes = {
           "result_code": 200,
           "result_desc": "successful",
           "refresh_seconds": 300,
           "device": {
               "device_id": "myDeviceId",
               "provision_enable": true,
               "provisioning_resource": {
                   "device_name": "myDeviceName",
                   "node_id": "myNodeId",
                   "product_id": "myProductId",
                   "app_id": "customization0000000000000000000",
                   "policy_ids": ["657a4e0c2ea0cb2cd831d12a", "657a4e0c2ea0cb2cd831d12b"]
               }
           }
       }
       return JSON.stringify(authRes);
   }
   ```
   其中，函数的请求参数（即event，JSON格式）如下：
   ```
   {
       "username": "myUserName",
       "password": "myPassword",
       "client_id": "myClientId",
       "certificate_info": {
           "common_name": "",
           "fingerprint": "123"
       }
   }
   ```
   表4请求参数信息 
   | 参数名称             | 参数类型       | 是否必选 | 描述                                                                                                       |
   |:---|:---|:---|:---|
   | username         | String     | 是    | **参数说明：** MQTT协议中CONNECT消息的username字段，与[3]的username格式相同。 |
   | password         | String     | 是    | **参数说明：**MQTT协议中CONNECT消息的password参数。                                                                    |
   | client_id        | String     | 是    | **参数说明：**MQTT协议中CONNECT消息的clientId参数。                                                                    |
   | certificate_info | JsonObject | 否    | **参数说明：**MQTT协议中CONNECT消息的设备证书信息。                                                                        |
      
   表5certificate_info证书信息 
   | 参数名称        | 参数类型   | 是否必选 | 描述                                        |
   |:---|:---|:---|:---|
   | common_name | String | 是    | **参数说明：**设备携带设备证书时，从设备证书中解析的commonName信息。 |
   | fingerprint | String | 是    | **参数说明：**设备携带设备证书时，从设备证书中解析的指纹信息。         |
      
   表6返回参数信息 
   | 参数名称            | 参数类型       | 是否必选 | 描述                                                                 |
   |:---|:---|:---|:---|
   | result_code     | Integer    | 是    | **参数说明：**鉴权结果码， 返回200标识鉴权成功。                                       |
   | result_desc     | String     | 否    | **参数说明：**鉴权结果描述信息。                                                 |
   | refresh_seconds | Integer    | 否    | **参数说明：**鉴权结果的缓存时间，单位(s)。                                          |
   | device          | JsonObject | 否    | **参数说明：**认证成功时的设备信息。当设备信息中的设备ID不存在时，如果用户开启自注册，则平台会根据设备信息自动创建对应的设备。 |
      
   表7device设备信息 
   | 参数名称                  | 参数类型       | 是否必选    | 描述                                                                                                                                                                                                             |
   |:---|:---|:---|:---|
   | device_id             | String     | 是       | **参数说明：**设备ID，自注册场景、非自注册场景都必选。全局唯一，用于唯一标识一个设备。如果携带该参数，平台将设备ID设置为该参数值；建议使用product_id + _ + node_id拼接而成。 **取值范围：**长度不超过128，只允许字母、数字、下划线（_）、连接符（-）的组合，建议不少于4个字符。 |
   | provision_enable      | Boolean    | 否       | **参数说明：**自注册开关。 **默认值：**false。                                                                                                                                    |
   | provisioning_resource | JsonObject | 自注册场景必选 | **参数说明：**自注册参数信息。                                                                                                                                                                                              |
      
   表8provisioning_resource自注册参数信息 
   | 参数名称        | 参数类型           | 是否必选 | 描述                                                                                                                                                                                                                                                                              |
   |:---|:---|:---|:---|
   | device_name | String         | 否    | **参数说明：**设备名称，资源空间下唯一，用于资源空间下唯一标识一个设备。 **取值范围：**长度不超过256，只允许中文、字母、数字、以及_?'#().,\&%@!-等字符的组合，建议不少于4个字符。 最小长度：**1** 最大长度：**256** |
   | node_id     | String         | 是    | **参数说明：**设备标识码，通常使用IMEI、MAC地址或Serial No作为node_id。 设备标识码长度为1到64个字符，包含英文字母、数字、连接号-和下划线_。 注意：NB设备由于模组烧录信息后无法配置，所以NB设备会校验node_id全局唯一。 **取值范围：**长度不超过64，只允许字母、数字、下划线（_）、连接符（-）的组合，建议不少于4个字符。                                        |
   | product_id  | String         | 是    | **参数说明：**设备关联的产品ID，用于唯一标识一个产品模型，创建产品后获得。取值范围：长度不超过256，只允许中文、字母、数字、以及_?'#().,\&%@!-等字符的组合，建议不少于4个字符。 **取值范围：**长度不超过256。                                                                                                         |
   | app_id      | String         | 是    | **参数说明：**资源空间ID，该参数指定创建的设备归属到哪个资源空间。 **取值范围：**长度不超过36，只允许字母、数字、下划线（_）、连接符（-）的组合。                                                                                                                                                   |
   | policy_ids  | List\<String\> | 否    | **参数说明：**Topic策略ID。                                                                                                                                                                                                                                                             |
      
   图5函数编写-部署   
   ![](https://support.huaweicloud.com/usermanual-iothub/figure/zh-cn_image_0000001970190713.png "点击放大")
   
   
6. FunctionGraph返回结果后，判断是否需要支持自注册功能，如果需要则触发设备的自动注册。自注册设备全部默认为密钥认证，密钥随机生成。IoTDA收到鉴权结果后，进行接下来的业务实现流程。
 
