Cognito Identity Pool + IoT Core 实现 Mobile 端用户对设备权限的精细化控制

阅读数:161 2019 年 9 月 20 日 09:08

Cognito Identity Pool + IoT Core 实现 Mobile 端用户对设备权限的精细化控制

场景概述

目前,越来越多的 Iot 厂商会开发自己的 APP,使得终端用户可以通过 APP 绑定自己的设备,检测自己设备的实时情况,并且对设备做即时的控制。在此场景下,Mobile 端的终端用户应该只能发布消息到自己的设备。用户和设备的关系可能是一对多或者多对多, 比如同一个 user 拥有多个设备,用户 A 只能发布消息到 /userA/deviceX 的 topic 中;多个设备可能受同时受多人控制,此时用户 B 和 C 都有权限发布消息到 /device2/temp 下。本文考虑到此两种场景并做展开,利用 Cognito 和 AWS Iot Core,实现终端对设备的精细化控制权限管理。

架构图

Cognito Identity Pool + IoT Core 实现 Mobile 端用户对设备权限的精细化控制
通过 cognito user pool,无需自己 coding,即可轻松实现用户的注册、登录、注销等基本操作。Cognito Identity Pool 可以与 cognito user pool 或是其他第三方账号 (如 google,facebook) 做对接,利用 IAM Role 实现对 AWS 资源的精细化控制。本文同时使用 cognito User Pool 和 cognito identity Pool,实现对 Iot Core 的访问管理。终端用户通过 cognito user pool 的用户池,获得登录 token,通过此登录成功的 token,拿到 cognito Identity Pool Authorized Role 的身份,使得他有权访问 Iot Core 并且只能发布消息到自己的设备控制 topic。用户的登录 ID 和设备之间的绑定关系存储在 AWS NoSQL 数据库 DynamoDB 当中,用户只能发布消息到自己的 Iot 设备。

先决条件

  1. 拥有 AWS 账号
  2. 安装 AWS Iot JavaScript SDK
  3. 安装 Browserify 将 AWS IoT JavaScript SDK(Nodejs 版本) 转化为浏览器可以直接引用的 JavaScript 包.

操作步骤

第一步:资源配置

0. 创建 dynamoDB table

创建名称为 iot 的 table,将 IdentityId 为主键,其他的保留默认值即可. 此 table 主要用来维护用户 id 和设备 id 之间的 mapping 关系。
Cognito Identity Pool + IoT Core 实现 Mobile 端用户对设备权限的精细化控制

1. 创建 cognito 用户池 user pool

输入 user pool 名称(如 cognito-user-pool-for-iot),review defaults, 并根据需求做自定义修改(如可以修改 necessary attributes,密码长度等),此 demo 均利用默认值。

2. 创建并配置应用客户端 app client

选择应用客户端,取消 generate client secret 的选项
Cognito Identity Pool + IoT Core 实现 Mobile 端用户对设备权限的精细化控制
在左侧 APP-Integration 项目下,需要我们修改的有 2 个地方,一是 APP client setting,修改 callback URL 以及 scope token 作用范围,二是自定义 domain name(需要全 region 唯一)
Cognito Identity Pool + IoT Core 实现 Mobile 端用户对设备权限的精细化控制
注意:localhost:8000 仅在测试环境中使用。实际生产环境,这里的 callback 请修改为 https 的网址,注意:不支持 http 协议。请勿写入 http://ip 等形式。

记下 userPoolID 和 app Client ID,在下一步骤中会用到。

3. 创建 cognito identity pool

命名完毕后,对于 authentication provider, 选择 Cognito. 输入上一步记下的两个 ID:user pool ID(User Pools → demo-pool → General Settings → Pool ID) 以及 app client ID(User Pools → demo-pool → App Integration → App client settings → demo-app-client→ ID), 同时我们可以勾选允许 unauth 用户访问。
Cognito Identity Pool + IoT Core 实现 Mobile 端用户对设备权限的精细化控制

4. 设置 cognito identity pool 权限:授予 Iot 和 DDB 的访问权限

在点击创建后,进入到权限设置页面。可以在这里直接设置,也可以后续在 IAM role 当中随时更新 policy。
Identity pool 会自动创建两个 role,一种为 unauthorized,即未登录仅游客身份的用户,可自行为此类用户授予一些基本的浏览权限;另外一种为 authorized,成功验证身份的用户。

在本文当中,我们主要实现的功能为登录用户可以并且只能访问到隶属于自己的设备,未登录用户仅能网页浏览并且有登录选项。 因此,对于授权用户我们赋予对用户自己的 id 下的设备有着“iot:Connect”, “iot:Publish”, “iot:Subscribe”, “iot:Receive”, “iot:GetThingShadow”的允许权限,以及访问 dynamoDB,和修改 dynamoDB 的权限。${ cognito-identity.amazonaws.com :sub}为从 cognito-user-pool 传递过来的变量,标记用户的 identityID。每个 user 不同且唯一。我们通过此值来限定不同 user 之间的访问权限。

  • 对于 authorized users. 请下载 IAM policy 并且替换以下参数:
    请将替换为自己的 12 位 ID(去掉 <> 两个尖括号)
    请将去掉尖括号替换为自己使用的区域(去掉 <> 两个尖括号),如日本为 ap-northeast-1,virginia 为 us-east-1, 如使用其他 region,请务必替换为自己的对应代码,其他 region-code 请参考此页. 本文使用日本区域 ap-northeast-1 做演示。 resource 完整示例为 arn:aws:iot:us-west-1:123456789102:client/${ cognito-identity.amazonaws.com :sub}。
  • Unauth-Role 的 policy 如下
复制代码
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"mobileanalytics:PutEvents",
"cognito-sync:*"
],
"Resource": [
"*"
]
}
]
}

5. 在 Iot 中添加 Iot policy:为 Iot 授予访问权限

在 Iot core 当中安全–策略 (policy) 页面,添加策略,命名 cognito-identity-general-policy,具体权限如下。Cognito 将通过 attachPolicy 命令为自己授予这条 policy,使得每个 Auth user 有权限访问 Iot 且仅能发消息给自己的设备。此段 policy 也可以在这里找到

复制代码
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:Connect",
"Resource": "arn:aws:iot:<your-region-code>:<account-id>:client/${cognito-identity.amazonaws.com:sub}"
},
{
"Effect": "Allow",
"Action": "iot:Publish",
"Resource": "arn:aws:iot:<your-region-code>:<account-id>:topic/${cognito-identity.amazonaws.com:sub}/*"
},
{
"Effect": "Allow",
"Action": "iot:Subscribe",
"Resource": "arn:aws:iot:<your-region-code>:<account-id>:topic/${cognito-identity.amazonaws.com:sub}/*"
}
]
}

至此,通过 cognito 连接 Iot 并只能访问自己资源已经配置完毕,可以用自己的 web 页面嵌入此功能做验证或者测试。 本文用一个 demo 前端界面演示效果。

第二步:前端 demo 代码

此网页实现三个功能,一是登录,注册,是由 cognito user pool 来实现的;

二是设备绑定,此网页模拟了用户拿到设备之后,手输设备号完成绑定的过程,在实际 APP 当中,这一步通常是由扫二维码的形式来实现绑定的,因 web 网页版不好模拟扫码,故用手输的方式;

三是消息收发。点击已有设备,即模拟一次消息传输的过程。页面还有一个 button 是验证发送到其他 topic 会出现什么情况。

完整代码请点击这里 <<<<< 下载。

  1. (可选步骤,已经附此最终文件 bundle.js)JS 引入 AWSIotDeviceSdk 浏览器版本
    (1)安装 AWSIotDeviceSdk Nodejs
复制代码
#AWSIotDeviceSdk.js
var AwsIot = require('aws-iot-device-sdk');
window.AwsIot = AwsIot; // make it global

(2)在 Shell 当中运行 browserify, 将 nodejs 转化为前端可引入的 JS

复制代码
terminal> browserify path/to/AWSIotDeviceSdk.js -o bundle.js
  1. 修改前端代码, 替换参数
    在 index.html 中修改以下内容:

(1) 在 function initCognitoSDK() 中替换所有的,,, 为自己的值,这一步是设置 cognito 的过程。

AWS.config.region =’’; // 替换为自己的 region-code

复制代码
var authData = {
ClientId:'<your-app-client-id>', // APP client id here, 如 5smxxxc7lcetqvao6ed967sq01
AppWebDomain : '<your-custom-domain>', // 去掉 "https://" 部分. 样例格式:xxx.auth.ap-northeast-1.amazoncognito.com
TokenScopesArray : ['openid','email'], // like ['openid','email','phone']...
RedirectUriSignIn : 'http://localhost:8000/', // 只供本地测试,实际生产请写 https 网址,且必须要和前面控制台中 cognito callback 设置保持一致,不然会报 redirect_mismatch 的错误
RedirectUriSignOut : 'http://localhost:8000/', // 只供本地测试,实际生产请写 https 网址,且必须要和前面控制台中 cognito callback 设置保持一致,不然会报 redirect_mismatch 的错误
IdentityProvider : '<identity-provider-id>', // 替换为自己的 identity pool id
UserPoolId : '<your-user-pool-id>', //user pool ID,example: ap-northeast-1_410bH7K8x
AdvancedSecurityDataCollectionFlag : false
};
// 在 onsuccess 回调函数当中
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: '<identity-provider-id>',//identity provider id,example: ap-northeast-1: xxxxxxxx-xxxx-xxxx-xxxxxx
Logins: login
});

user pool ID 在 User Pools → demo-pool → General Settings → Pool ID
app client ID 在 User Pools → demo-pool → App Integration → App client settings → demo-app-client→ ID 可找到

(2) 如果 iot policy 为自己命名的,则 attachPolicy(“cognito-identity-general-policy”, principal) 第一个参数替换为自己的 iot policy name。通过此操作,当前用户在拿到 token 后,可以实现与 iot core 的互通。

(3) 在 function connect(principal) 当中替换

复制代码
device = AwsIot.device({
clientId: clientID,
host: '<your-iot-endpoint>', //example: xxxxxx.iot.<your-region-code>f.amazonaws.com
protocol: 'wss',
accessKeyId: AWS.config.credentials.accessKeyId,
secretKey: AWS.config.credentials.secretAccessKey,
sessionToken: AWS.config.credentials.sessionToken
});

此 iot-endpoint 可以在 iot core-setting 当中找到:
Cognito Identity Pool + IoT Core 实现 Mobile 端用户对设备权限的精细化控制

(4) 在 function userButton(auth) 当中替换自己的,,,,实现页面跳转。

复制代码
function userButton(auth) {
var state = document.getElementById('signInButton').innerHTML;
if (state === "Sign Out") {
//************************* 需自行修改,替换为自己的域名,clientid 以及回调地址 ************************//
document.getElementById("signInButton").href="https://<your-custom-domain>.auth.<your-region-code>.amazoncognito.com/logout?response_type=code&client_id=<your-client-id>&logout_uri=<your-call-back-url>";
document.getElementById("signInButton").innerHTML = "Sign In";
auth.signOut();
showSignedOut();
} else {
//auth.getSession();
//************************* 需自行修改,替换为自己的域名,clientid 以及回调地址 ************************//
document.getElementById("signInButton").href="https://<your-custom-domain>.auth.<your-region-endpoint>.amazoncognito.com/login?response_type=code&client_id=<your-client-id>&redirect_uri=your-call-back-url>";
}
}

(5) 在 function publishMessage(env) 当中,可以选择是否设置 qos 参数。这两种参数会在后续的实验中有不同的效果,我们先不设置 qos 试试看(默认)。

  1. 搭建测试服务器
    有两种方式:

(1) localhost 方式 在 shell 当中运行

复制代码
python -m SimpleHTTPServer

在浏览器当中输入 http://0.0.0.0:8000 或者 localhost:8000 进行验证。建议打开浏览器的 developer tools 查看日志以及 MQTT 传输过程。

注意:这种场景下,在点击 sign in 之后,因浏览器安全等级不同,有些浏览器可能会显示 connecting… unable to connect websocket 的 error 提示,这是因为页面停留在原 http 页面,无法自动进行证书验证,此时需要在浏览器新 tab 当中手动输入 https://xxx(复制原本 wss://xxxx 后面的 url)进行手动的加载证书的操作。之后再刷新原 localhost:8000 即可正常加载。

(2) 在实际生产当中,请直接打开 https://your-own-domain 进行测试。 留意:如果用自己的域名,请务必保持 cognito–APPclientSetting 中以及代码里面所有的 callback 回调地址也都改为 your own domain,否则会报 redirect_dismatch 的错误

验证说明

(1)新建的 cognito user pool 是没有用户的,可以在页面验证用户注册和用户登录的过程,或者直接在 cognito user pool 当中手动创建新用户也可以。
Cognito Identity Pool + IoT Core 实现 Mobile 端用户对设备权限的精细化控制

(2)原始 dynamoDB 当中没有数据,可以通过点击 add a new device 的按钮来模拟设备绑定的过程。这时可以输入一串字符(如 iphone-15341), 点击 submit 按钮,等待几秒钟,在页面最下方即出现设备列表 iphone-15341 publish。
Cognito Identity Pool + IoT Core 实现 Mobile 端用户对设备权限的精细化控制

这时 dynamodb 写入一条新数据,代表新增一条 identityID 与 device 之间的绑定关系的记录。当用户下一次登录时,会直接展示这些 device lists。
这个表的结构如图所示:

Cognito Identity Pool + IoT Core 实现 Mobile 端用户对设备权限的精细化控制

(3)进入 Iot-test 页面,订阅#(通配符,即订阅所有 topic)。在 web 页面点击刚刚出现的 xxxx publish 的按钮,可以在 console 当中看到实时的消息推送,此时 Iot 连接并且发布消息已成功。前端页面会显示发送出去的 message 的 topic 和具体内容。
Cognito Identity Pool + IoT Core 实现 Mobile 端用户对设备权限的精细化控制

(4)web 页面的”Demo Unauthed situation“这个按钮,是模拟当前用户如果要发送不在权限范围内的情形,这个按钮会发送到名为 test 的 topic。这时候我们点击此 button,会出现两种不同的情况:

  • 如果在 publishMessage 当中,不设置 Qos(默认代码),这时候 MQTT 不会验证传输是否成功,尽管 web 页面会显示发送成功,然而在 Iot 的 console 当中,会发现这条消息实际是未送达且永远不会被送达的。
  • 如果设置{qos:1}(代码改动如下)
复制代码
function publishMessage(env) {
var topic = env.target.topic;
var msg = env.target.msg;
//device.publish(topic,msg, function (err) {
device.publish(topic,msg, { qos: 1 }, function (err) {
if (err) {
console.log("failed to publish iot message! ",topic);
console.error(err);
} else {
console.log("published to TopicName: ", topic);
openTab("messagedetails");
showMessage("Message Published", "Topic: "+topic , "Message: "+msg);
}
});
}

因权限设置问题,Iot 仍然无法收到这条消息,但是 web 页面会不断重连尝试重新发送,根据官方解释,Iot 会尝试长达一个小时的重传,此时在点击 demo unauthed situation 的按钮后,页面会出现卡顿,打开 developer tool 会发现不停的 reconnect 尝试重传。此时点击其他 publish 的 button 也没有反应。

注:实际生产中,因为不会有这样一个 unauth test 的 button,因此可以设置 qos:1。 引用文档:”AWS IoT will retry delivery of unacknowledged quality-of-service 1 (QoS 1) publish requests to a client for up to one hour. If AWS IoT does not receive a PUBACK message from the client after one hour, it will drop the publish requests.”

参考链接:
https://aws.amazon.com/cn/blogs/iot/configuring-cognito-user-pools-to-communicate-with-aws-iot-core/

作者介绍:

李天歌
AWS 解决方案架构师

许昌月
AWS 解决方案架构师

本文转载自博客 aws。

原文链接:
https://amazonaws-china.com/cn/blogs/china/cognito-identity-pool-iot-core-realize-mobile-user-control/

评论

发布