我們想象這么一個IoT應用場景:廠商A使用AWS IoT來開發物聯網解決方案,那么A把設備賣給用戶的時候,需要使用戶能夠登入AWS IoT系統來控制其購買的A的設備,也就是說給用戶分配適當的權限。下面本文簡要概括討論如何實現這樣一個場景。
部分細節可以參考這個博客:https://blog.csdn.net/luo_bo1/article/details/84567988
1. 用戶身份的管理
首先,用戶必須有一個獨特的身份在這個系統中。事實上,開發者甚至不需要自己維護一個管理身份的服務器,用戶也不願意注冊那么多賬號。為解決這個問題,便產生了直接使用第三方賬號身份來映射到AWS IoT系統中的方法,也就是說,用戶只要有一些公共的第三方身份提供商的賬號(如谷歌、亞馬遜等),便允許直接使用AWS IoT系統。本文使用了Login with Amazon這個第三方服務,廠商只要根據Login with Amazon的文檔實現一個接口即可。主要有如下幾步:
(1) 在developer.amazon.com后台注冊一個OAuth的客戶端,還要提供隱私策略,獲得一個亞馬遜發放的OAuth ClietID和Client Secret,這樣亞馬遜才能認證這是哪個開發者想獲得用戶的信息。在用戶授權后,即可向亞馬遜獲得用戶的基本身份信息。
(2)本人是用Web實現的,因此需要設置OAuth過程中需要的回調URL,並且把這個URL加入Login with Amazon的白名單。此外,還要在Web setting中設置Allow origin。在調用亞馬遜這個接口時,亞馬遜會驗證ClientId,web URL來認證開發者的身份,並且有了origin也能防止這是別人在冒用你的ClientID(用戶點擊login with amazon的時候瀏覽器會攜帶origin發給亞馬遜,盡管這個有時可以偽造);用戶授權后,結果token會以重定向的方式讓用戶瀏覽器訪問白名單中存在的回調URL,這樣就確保只有開發者的服務器可以獲得token,防止別人偷取。
(3)開發者獲得了用戶的token,就可以查詢獲得用戶在Amazon的 信息,進而獲得到一個唯一的用戶ID。這個ID應該是唯一匿名化的身份標識,即不會泄露用戶的信息,不同Client獲得的也不一樣,防止用戶被追蹤。
2. 給對應用戶分配適當的權限
現在我們獲得了用戶的身份,但是用戶要訪問的是AWS IoT中的資源,如何設置才能將AWS中的權限,關聯至第三方身份提供商給的身份呢?這就需要AWS Cognito的Identity Pool出馬了。
(1)首先,cognito需要驗證用戶的身份,然后在Identity Pool中創建一個對應的身份映射。這首先需要在cognito的Identity Pool中設置Authentication providers,添加開發者創建的login with amazon的標識,即
(2)開發者獲得用戶第三方token后,向cognito發送該token,就表明了該用戶身份,cognito會再返回給程序一系列cognito的token。由於用戶cognito就是AWS自己的服務,所以可以關聯AWS IoT中的權限給該用戶使用。具體動態關聯的方法沒有深究,只是用aws cli簡單測試實現,正確動態做法可以參考官方PPT:https://www.slideshare.net/AmazonWebServices/iot-apps-with-aws-iot-and-websockets
aws cli使用需要先設置, 可以參考https://razeencheng.com/post/tool-awscli-overview-1
指令有點坑,記錄一下:
aws iot attach-policy --policy-name <value> --target <value>
(3)最后強調一點,策略設置時要注意權限的控制,AWS提供了策略變量來獲得用戶特有的不變憑證,參考https://blog.csdn.net/luo_bo1/article/details/84567988即可。另外設置的時候有點坑,既要設置認證過cognito用戶的粗粒度權限,又要在AWS IoT中設置細粒度的權限並且關聯到cognito用戶上。
3.附錄JS代碼
注:必須自己搭建個web服務器來測試,否則由於瀏覽器安全限制(好像是專門本地的文件)無法使用亞馬遜的js API。推薦XAMPP,起名成html直接放到htdoc下就好了,xampp還直接支持https。
1 <!doctype html> 2 <html lang="en_US"> 3 4 <head> 5 <script src="https://sdk.amazonaws.com/js/aws-sdk-2.451.0.min.js"></script> 6 </head> 7 8 <body> 9 <a href id="LoginWithAmazon"> 10 <img border="0" alt="Login with Amazon" 11 src="https://images-na.ssl-images-amazon.com/images/G/01/lwa/btnLWA_gold_156x32.png" width="156" 12 height="32" /> 13 </a> 14 <button onclick="myFunction()">Myfunction</button> 15 16 <div id="amazon-root"></div> 17 <script type="text/javascript"> 18 19 window.onAmazonLoginReady = function () { 20 amazon.Login.setClientId('amzn1.application-oa2-client.XXXXX'); 21 }; 22 (function (d) { 23 var a = d.createElement('script'); a.type = 'text/javascript'; 24 a.async = true; a.id = 'amazon-login-sdk'; 25 a.src = 'https://assets.loginwithamazon.com/sdk/na/login1.js'; 26 d.getElementById('amazon-root').appendChild(a); 27 })(document); 28 </script> 29 30 <script type="text/javascript"> 31 document.getElementById('LoginWithAmazon').onclick = function () { 32 options = { scope: 'profile' }; 33 amazon.Login.authorize(options, 34 'https://127.0.0.1/test.html'); 35 return false; 36 }; 37 </script> 38 39 <script> 40 function myFunction() { 41 // Set the region where your identity pool exists (us-east-1, eu-west-1) 42 AWS.config.region = 'us-east-1'; 43 44 // Configure the credentials provider to use your identity pool 45 AWS.config.credentials = new AWS.CognitoIdentityCredentials({ 46 IdentityPoolId: 'us-east-1:d9fe3941-df89-4190-ab80-XXXXX', 47 Logins: { // optional tokens, used for authenticated login 48 'www.amazon.com': '第三方登陸獲得的token' 49 } 50 }); 51 //檢查獲得的用戶token是否有效https://api.amazon.com/auth/O2/tokeninfo?access_token=Atza%7CIwEBINapjp9vq3UgptE95f5fqL4DQ5K57Ex5tWTLh0D6K3-omYt8N2H_D-H8tDnn9fyqkrbZ0HdrnQJ4F2_-VtMVB5IOM5quEqr8qZCVVOswPKBHYnL5VwZ4YXtWJiqkssPlsvygcSD74lJWJlAVGTewHP9HWAR4HL4kADPJIyKxI2i_BWpGrDpQb6nw5Yeh6VbtTG9zTWpZUfZsENpkoMo6K5m0H-rw_NmiiZxIBsaOgtIlYTTcG5qKcRFevPXk8SQ8BZKRmXd5Hx2M1bld4rInXLfR41OvuTXH1GKXJHVKMc2hvGt6sMOCcqAXw2ABKAciYAQ1X9uV-RD5zWcp02bw7fgKr8IgwmORyHUhLnnwY8FRYTUQQcTlNHsHVzsbSQ7FYZn7IST9O7nKiSyUryh3VJyCnEqyyvpDzIhSjNjPIyIprpKZoZL1iVKH0oLL_4slT79DVAmuOsUjUKjXnWHbImrF0ZZissrfHPCE2708RWwu-ZgpSsmhPqgRpgE-TlX803ziNo2kvazKpdPg6O6MGG7H2yV8PUW-zwVXO1XxJTK4Nw 52 53 // Make the call to obtain credentials 54 AWS.config.credentials.get(function () { 55 // Credentials will be available when this function is called. 56 var accessKeyId = AWS.config.credentials.accessKeyId; 57 var secretAccessKey = AWS.config.credentials.secretAccessKey; 58 var sessionToken = AWS.config.credentials.sessionToken; 59 var identityId = AWS.config.credentials.identityId; 60 console.log(accessKeyId); 61 console.log(secretAccessKey); 62 console.log(sessionToken); 63 console.log(identityId); 64 65 }); 66 } 67 </script> 68 69 70 </html>
