API网关和AWS Lambda进行身份验证

当Foreach最初涉足微服务领域时,我们并没有真正构建微服务。 我们以为我们做到了,但是我们所有的服务中总存在一些逻辑。 当然,每个服务实际上应该只专注于自己的任务,而不应该专注于属于另一个微服务的事物。 我们这方面最明显的棘手是认证和授权逻辑。

在某个时候,我们有几个“微”服务,它们根据AuthenticationService (甚至在较早的日子,甚至是针对共享数据库)验证了传入请求的Authorization标头。 这给我们的AuthenticationService造成了比我们想要的更多的负载(多次验证同一个令牌),但是这也导致在所有这些服务中都存在一些重要的代码。 而且,正如任何开发人员所知,共享代码铺平了通往地狱的道路。 微服务变得超出其实际用途,这使得它们变得更难开发和维护。

在寻求救赎的过程中,我们Swift找到了一些可以帮助我们的解决方案。

JSON Web令牌

我们考虑的第一件事是开始使用JSON Web令牌(JWT) 。 JWT是一个开放标准,它定义了一种独立的方式来在各方之间安全地传输信息。 自包含意味着令牌本身可以包含我们需要的所有信息,例如用户的标识符或用户名。 安全意味着其他方不可能干扰这些令牌。 令牌包含一个加密部分,要解密它,您需要一个只有您知道的秘密密钥。 换句话说,如果令牌已被篡改,您将知道。

JWT是一个非常有趣的领导者,因为在我们这方面进行最小的调整,从理论上讲,我们甚至可以消除微服务中的一些额外工作量(无论如何它们都不应该这样做)。 令牌的验证是一个最小的过程,可以很好地集成到Spring框架中,因此我们不需要那么多代码。 令牌还将包含我们需要的所有信息,因此我们不再需要从另一个Web服务请求此信息。

但是,JWT的问题在于,已经有其他各方开发的其他一些应用程序与API集成在一起。 事实证明,当我们开始分发JWT令牌时,并不是所有的应用程序都那么满意。 由于短期内无法更改这些应用程序,因此我们暂时保留了这一想法。

API网关

我们的另一个想法是引入API网关。 这可以看作是我们API的包装,意在为最终用户抽象我们的API。 它可以更改对另一种格式的响应。 它可以将多个HTTP请求合并为一个请求。 或者它可以提供其他监视功能(例如“谁向某个端点发送垃圾邮件?”)。 但最重要的是,它应该抽象与身份验证有关的所有内容。

在我们的例子中,想法是API网关甚至在请求被代理到我们的应用程序之前都会验证传入的Authorization标头。 它应该缓存结果,以便如果同一用户请求五个端点,我们仍然每小时仅验证一次令牌,并且应该将身份验证信息传递给我们的API,以便我们知道谁在请求资源。

我们的解决方案:AWS API Gateway

认证方式

https://docs.aws.amazon.com/apigateway/latest/developerguide/images/custom-auth-workflow.png

市场上有许多符合此描述的产品,但经过一番考虑,我们决定尝试一下AWS API Gateway。 我们实施了自定义的“授权人”。 这是一个Lambda函数,它接收客户端提供的授权令牌作为输入,并返回客户端是否有权访问所请求的资源。 如果身份验证被拒绝,API网关将向客户端返回403 HTTP代码。 否则,该请求将被代理到我们的服务中。 授权者Lambda的结果在缓存中保留了一个小时。 我们还希望使用HTTP标头将用户的身份传递给我们的基础服务。 这样,我们知道谁在我们的应用程序中执行请求。

授权者

我们的自定义Lambda函数是用Python编写的。 它从传入的请求中获取Authorization标头,并向我们的AuthenticationService启动HTTP请求-这是我们唯一可以验证传入的信息是否有效以及令牌适用于谁的地方。 这个HTTP请求将告诉我们最终用户是谁。

Lambda函数的代码(主要基于AWS提供的示例代码)如下所示:

from __future__ import print_function import re 
import urllib2 
import base64 
import json 
import os def lambda_handler(event, context): print("Client token (provided): " + event['authorizationToken']) clientAuthorizationToken = re.sub('^%s' % 'Bearer', '', re.sub('^%s' % 'bearer', '', event['authorizationToken'])).strip() print("Client token (parsed): " + clientAuthorizationToken) print("Method ARN: " + event['methodArn']) url = os.environ['CHECK_TOKEN_ENDPOINT'] + "?token=" + clientAuthorizationToken print("Check token URL: " + url) authorizationHeader = 'Basic %s' % base64.b64encode(os.environ['CHECK_TOKEN_ENDPOINT_CLIENT_ID'] + ':' + os.environ['CHECK_TOKEN_ENDPOINT_CLIENT_SECRET']) print("Our authorization header: " + authorizationHeader) tmp = event['methodArn'].split(':') apiGatewayArnTmp = tmp[5].split('/') awsAccountId = tmp[4] policy = AuthPolicy('urn:user:unknown', awsAccountId) policy.restApiId = apiGatewayArnTmp[0] policy.region = tmp[3] policy.stage = apiGatewayArnTmp[1] request = urllib2.Request(url, headers={"Authorization": authorizationHeader}) try: result = urllib2.urlopen(request) data = json.load(result) print("HTTP Response data: " + str(data)) context = { 'userUrn':  data['user_urn'] if data.has_key('user_urn') else None, 'clientId': data['client_id'] } policy.principalId = data['user_urn'] if data.has_key('user_urn') else 'urn:client:%s' % data['client_id'] policy.allowMethod('*', '*') print('Allowing resource %s. Client: %s, User: %s, Principal: %s' % (policy.allowMethods[0]['resourceArn'], context['clientId'], context['userUrn'], policy.principalId)) except urllib2.HTTPError, e: print("Error during the HTTP call: %s" % e) policy.denyAllMethods() context = {} authResponse = policy.build() authResponse['context'] = context return authResponse class HttpVerb: GET = 'GET' POST = 'POST' PUT = 'PUT' PATCH = 'PATCH' HEAD = 'HEAD' DELETE = 'DELETE' OPTIONS = 'OPTIONS' ALL = '*' class AuthPolicy(object): awsAccountId = '' principalId = '' version = '2012-10-17' pathRegex = '^[/.a-zA-Z0-9-\*]+$' allowMethods = [] denyMethods = [] restApiId = '*' region = '*' stage = '*' def __init__(self, principal, awsAccountId): self.awsAccountId = awsAccountId self.principalId = principal self.allowMethods = [] self.denyMethods = [] def _addMethod(self, effect, verb, resource, conditions): if verb != '*' and not hasattr(HttpVerb, verb): raise NameError('Invalid HTTP verb ' + verb + '. Allowed verbs in HttpVerb class') resourcePattern = re.compile(self.pathRegex) if not resourcePattern.match(resource): raise NameError('Invalid resource path: ' + resource + '. Path should match ' + self.pathRegex) if resource[:1] == '/': resource = resource[1:] resourceArn = 'arn:aws:execute-api:{}:{}:{}/{}/{}/{}'.format(self.region, self.awsAccountId, self.restApiId, self.stage, verb, resource) if effect.lower() == 'allow': self.allowMethods.append({ 'resourceArn': resourceArn, 'conditions': conditions }) elif effect.lower() == 'deny': self.denyMethods.append({ 'resourceArn': resourceArn, 'conditions': conditions }) def _getEmptyStatement(self, effect): statement = { 'Action': 'execute-api:Invoke', 'Effect': effect[:1].upper() + effect[1:].lower(), 'Resource': [] } return statement def _getStatementForEffect(self, effect, methods): statements = [] if len(methods) > 0: statement = self._getEmptyStatement(effect) for curMethod in methods: if curMethod['conditions'] is None or len(curMethod['conditions']) == 0: statement['Resource'].append(curMethod['resourceArn']) else: conditionalStatement = self._getEmptyStatement(effect) conditionalStatement['Resource'].append(curMethod['resourceArn']) conditionalStatement['Condition'] = curMethod['conditions'] statements.append(conditionalStatement) if statement['Resource']: statements.append(statement) return statements def allowAllMethods(self): self._addMethod('Allow', HttpVerb.ALL, '*', []) def denyAllMethods(self): self._addMethod('Deny', HttpVerb.ALL, '*', []) def allowMethod(self, verb, resource): self._addMethod('Allow', verb, resource, []) def denyMethod(self, verb, resource): self._addMethod('Deny', verb, resource, []) def allowMethodWithConditions(self, verb, resource, conditions): self._addMethod('Allow', verb, resource, conditions) def denyMethodWithConditions(self, verb, resource, conditions): self._addMethod('Deny', verb, resource, conditions) def build(self): if ((self.allowMethods is None or len(self.allowMethods) == 0) and (self.denyMethods is None or len(self.denyMethods) == 0)): raise NameError('No statements defined for the policy') policy = { 'principalId': self.principalId, 'policyDocument': { 'Version': self.version, 'Statement': [] } } policy['policyDocument']['Statement'].extend(self._getStatementForEffect('Allow', self.allowMethods)) policy['policyDocument']['Statement'].extend(self._getStatementForEffect('Deny', self.denyMethods)) return policy

网关配置

创建Lambda函数之后,该配置网关了。 您可以在AWS控制台中或使用CloudFormation模板执行此操作。 我们不会详细解释如何配置API网关,因为这是AWS站点上记录良好的任务 。 但是,我将解释一些配置授权者的细节。

授权人

在“ API网关配置”部分中的左侧,您会看到“授权者”选项。 您可以在那里选择创建新的授权者。 当您单击按钮时,您将看到以下表格:

认证方式

重要事项:

  • Lambda函数:选择之前创建的授权者Lambda
  • Lamba事件有效负载:令牌
  • 令牌来源:授权(如果您的客户端使用“授权”标头发送令牌)
  • 授权缓存:已启用

资源资源

接下来,我们转到您要保护的方法。 单击左侧的资源,然后在列表中选择一种方法。 您应该看到类似于以下屏幕的屏幕:

认证方式

点击“方法请求”。 然后,您可以在顶部配置为使用之前添加的授权者。

认证方式

返回上一个屏幕,然后单击“集成请求”。 在底部,我们将配置一些要发送到API的标头。 这些包含有关用户的信息,我们将在API中使用这些信息来了解谁在发出请求。 注意:我们不必担心恶意用户在请求中发送这些标头。 我们的自定义授权者的结果将覆盖它们。

认证方式

未来

虽然我们当前的实施在生产中运行良好,但我们始终在寻找有关如何改进产品以及由此向客户提供服务的想法。 我们将继续关注的事情之一是,有一天开始使用JWT令牌,这很可能与API Gateway结合使用。 这将使设置更加容易,但是将需要对某些应用程序进行更改,而这是我们目前无法做到的。

此外,我们确实对如何从API网关中获取更多信息有一些想法。 我们对每个应用程序和每个用户的速率限制非常感兴趣。 我们希望能够以这种方式配置移动应用程序,例如,仅允许每小时执行一百个请求,或者仅允许某个最终用户少量请求。

将API Gateway与AWS Lambda结合使用是一种相对简单的方法,可以向您的应用程序添加可靠的身份验证方法,而不会中断其他服务。

翻译自: https://www.javacodegeeks.com/2018/11/api-gateway-aws-lambda-authentication.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/344898.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【渝粤教育】广东开放大学 企业项目报表分析 形成性考核 (35)

题库查询系统 选择题 题目:流动债的流动性分析主要从流动负债的到期日流动负债的推迟可能性两方面进行。 答案: A、正确 题目:营业利润是以主营业务利润为基础加上其他业务利润减去销售费用、管理费用和财务费用,再加上营业外收入减去营业外支…

魅蓝x android 7,魅蓝x2什么时候发布 魅蓝x2发布时间最新消息

日前,魅蓝品牌掌门人李楠或kkk便在微博上放出“bluegate(蓝色大门)”这样一个句子,似乎暗示魅蓝品牌或在近期会有动作,并且由于该条微博的尾巴显示为“下款魅蓝Android”,所以在不少网友看来,或许预示着魅蓝将有新机即…

【渝粤教育】广东开放大学 数据库原理与应用 形成性考核 (1)

题库查询系统 选择题 题目:数据库系统是采用了数据库技术的计算机系统,它是一个集合体,包含数据库、计算机硬件、软件和( ) 答案: A、数据库管理员 题目:数据库(DB)&am…

Android 21mod,熊猫博士小镇合集 Mod

游戏介绍打通30个独具特色的区域界限,探索并发掘各式各样的奇妙冒险!开启乐趣无限的冒险!想成为一名维护正义的警察,或经营一间自己的美发沙龙,或成为一名救治动物的兽医?在熊猫博士小镇中,一切…

内存泄漏代码_调查内存泄漏第1部分–编写泄漏代码

内存泄漏代码前几天,我发现了这个小问题:该服务器运行了一段时间,然后掉下来了。 然后通过启动脚本重新启动,整个过程重复进行。 听起来并没有什么坏处,因为它虽然对数据造成了重大损失,但对业务的重要性并…

【渝粤教育】广东开放大学 广东开放大学学习指引 形成性考核 (28)

题库查询系统 选择题 题目:在开放大学的学习中认识同学可以增强开放大学学生间的交流与友情,促进学生对开放大学的归属感与认同感,克服学习中的孤独感。 答案: A、“对”。 题目:广东开放大学服务于广东学习型社会&…

【渝粤教育】广东开放大学 法理学 形成性考核 (46)

题库查询系统 选择题 题目:法理学与部门法的关系是( )的关系。 答案: A、理论与实际 B、 一般与特殊 C、 整体与局部 D、论与史 题目:法的最终决定因素是()。 答案: A、阶级斗争状况…

android 打印kernel log,android8.0 kernel4.9.44 各层log打开

一.问题平台android8.0 kernel4.9.44,想加个i2c驱动,打开log都是一件脑瓜疼的事。二.log总结android有三种log。1.kernel下面的,就是driver层log。2.kernel上面的,init和init.rc启动的程序的log,应用层3.kernel上面的&…

【渝粤教育】广东开放大学 网络完全与技术 形成性考核 (44)

题库查询系统 选择题 题目:以下哪个不属于数字签名的功能( ) 答案: A、不可伪造的 B、可重用的 C、可信的 D、不可抵赖的 题目:前缀为Worm的病毒是( ) 答案: A、蠕虫病毒 B、后门病毒 C、杩 D、脚本病毒 题目:下面关于…

Java整数缓存-为什么Integer.valueOf(127)== Integer.valueOf(127)为True

在一次采访中,我的一个朋友被问到如果我们有两个Integer对象, Integer a 127; Integer b 127; Integer a 127; Integer b 127; 为什么当a b都持有两个单独的对象时,其值为true ? 在本文中,我将尝试回答这个问题&a…

【渝粤教育】广东开放大学 中国法律史 形成性考核 (31)

题库查询系统 选择题 题目:春秋时期在晋国铸刑鼎的是 答案: A、赵鞅 题目:汉代的买卖契约叫做 答案: A、券书 题目:西周法官在审判中判断当事人陈述真伪的方式叫做 答案: A、“五听” 题目:明朝…

高鸿股份与鸿蒙,高鸿股份(000851)个股分析_牛叉诊股_同花顺财经

资金净流入资金净流出行业平均线[{"date":"2021-04-14","value":"1028.65","field":null},{"date":"2021-04-15","value":"213.51","field":null},{"date":&q…

【渝粤教育】广东开放大学 会展项目管理 形成性考核 (59)

题库查询系统 选择题 题目:为了确保项目团队和其他项目干系人完全理解并且投入到项目,目标必须是? 答案: A、现实的和可达到的 题目:在一定的组织里,一个项目一般不会正式启动,除非完成了&#…

【渝粤教育】广东开放大学 原画设计 形成性考核 (23)

题库查询系统 选择题 题目:在图层蒙版里用黑色画笔涂抹,可以遮盖住图层内相对应位置的图像信息 答案: A、正确 题目:图层样式描边的描边是根据选区边缘或路径来做描边的 答案: A、正确 题目:魔术橡皮擦工具…

android软解码花屏,视频花屏 · Issue #386 · bilibili/ijkplayer · GitHub

各位大神,还是没法解码ijkmp_set_format_callback(0x10d5e5, 0x17ec4a20)ijkmp_set_format_callback()voidijkmp_set_option_int(start-on-prepared, 1)ijkmp_set_option_int()voidijkmp_ios_set_view(glView0x17d36c10)ijkmp_ios_set_view(glView0x17d36c10)voidij…

【渝粤教育】广东开放大学 土木工程材料 形成性考核 (22)

选择题 题目:下列外加剂不能用于改善混凝土的耐久性的是 。 题目:安定性不良的水泥严禁在工程中使用。 题目:有硫酸盐腐蚀的混凝土工程应优先选择( )水泥 题目:有耐热要求的混凝土工程,应优先选…

记事本写html怎么加a1图片,记事本-功能待添加

IO流、图形化用户界面、事件监听import java.awt.event.*;import javax.swing.*;import java.io.*;public class Jsb extends JFrame implements ActionListener{JMenuBar cd;JMenu cd1,cd2;JMenuItem cdx1,cdx2;JTextArea wby;JScrollPane gd;public static void main(String[…

orika 映射非空字段_Orika:将JAXB对象映射到业务/域对象

orika 映射非空字段这篇文章着眼于使用Orika将JAXB对象映射到业务域对象。 本月初, 我使用基于反射的Dozer讨论 了相同的映射用例 。 在本文中,我假设需要映射相同的示例类,但是它们将使用Orika而不是Dozer进行映射 。 Dozer和Orika旨在解决…

【渝粤教育】广东开放大学 应用创意写作 形成性考核 (54)

选择题 题目:《四库全书》将图书分为 ( )四部。 题目:“五经”指 ( )。 题目:汉代 ( )在《论六家要旨》中,重点对春秋战国时诸子百家中的儒、墨、名、法、道、…

【渝粤教育】广东开放大学 教育心理学 形成性考核 (42)

选择题 题目:认知过程不包括以下哪种要素() 题目:下列说法中关于动机的说法中正确的是() 题目:关于感觉和知觉下列说法中错误的是 ( ) 题目:下列关于意识和无…