我们正处于一个信息大暴发的时代,每天都能产生数以百万计的新闻资讯!
虽然有大数据推荐,但面对海量数据,通过我们的调研发现,在一个小时的时间里,您通常无法真正有效地获取您感兴趣的资讯!
头条新闻资讯订阅,旨在帮助您收集感兴趣的资讯内容,并且在第一时间通知到您。可以有效节约您获取资讯的时间,避免错过一些关键信息。
使用无服务器进行构建的好处之一是,您可以以一种可组合的方式设计事物。这意味着您可以将逻辑与其他志同道合的逻辑紧密结合在一起,然后让事物与其他组件保持松散耦合,以便事物易于更改而不会太脆弱。构建API时,您通常需要某种授权方来验证所提供的令牌。在本文中,我将逐步介绍如何使用Golang构建自定义API网关授权方。使用Golang的API网关授权方作为参考,这是我想向您展示的架构图。使用Golang的API网关授权方
以上实现的是以下内容定义一个API网关来管理我们资源的有效负载使用Lamabda处理授权针对Cognito用户池验证令牌利用具有自定义设置TTL的缓存来节省计算最后,如果一切顺利,允许访问受保护的资源也将能够提供对声明上下文的覆盖这篇文章还有另一半,我将向您展示如何使用Lambdas和DyanamoDB扩展我们将使用的JWT。如果您对此感到好奇,这里的文章将向您展示这是如何完成的遍历代码CDK从Cognito开始要使用Cognito进行验证,我们首先需要构建一个Cognito实例以及一个能够登录的客户端。定义UserPool如下所示。没有什么需要额外解释的,所以让我们继续讨论客户端。this._pool=newcognito.UserPool(this,"SamplePool",{userPoolName:"SamplePool",selfSignUpEnabled:false,signInAliases:{email:true,username:true,preferredUsername:true,},autoVerify:{email:false,},standardAttributes:{email:{required:true,mutable:true,},},customAttributes:{isAdmin:newcognito.StringAttribute({mutable:true}),},passwordPolicy:{minLength:8,requireLowercase:true,requireDigits:true,requireUppercase:true,requireSymbols:true,},accountRecovery:cognito.AccountRecovery.EMAIL_ONLY,removalPolicy:cdk.RemovalPolicy.DESTROY,});将客户端添加到UserPool也很简单。这么多的选择,但我下面的是相当香草。使用此客户端,您可以通过一种方式与用户一起登录并针对它进行其他应用程序开发。正如您稍后将在本文中看到的那样,我只是使用Postman将所有这些整合在一起。this._pool.addClient("sample-client",{userPoolClientName:"sample-client",authFlows:{adminUserPassword:true,custom:true,userPassword:true,userSrp:false,},idTokenValidity:Duration.minutes(60),refreshTokenValidity:Duration.days(30),accessTokenValidity:Duration.minutes(60),});建立授权者现在“自定义”使用Golang构建自定义API网关授权器。Authorizer只不过是一个Lambda函数。因此,如果您愿意,这可以是从另一个堆栈导入的。但为简单起见,我将所有内容都包含在这一套基础设施中。如果您想更深入地了解CDK和GoFunction,这里有一篇文章可以帮助您CDK中的函数定义。exportclassAuthorizerFunctionextendsConstruct{privatereadonly_func:GoFunction;constructor(scope:Construct,id:string,poolId:string){super(scope,id);this._func=newGoFunction(this,"AuthorizerFunc",{entry:path.join(__dirname,`../../../src/authorizer`),functionName:"authorizer-func",timeout:Duration.seconds(30),environment:{USER_POOL_ID:poolId,},});}getfunction():GoFunction{returnthis._func;}}正如我上面提到的,一个简单的GoFunction实现。唯一需要注意的有趣的事情是USER_POOL_ID的环境变量。让我们来看看为什么这很重要。Golang中的函数实现对于这个使用Golang构建自定义API网关授权方的示例,我将验证JWT并添加一些额外的上下文。您的实施可能会有很大不同,这也是我喜欢这种方法的原因。您可以根据需要拥有多个不同的授权者,并且您的受保护资源不知道调用堆栈中在它们之上发生的事情。我想向您展示的第一件事是如何为众所周知的Cognito端点建立密钥集。我在init()函数中这样做是因为我知道它会在Lambda初始化时运行一次,然后我将输出“缓存”在一个变量中,该变量将在Lambda调用中保持自身。不是冷启动,而是调用。funcinit(){log.SetFormatter(&log.JSONFormatter{PrettyPrint:false,})log.SetLevel(log.DebugLevel)region:="us-west-2"poolId:=os.Getenv("USER_POOL_ID")varerrerrorjwksUrl:=fmt.Sprintf("https://cognito-idp.%s.amazonaws.com/%s/.well-known/jwks.json",region,poolId)keySet,err=jwk.Fetch(context.TODO(),jwksUrl)iferr!=nil{log.WithFields(log.Fields{"error":err,"url":jwksUrl,}).Fatal("errorgettingkeyset")}}jwksUrl上面的变量记录在AWS开发人员指南中。我使用来"github.com/lestrrat-go/jwx/jwt"表示KeySet我将使用的来验证令牌的真实性和过期时间。还记得USER_POOL_ID上面CDK中的变量吗?这就是它发挥作用的地方。构建众所周知的端点需要UserPoolId此过程的下一部分是执行验证。我不打算在本文中详细说明这是如何发生的,但本质上图书馆将:验证令牌的结构验证签名密钥与密钥使用的算法匹配验证过期以及令牌尚未过期这就是使用库的好处:)下面是调用它的方法。bounds:=len(event.AuthorizationToken)token:=event.AuthorizationToken[7:bounds]parsedToken,err:=jwt.Parse([]byte(token),jwt.WithKeySet(keySet),jwt.WithValidate(true),)如果上述任何一项失败,的输出jwt.Parse将返回一个。error这意味着在这种情况下,您可以发出拒绝。像这样:returnevents.APIGatewayCustomAuthorizerResponse{PrincipalID:"",PolicyDocument:events.APIGatewayCustomAuthorizerPolicy{Version:"2012-10-17",Statement:[]events.IAMPolicyStatement{{Action:[]string{"execute-api:Invoke"},Effect:"Deny",//HereistherejectionResource:[]string{"*"},},},},UsageIdentifierKey:"",},nil请注意,我没有返回错误。这只是拒绝访问。403响应不是错误,那么为什么要返回一个呢?在一切正常的情况下,只需返回许可即可。returnevents.APIGatewayCustomAuthorizerResponse{PrincipalID:"",PolicyDocument:events.APIGatewayCustomAuthorizerPolicy{Version:"2012-10-17",Statement:[]events.IAMPolicyStatement{{Action:[]string{"execute-api:Invoke"},Effect:"Allow",//ReturnAllowResource:[]string{"*"},},},},Context:DumpClaims(parsedToken),UsageIdentifierKey:"",},nil我还想强调该DumpClaims功能。那有什么作用?Lambda授权器的一项很酷的事情是,您可以将作为“上下文”发送的内容扩展到下游方。如果您想将令牌的一部分带到预定的目的地怎么办?该请求将向JWT发送公开的详细信息,但不会传递私人声明或您扩展的内容。也许是客户ID?也许是一些角色?funcDumpClaims(tokenjwt.Token)map[string]interface{}{m:=make(map[string]interface{})m["customKey"]="SomeValueHere"returnm}对于本文,很简单,我只是customKey在上下文中添加一个。我将很快向您展示它是如何出现的。CDK受保护的资源使用Golang构建自定义API网关授权器的乐趣已经结束了一半。那只是意味着另一半即将开始!现在我们已经有了授权者,我们该怎么办?当然,在它后面放一个受保护的资源!constructor(scope:Construct,id:string,func:IFunction){super(scope,id);constauthorizer=newTokenAuthorizer(this,"TokenAuthorizer",{authorizerName:"BearTokenAuthorizer",handler:func,});this._api=newRestApi(this,"RestApi",{description:"SampleAPI",restApiName:"SampleAPI",deployOptions:{stageName:`main`,},defaultMethodOptions:{authorizer:authorizer,},});}那就是API网关CDK代码。请注意,defaultMethodOptions我正在添加一个“授权者”。这只是一个IFunction.这又可能是一个导入,或者在我们的例子中,它是我们刚刚构建的Authorizer。现在有了API,我们可以创建资源。constructor(scope:Construct,id:string,api:RestApi){super(scope,id);this._func=newGoFunction(this,`ProtectedResource`,{entry:path.join(__dirname,`../../../src/protected-resource`),functionName:`protected-resource-func`,timeout:Duration.seconds(30),});api.root.addMethod("GET",newLambdaIntegration(this._func,{proxy:true,}));}对于我们的示例,我正在使用Lambda代理集成并将其定义在“根”级别。所以我们可以期待“/”路径上的GET请求。这个端点的实际处理程序也是一个简单的演示。funchandler(ctxcontext.Context,eventevents.APIGatewayProxyRequest)(*events.APIGatewayProxyResponse,error){success:=&Response{Message:"Congrats!APayload",CustomKey:event.RequestContext.Authorizer["customKey"].(string),}b,_:=json.Marshal(success)return&events.APIGatewayProxyResponse{Body:string(b),StatusCode:200,Headers:map[string]string{"Content-Type":"application/json",},},nil}customKey注意和的使用event.RequestContext.Authorizer["customKey"].(string)。这个event.RequestContext.Authorizer包含一个`map[string]interface{},您可以利用它来发挥自己的优势。用例是无穷无尽的,但我经常将它用于我扩展的客户详细信息和用户角色以及个人资料数据。把它们放在一起让我们将自定义API网关授权方的输出与Golang放在一起。为此,这里是一起测试这一切的场景。第一件事在引导帐户中:basecdkdeploy创建已知用户部署基础架构后,您应该拥有2个lamda授权人保护资源API网关附有Authoirzer的ProtectedResource的一个端点授权人部署阶段一个已知的用户池这是您的UserPool的外观
用户池.注意用户池ID(出于某些原因我已经清除了我的ID)。您需要复制该ID,因为稍后会很重要。
现在的客户名单客户名单该表中的ClientID也很重要。同样,我的已经清理干净了,但请注意你的。最后,创建一个用户并将其标记为已验证。
创建用户记下他们的密码,因为我们将在一分钟内使用密码流程登录游览API网关对于我们的主要受保护资源,这是它的创建方式
受保护的资源Authorization字段指向我们在本文开头定义的BearerTokenAuthorizer。然后在API网关上定义授权者。请记住,如果您使用本文中定义的基本路径映射并共享授权方,则需要为每个API网关附加它。
授权方API网关执行请求我们终于准备好运行这个东西了。但首先,让我们获得一个令牌。还记得我说过要捕获UserPool中的ClientID吗?现在是时候把它拿出来了。获取令牌请求
这个的输出将是你的三个令牌。访问令牌身份证刷新令牌请随意在下一个请求中使用ID或Access。发出请求很简单。失败请求首先,让我们看看BadToken会发生什么
错误的请求您在CloudWatch中的日志应如下所示失败Cloudwatch
请求成功现在为了成功!
您在CloudWatch中的日志应如下所示失败Cloudwatch
你做到了!使用示例事件在本地测试如果我不包括您也可以对授权方进行一些本地测试,那将是我的疏忽。这可以通过两种方式发生一些单元测试使用测试事件文件运行本地文件如果您在此堆栈上本地执行,您将在目录中cdksynth得到一个。您可以像这样运行存储库中包含的测试文件MainStack.template.jsoncdk.outbashsamlocalinvokeAuthorizerFunc-tcdk.out/MainStack.template.json--eventsrc/authorizer/test-events/e-1.json--env-varsenvironment.json--skip-pull-image总结那是一篇包含很多细节的长文章,但在使用无服务器技术构建安全且可扩展的API时,这种模式非常有用。通过使用Golang添加自定义API网关授权器,您可以在堆栈的高层捕获此授权逻辑,从而节省下游资源,使其不必处理此重复代码。此外,但利用事件上下文到您的下游Lambda,您可以使用您可能已自定义的PrivateClaims。
举报/反馈
以上内容为资讯信息快照,由td.fyun.cc爬虫进行采集并收录,本站未对信息做任何修改,信息内容不代表本站立场。
快照生成时间:2023-05-08 19:45:42
本站信息快照查询为非营利公共服务,如有侵权请联系我们进行删除。
信息原文地址: