写点什么

利用 Cognito Group 信息管理多套 API Gateway+lambda 环境

2019 年 9 月 20 日

利用Cognito Group信息管理多套API Gateway+lambda环境

随着目前 Serverless 微服务的应用越来越广泛,如何安全有效的管理多套 serverless 环境构建自己的应用系统逐渐成为一个大家关心的问题。 此文针对客户使用多套 API Gateway + Lambda 的场景,介绍了如何利用 Cognito 来实现访问权限的管理与区分。Cognito 用户池中不同 group 信息的用户可以访问不同的微服务环境。如果一个用户同时属于多个 group,则当前用户可以访问多套环境。终端用户将没有权限访问自己并不属于的 group 的 API 资源。


架构图


总述

Cognito 为 AWS 提供的 mobile 端访问控制工具。利用 Cognito User Pool,可以方便的实现 web 应用用户的注册,登录,登出等功能。Cognito 提供的用户池自带多种属性可以供管理者选择,其中包括 group,即组别信息。一旦拿到 group 的值,我们就可以利用该属性去做一些权限的判定与区分。本文利用两种方式来实现此过程,两者均利用 lambda 自定义 Authorization 来实现。每套 serverless 环境,需要一个 API Gateway,两个 lambda 函数。


  • 一种是前端解析 cognito 生成的 JWT token,将用户的 cognito:group 信息直接传给 API Gateway,API Gateway Authorization Lambda 通过对 group 信息的条件判断决定是否 allow 访问,仅允许本组成员访问本组资源,若为其他组成员,拒绝访问(您也可以将此值换成其他的 attribute);

  • 另一种方式是将 cognito 生成的 JWT token 传递给后端,后端通过 decode 解析整个 token,将解析后的值来做判断,并且可以将解析后的值进一步传递给后端的 lambda 来做正常的业务逻辑。 两种方法对比下来,前者更为简单,而后者则更为灵活。

  • 有关于 Cognito 生成的 JWT Token 的更多解释,请点击此页面


本文用到的代码点击这里获取<<<<


步骤

  1. 创建 cognito 用户池 user pool

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

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

  4. 选择应用客户端,取消 generate client secret 的选项

  5. 在左侧 APP-Integration 项目下,需要我们修改的有 2 个地方,一是 APP client setting,修改 callback URL 以及 scope token 作用范围,二是自定义 domain name(需要全 region 唯一)

  6. 注意:localhost:8000 仅在测试环境中使用。实际生产环境,这里的 callback 不支持 http 协议,请修改为 https 的网址。请勿写入 http://ip 等形式。


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


  1. 搭建 API Gateway 资源

  2. 如果还没有 API 资源,新建 rest-new API, 命名资源后,添加方法

  3. 在本例当中,我们添加一个 get method。点击 get 方法后,进入 API Gateway 的配置页面。

  4. 在 Integration Request 当中,选择 intergration type 为 lambda,配置自己的 lambda 函数名

  5. 注:此 lambda 函数为最终执行逻辑的业务层级的 lambda 函数,而不是 authorizer

  6. 增加 Lambda 自定义认证方式

  7. 添加新的 authorizer,选择用来做认证的 lambda 函数,event payload 选择 token,invoke role 留空。不建议开启 caching,以防止因存在缓存而出现测试结果混乱的可能。 除了 token 以外,事实上,API Gateway authorizer 也支持用 request 的方式(如 query string)来传递此值,本文对该话题不做进一步展开。

  8. 在 authorizer lambda 函数的设置当中,我们可以任意自定义规则。 在条件判断完毕后,允许的 policy example 如下:



allow_response = { "principalId": "random", "policyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": "execute-api:Invoke", "Effect": "Allow", "Resource":"<API-Gateway-method-arn>" } ] }, #需要传递给后端lambda的值 "context": { "key": "test", "numKey": 1 } }
复制代码


注意:的完整格式为 arn:aws:execute-api:{regionId}:{accountId}:{appId}/{stage}/{httpVerb}/[{resource}/[{child-resources}]]. 例如 arn:aws:execute-api:ap-northeast-1:1234567799:xxxxxxxx/beta/GET/(如果有 resource 传参接着写{resource})


deny 的 example policy 如下:


  deny_response= {    "principalId": event['authorizationToken'],    "policyDocument": {      "Version": "2012-10-17",      "Statement": [        {          "Action": "execute-api:Invoke",          "Effect": "deny",          "Resource":"<API-Gateway-method-arn>"        }      ]    }  }
复制代码


在本例当中,我们有两种方式可实现此判断,一种是直接传递解析过后的 value,另外一种是传递 jwt token,由 authorization lambda 自己做解析拿到 payload。以下为具体实现。


(0)创建 lambda 函数 进入 lambda 控制台, 选择右上角按钮“创建函数”, 命名函数名称,选择 python2.7 作为语言,并且给 lambda 函数一个角色(role)。如果 lambda 函数有对 AWS 其他产品或服务的调用,则需确保 lambda 的角色有访问其他产品服务的权限,否则会报 403 permission denied。有关于更多 role 的解释和说明,请参考这里.



(1)我们校验前端传递过来的 group 信息并做判断。如果该 user 的 group value 为本组资源,则允许访问 allow,否则 deny.lambda python2.7 参考代码如下, 也可以点击这里下载


import json
def lambda_handler(event, context): #获取group信息 groups= event['authorizationToken'].split(",") #如果只属于一个group,且为本组,则允许访问 if (len(groups) ==1): if (event['authorizationToken'] == 'group1'): response = allow_response
#否则deny else: response = deny_response return response else: #如果有多个group信息,只要有一组是本组资源,允许访问; for group in groups: #print(group) if (group == 'group1'): response = allow_response return response #循环完毕,没有本组信息,deny response = deny_response return response

配置完毕后可以点击test测试是否返回正确的policy,如
复制代码


{


“type”:“TOKEN”,


“authorizationToken”:"{caller-supplied-token}",


“methodArn”:“arn:aws:execute-api:{regionId}:{accountId}:{appId}/{stage}/{httpVerb}/[{resource}/[{child-resources}]]”


}


(2)前端将完整的JWT token传递给后端 cognito有三种token,分别为idToken, access Token以及refresh token。本demo当中附带的index.html通过前端的方式直观的展示了这三种token解析出来的payload。我们可以看到,idtoken与accesstoken解析出的claims包含cognito:groups,username,email等attribute。 ![](https://static001.infoq.cn/resource/image/53/37/53ce660419837d2fd7c7dbc2befc4637.png)idToken与accessToken均由三个部分组成,header,payload,以及signature。格式是这样的11111111111.22222222222.33333333333 在本文当中,我们只对payload做验证。实际生产也可以增加对signature的验证。有关于signature验证的解释,请参考这里.
python示例如下

复制代码


full_token=idToken.split(’.’)


b64_string=full_token[1] #payload token


b64_string += “=” * ((4 - len(b64_string) % 4) % 4) #ugh


print(base64.b64decode(b64_string)) #解析后的完整 json



这样我们就拿到了解析后的对应的json值。lambda可自行进行if逻辑判断或者传参给后端。
5. enable跨域功能在我们的demo中,我们会用localhost访问此API gateway,因此需要enable CORS否则会报跨域无法访问的错误 ![](https://static001.infoq.cn/resource/image/3c/6f/3cd1061f16d4137b1f3ac57283a8516f.png)在header处添加Authorization和Access-Control-Allow-Origin标头 ![](https://static001.infoq.cn/resource/image/16/fa/160266e500ec2c66267c23d5bb04eafa.png)6. 部署API在以上流程没有问题后,点击部署API ![](https://static001.infoq.cn/resource/image/96/a2/9629f43b69b424fe657f9a18f053b5a2.png)一定要注意,在每一次更新API的配置或者设置之后,一定要重新部署API,否则不会生效。
7. 测试用postman等API访问工具直接访问此get请求时,或方法(1)带authorization header但非group1信息时,方法(2)token不对时,均会显示无法访问 ![](https://static001.infoq.cn/resource/image/56/9d/561ac6fc2fffbd6b033a1db18db1029d.png)
只有header带group1时,可以实现访问 ![](https://static001.infoq.cn/resource/image/95/33/95b6638fc75db1abad7a59eade7a8033.png)8. 结合前端cognito测试可在前端注册用户,添加group,并将用户添加到某个group当中。 ![](https://static001.infoq.cn/resource/image/70/de/70aa627f174ca44894ce4ca76f29fbde.png)cognito JS核心代码如下,在代码可以demo用户登录获取token,解析token的过程。此demo的完整代码在这里下载,有关于cognito JS更多示例以及use case,可以点击此页面查看。
复制代码


$.ajax({


url: “https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/beta”,


type: “GET”,


beforeSend: function(xhr){


xhr.setRequestHeader(‘Authorization’, cognito_groups) ;


xhr.setRequestHeader(‘Access-Control-Allow-Origin’,’*’);


}


});


需要修改的字段如下: 根据两种方法的不同,为API endpoint的header当中发送的值可以是解析后的cognito_group,也可以是idtoken或是accessToken
修改cognito app client配置以及API endpoint
复制代码


function initCognitoSDK() {


AWS.config.region =‘ap-northeast-1’;


var authData = {  ClientId : '<app-client-id>', // Your APP client id here  AppWebDomain : '<your-custom-domain-name', // Exclude the "https://" part.   //TokenScopesArray : ['openid','email'], // like ['openid','email','phone']...  TokenScopesArray : ['openid'],   RedirectUriSignIn : 'http://localhost:8000',  //RedirectUriSignIn:"https://sdb53tv9o0.execute-api.ap-northeast-1.amazonaws.com/beta",  RedirectUriSignOut : 'http://localhost:8000',      UserPoolId : '<user-pool-id>',       AdvancedSecurityDataCollectionFlag : false};var login = {};var auth = new AmazonCognitoIdentity.CognitoAuth(authData);
// You can also set state parameter // auth.setState(<state parameter>); auth.userhandler = { onSuccess: function (result) { //根据传递的值不同,这里可以为解析后的cognito_group,也可以是idtoken或是accessToken var cognito_groups= showSignedIn(result); $.ajax({ url: "https://<API-address>/<stage>/", //替换为自己的API Gateway endpoint type: "GET", beforeSend: function(xhr){ xhr.setRequestHeader('Authorization', cognito_groups) ; xhr.setRequestHeader('Access-Control-Allow-Origin','*'); }, success: function(data) { console.log(data); } });
}, onFailure: function (err) { console.log('error',err); } };return auth;
复制代码


}



修改跳转地址

复制代码


function userButton(auth) {


var state = document.getElementById(‘signInButton’).innerHTML;


if (state === “Sign Out”) {


document.getElementById(“signInButton”).href=“https:///logout?response_type=code&client_id=&logout_uri=http://localhost:8000”;document.getElementById(“signInButton”).innerHTML = “Sign In”;auth.signOut();showSignedOut();


} else {  //session_info = auth.getSession();  //console.log(session_info);  document.getElementById("signInButton").href="https://<your-custom-domain-name>/login?response_type=code&client_id=<app-client-id>&redirect_uri=http://localhost:8000";
}
复制代码


}



可以打开浏览器–tools–developer tools,通过console的log查看API是否调用成功,或者通过networking tab查看API的response情况(200/403).
会发现只有当前user属于group1时,才允许访问,否则都为deny。
![](https://static001.infoq.cn/resource/image/8d/17/8d7e1307133b06e52020df462ffb4917.png)
## 资源销毁1. 删除API Gateway2. 删除lambda函数3. 删除cognito user pool
## 参考资料https://aws.amazon.com/cn/blogs/compute/introducing-custom-authorizers-in-amazon-api-gateway/
https://docs.aws.amazon.com/zh_cn/cognito/latest/developerguide/using-amazon-cognito-user-identity-pools-javascript-examples.html
https://docs.aws.amazon.com/zh_cn/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html
** 作者介绍:**
李天歌AWS解决方案架构师

张贝贝AWS 解决方案架构师
**本文转载自AWS博客。**
**原文链接:**https://amazonaws-china.com/cn/blogs/china/cognito-group-api-gateway-lambda/
复制代码


2019 年 9 月 20 日 08:08508
用户头像

发布了 1227 篇内容, 共 31.7 次阅读, 收获喜欢 33 次。

关注

欲了解 AWS 的更多信息,请访问【AWS 技术专区】

评论

发布
暂无评论
发现更多内容

嘿,我想要寄一封挂号信,收件时间是 6 年后,标题是: 让 6 年后的我,加倍奉还。

叶小鍵

学习 成功学 心理学 李笑来

干货!如何平稳用户无感知的完成系统重构升级

X先生

架构 运维 后台

第4周作业

Vincent

极客时间 极客大学

【万字长文】探讨可信构架之道

华为云开发者社区

架构 服务端

协同新机遇:让研发敏捷起来

人称T客

面试官:TCP/IP 协议到底在讲什么?想彻底搞懂TCP协议:还得从 TCP 三次握手四次挥手说起

云流

编程 程序员 互联网 计算机网络 面试求职

解Bug之路-串包Bug

无毁的湖光

redis socket Java 分布式

通证与通证经济你真的理解吗

CECBC区块链专委会

区块链 通证经济

模板方法模式——看看 JDK 和 Spring 是如何优雅复用代码的

Java架构师迁哥

敏捷教练的软技能

技术管理Jo

软技能 敏捷教练 引导者

为什么我的缓存设置在chrome中不生效

书虫

chrome 缓存 浏览器 HTTP

媒体电视台跟进,船长梁晓玲平台拉人头卖课引起多方报道!

成周

The Go Blog-Article index

卓丁

教师节特别活动:第四范式多项自研技术及其应用实践分享

天枢数智运营

人工智能 推荐系统 第四范式 个性化推荐

使用开源软件构建工业互联网的平台

刘旭东

工业互联网 Odoo thingsboard

oeasy 教您玩转 linux 010207 黑客帝国 matrix

o

传统产业数字化转型的思考与建议

CECBC区块链专委会

经济转型 企业经济

【原创】经验分享:一个Content-Length引发的血案(almost....)

一枝花算不算浪漫

java安全编码指南之:表达式规则

程序那些事

java安全编码 java安全 安全编码规则

数据质量管理工具的意义和定位

苏槐

数据治理 数据质量管理 数据质量平台

MySQL高性能架构设计原则

李浩宇/Alex

物联网通信技术最全科普!你一定要了解的NB-IoT

华为云开发者社区

物联网

CSS常用样式——绘制单(双)箭头的多种方法(2)

程序员学院

CSS html 程序员

Redis 哨兵模式

是老郭啊

redis redis哨兵模式 redis哨兵 redis哨兵集群

第4周总结

Vincent

极客时间 极客大学

深度解析!--阿里开源分布式事务框架Seata

攀鱼飞岩

分布式 分布式事务 微服务 分布式锁 Seate

Docker 容器编排利器 Docker Compose

哈喽沃德先生

Docker 容器 微服务 Docker-compose 容器化

新基建夯实粤港澳大湾区高质量发展基础

CECBC区块链专委会

区块链 人工智能 大数据

去中心化交易所搭建,虚拟币去中心化交易系统

13823153121

交易所开发

云图说 | GPU共享型AI容器,让AI开发更普及

华为云开发者社区

AI 容器

大厂运维必备技能:PB级数据仓库性能调优

华为云开发者社区

架构 数据

演讲经验交流会|ArchSummit 上海站

演讲经验交流会|ArchSummit 上海站

利用Cognito Group信息管理多套API Gateway+lambda环境-InfoQ