资讯专栏INFORMATION COLUMN

使用 Authing + Lambda 替代 AWS Cognito

Vultr / 811人阅读

摘要:编写函数编写函数推荐使用这个,控制台中的函数编写堪称让人痛不欲生。在控制台中安装在中引入包后会一起打包上传到运行时中。

Amazon Web Services(AWS) 虽然作为市场份额全球第一的云计算厂商,其产品也不是完美无缺的,Cognito (AWS 的身份认证解决方案)及其附带的中文文档就是一个反面教材,其难用程度令人发指。当然,除了不易用之外,还有访问速度缓慢,不适用于中国市场等问题存在。

而国产的 Authing 可以解决使用 Cognito 的诸多问题,先看一下 Authing 的介绍:

Authing 是一个身份认证服务商,其提供了企业级身份认证和管理解决方案,客户分布教育、IoT、互联网和电商等多个行业。

Lambda 是一个由 AWS 提供的 Function-as-a-Service (FaaS) 平台 。Lambda 和 AWS 生态结合的非常紧密,接入 Lambda 后,开发者可以使用 AWS 生态内的所有资源。比如,我们可以创建一个 Lambda 函数,让用户通过 Cognito 登录(当然这篇文章是让用户使用 Authing 登录),然后再调用另外一个可以上传文件到 S3(AWS 的存储服务) 的 Lambda 函数。

这类平台(现在多被称为 Serverless,无服务器架构)的一个好处是可以让开发者无需担心基础设施,专心业务研发。

FaaS 或者说 Serverless 平台正在逐渐获得市场关注,因为这种类型的平台可以让开发者不用再关注基础设施。"What is serverless" 这篇文章详细的讲解了什么是「无服务器计算」和「无服务器计算」的好处,推荐读一下。

这篇文章的主要目的是介绍如何使用 Authing + Lambda 替代 AWS Cogito,点击这里体验最终 demo。

此外,Authing 遵循 OIDC 规范,所以本篇文章将使用 OIDC 来做认证,如果你还不了解什么是 OIDC,请查看这篇文章。

首先确认下用户的操作流程:

打开页面:https://sample.authing.cn/aws/;

点击 Login 进行登录,此时跳转到 Authing 的登录页面(应用的二级域名);

输入账号密码进行登录,若没有账号密码请先进行注册;

登录成功后返回第一步打开的页面,并显示登录用户的头像;

此时用户可以看到从 AWS Lambda 请求回来的 Private 信息;

最终效果如下图所示:

点击这里体验 DEMO

创建一个 Authing 应用

如果你还没有注册 Authing,那么请点击这里进行注册,注册完成后,按以下步骤创建一个 Authing 应用。

创建应用

填写基本信息,应用类型选择 Web 应用

创建完成后会进入到应用主页(空空如也)

创建 OIDC 应用

创建完应用后相当于你有了一个用户池,接下来你可以创建 OIDC 应用来授权其他程序(你自己写的或其他第三方程序)访问你的用户池。

如果你还不清楚什么是 OIDC,请参考这篇文章。

点击「第三方登录」开始创建 OIDC 应用

选择「OIDC 应用」选项卡,并点击「创建 OIDC 应用」

填写应用名和认证地址,并勾选 id_token token

这里要说明一下,创建 OIDC 应用时的认证地址将由 Authing 生成一个二级域名(支持 HTTPS),且不能重复,回调 URL 填写你自己的回调地址即可,在这里我用的是 https://authing.cn,注意,OIDC 协议中不允许回调 URL 为 localhost,请使用代理工具进行调试。

点击确认,就可以看到我们有了第一个基于 OIDC 协议的授权应用

创建完成后你可以访问 lambda.authing.cn ,此时会看到报了一个错,别害怕,这是因为我们发起的授权链接不正确。

发起正确授权请求的方式请继续往下看。

发起授权请求

和绝大多数的 OAuth 应用差不多,OIDC 的授权链接也需要拼接(如果你开发过微信应用,应该会很容易理解),Authing OIDC 应用的授权链接符合标准规范,具体格式为:

https://lambda.authing.cn/oau...;应用 ID>&redirect_uri=<回调 URL,必须和平台配置完全一样>&scope=openid profile&response_type=&state=<一个随机字符串,用来防范 CSRF 攻击>

若需要查看详细的参数,请点击这里查看。

例如:

https://lambda.authing.cn/oau...://authing.cn&scope=openid profile&response_type=id_token token&state=jacket

为了简单起见,这里我们的 response_type 设置为「id_token token」,这样不需要使用「code」换取 token,token 会直接附带到回调地址中。

如果你的授权链接正确,应该可以看到上图这样的登录窗口,同时这个窗口也是你的终端用户所使用的窗口,他们都将从这里登录然后回调到你配置好的回调 URL 中。

你可以试着注册一个账号然后进行登录,登录完成后可以在控制台中观察到登录状况。

注册成功

登录之后的授权页面

控制台中观察到的用户数据

在你登录成功后应该会看到回调到了你填写 URL 中,并且附带了很多参数,接下来我们会阐述如何使用这些参数。

获取用户信息
回调到在控制台中配置的 redirect_uri 中后,将附带以下信息:

{
    "id_token": "JWT_TOKEN",
    "access_token": "JWT_TOKEN",
    "expires_in": "3600",
    "token_type": "Bearer",
    "state": "jacket",
    "session_state": "644d7b324ba61d517fdedd28b5b6e365d78f2a8178f2ee742474d5b57a99eb3f"
}

可以看到其中包含了 access_token 和 id_token,其中 access_token 可以帮助你从 Authing 后端获取用户信息,而 id_token 中包含了基本的信息,如果你要获取用户的头像,那么是需要通过 access_token 获取的。

我们先看一个 id_token 的例子:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJzdWIiOiI1Y2MyYTg1MTFiYmFmMDRmOTNjZTQ4OWYiLCJub25jZSI6IjE4MzEyODkiLCJzaWQiOiI5MzkwZDA1ZC01ZTM3LTQ3ZWUtODJjNi1jNTQ1ZjA2ODhhMDAiLCJhdF9oYXNoIjoiNmxZMGRXajZYUTY0aExWdHAtR2tEdyIsInNfaGFzaCI6IlZVOU5QYV9JQ0VTSEdxRmxUZ3A2LUEiLCJhdWQiOiI1Y2MyYjU0OGQxNGM3NDJkYjg5M2JhNTUiLCJleHAiOjE1NTYzNjY0ODksImlhdCI6MTU1NjM2Mjg4OSwiaXNzIjoiaHR0cHM6Ly9vYXV0aC5hdXRoaW5nLmNuL29hdXRoL29pZGMifQ.Qc_OMqMf6_wwzW2SsEgEtiaGr3ZY1FWHnRrMU2M7LADGlNpq_pvPrFxAVsR2j-BFr1y48M-Trvq6yAu4_ZOUBHPtIIpoQ5W2bnABytUV693ZcwNlf9CCiLc-k0LG3o1U-BmiH3L6NAV7aKGsfVHS8toiNbVDuimPVdYJsRrF2C1jj1meM1K8FBVwqozXm6YtB--u3sqY4IszHnd5PMEWguLsOkpZJIh7xWeYPpVQ5WKfx0cA8rB_T2puSCbeaUVhgIwNADy06qBqXhUOiA4gdcNbHtx7tvGZMxzMC3rdjpXoZk89Duh3O5tHlMtaBlidJGYavUSjVl7potESecSlBg

使用 jwt.io 解析后将得到如下结果:

{
  "sub": "5cc2a8511bbaf04f93ce489f",
  "nonce": "1831289",
  "sid": "9390d05d-5e37-47ee-82c6-c545f0688a00",
  "at_hash": "6lY0dWj6XQ64hLVtp-GkDw",
  "s_hash": "VU9NPa_ICESHGqFlTgp6-A",
  "aud": "5cc2b548d14c742db893ba55",
  "exp": 1556366489,
  "iat": 1556362889,
  "iss": "https://oauth.authing.cn/oauth/oidc"
}

其中包含了签发时间(iat)、过期时间(exp)等字段,可以用来判断用户有没有被认证过,在 OIDC 的规范中,JWT 使用 OIDC 应用的 secret 签发,需要开发者在后端验证(这一步我们将会在 Lambda 中执行)后继续执行开发者本身的业务流程。

再来看看 access_token 的例子:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQyODE3S3gwdmJpNmlfS2MifQ.eyJqdGkiOiJza0p-bTNaYmZsTjVxVGEzR2J2YlMiLCJzdWIiOiI1Y2MyYTg1MTFiYmFmMDRmOTNjZTQ4OWYiLCJpc3MiOiJodHRwczovL29hdXRoLmF1dGhpbmcuY24vb2F1dGgvb2lkYyIsImlhdCI6MTU1NjM2Mjg4OSwiZXhwIjoxNTU2MzY2NDg5LCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIiwiYXVkIjoiNWNjMmI1NDhkMTRjNzQyZGI4OTNiYTU1In0.Uf3YK4D9HL-G71hkA4cWt5kitDo5rNgwVA9Vqlv4RjAILNDTylYWtkacKJpLcOSS81ivaNpDVNYYzBSoyN-eMH80VhArPUre74F9SHdonA-IVFVPT0DHRtOAJI9kqDW4tgTXhZeZMUm-MCjVjR-q8XrayXaqrC5Hu5W3D1N-K_jZOlwxzIBf51nuC4NMvSI_wPpYj2WPzGxFwpfTCEbnhj5RO0CcThRpC3EdmpbtcJqStd7AZQhkLyTb1TQLHJOel8DSxLnLnoIU0rZXsodK6EjE_oqRLagetNXF1cKfRmnGFaAKZKqgvHc527S_CVkgXIwcHBRmDeqo93CCId_hmQ

使用 jwt.io 解析后将得到如下结果:

{
  "jti": "skJ~m3ZbflN5qTa3GbvbS",
  "sub": "5cc2a8511bbaf04f93ce489f",
  "iss": "https://oauth.authing.cn/oauth/oidc",
  "iat": 1556362889,
  "exp": 1556366489,
  "scope": "openid profile",
  "aud": "5cc2b548d14c742db893ba55"
}

可以看到 access_token 相比 id_token 是少了很多信息的,这里有一段英文的介绍,该介绍讲解了 access_token 和 id_token 的区别:

ID Tokens vs Access Tokens. The ID Token is a security token granted by the OpenID Provider that contains information about an End-User. This information tells your client application that the user is authenticated, and can also give you information like their username or locale.You can pass an ID Token around different components of your client, and these components can use the ID Token to confirm that the user is authenticated and also to retrieve information about them.Access tokens, on the other hand, are not intended to carry information about the user. They simply allow access to certain defined server resources. More discussion about when to use access tokens can be found in Validating Access Tokens.

简单来讲,id_token 告诉你用户被验证过了,而 access_token 是一个你可以访问资源服务器(这里就是 Authing) 的一个凭证。

同时也可以看到,idtoken 包含的信息较少,如果想获取更多信息,需要使用 access_token 来获取。获取方式也非常简单,只需要往以下链接发送 GET 请求并且附带 access_token 即可,如:

$ curl https://users.authing.cn/oauth/oidc/user/userinfo?access_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InIxTGtiQm8zOTI1UmIyWkZGckt5VTNNVmV4OVQy...balabala...verylong...

可以获取到 id 等信息,获取到 id 之后你可以将 id 存储到你自己的数据库中以完成自己的实际业务。

{
    "sub":"5cc2a8511bbaf04f93ce489f",
    "nickname":"",
    "picture":"https://usercontents.authing.cn/authing-avatar.png"
}

上面的 JSON 是一个使用 access_token 换取用户数据后的返回结果。

好了,现在我们已经获取到 Token 了,接下来我们需要在 Lambda 中验证这个 Token 的合法性并在前端显示不同的信息。

编写 Lambda 函数

编写 Lambda 函数推荐使用 Serverless 这个 CLI,AWS 控制台中的函数编写堪称让人痛不欲生。

同时,你可以到这里查看完整代码。

Lambda 在这篇文章中主要用来做三件事:

对 id_token 进行认证,以获取用户是否被认证过;

提供一个 Public API,此 API 可以直接被访问;

提供一个 Private API,此 API 需要经过认证后被访问;

对 id_token 进行认证

认证 id_token 首先需要知道 OIDC 应用的 secret,此值可以在 Authing 控制台查看 OIDC 应用的详情中找到:

请务必保管好此值,避免向任何人泄漏

id_token 在签发时的签名是此 secret ,因此在 JavaScript 中可以直接使用 jsonwebtoken 这个库来验证 id_token 的合法性(详情请参考:验证 Token 合法性)。

在控制台中安装 jsonwebtoken:

$ npm install jsonwebtoken --save

P.S. 在 lambda 中引入包后会一起打包上传到 AWS Lambda 运行时中。

const jwt = require("jsonwebtoken");

// Policy helper function 
// 这是 AWS 提供的模版代码,这里不需要做修改
const generatePolicy = (principalId, effect, resource) => {
  const authResponse = {};
  authResponse.principalId = principalId;
  if (effect && resource) {
    const policyDocument = {};
    policyDocument.Version = "2012-10-17";
    policyDocument.Statement = [];
    const statementOne = {};
    statementOne.Action = "execute-api:Invoke";
    statementOne.Effect = effect;
    statementOne.Resource = resource;
    policyDocument.Statement[0] = statementOne;
    authResponse.policyDocument = policyDocument;
  }
  return authResponse;
};

// Reusable Authorizer function, set on `authorizer` field in serverless.yml
module.exports.auth = async (event, context, cb) => {
  if (event.authorizationToken) {
    // remove "bearer " from token
    const token = event.authorizationToken.substring(7);

    try {
        let decoded = jwt.verify(token, "YOUR_OIDC_APP_SECRET"),
          expired = (Date.parse(new Date()) / 1000) > decoded.exp;
        if (expired) {
          cb("Unauthorized, Login information has expired.");
        }else {
          cb(null, generatePolicy("user", "Allow", event.methodArn));
        }
      } catch (error) {
        cb("Unauthorized");
      }
  } else {
    cb("Unauthorized");
  }
};
公共 API
// Public API
module.exports.publicEndpoint = (event, context, cb) => {
  cb(null, { message: "Welcome to our Public API!" });
};
私有 API
// Private API
module.exports.privateEndpoint = (event, context, cb) => {
  cb(null, { message: "Only logged in users can see this" });
};
serverless.yml
service: serverless-authorizer

provider: 
 name: aws
 runtime: nodejs8.10

functions:
  auth:
    handler: handler.auth
  getUserInfo:
    handler: handler.getUserInfo
    events:
      - http:
          path: api/userInfo
          method: get
          integration: lambda
          cors: true    
  publicEndpoint:
    handler: handler.publicEndpoint
    events:
      - http:
          path: api/public
          method: get
          integration: lambda
          cors: true
  privateEndpoint:
    handler: handler.privateEndpoint
    events:
      - http:
          path: api/private
          method: get
          integration: lambda
          authorizer: auth # See custom authorizer docs here: http://bit.ly/2gXw9pO
          cors: true

此文件可用来配置需要鉴权的路由,如上面代码中的 privateEndpoint,配置了 authorizer 为 auth 函数。

点击此处查看完整代码。

测试 Lambda

写完了代码之后我们需要进行测试。

Lambda 支持直接在本地测试,可以使用如下命令:

$ sls invoke local -f auth --data "{"authorizationToken": "Bearer "}"

如果本地测试返回了如下信息则表示验证成功:

{
    "principalId": "user"
}
部署 Lambda
$ serverless deploy

部署完成后会得到三个链接,这三个链接分别是上述代码的三个函数。

红框中的路由是在 serverless.yml 中定义好的,可以直接映射到函数中。

使用 curl 或 postman 将 OIDC 登录后的 id_token 携带到 header 的 Authorization 中即可查看结果,如:

$ curl --header "Authorization: " 

上述三个路由的结果应该为:

curl  - Should work! Public!
curl  - Should not work
curl --header "Authorization: "  - Should work! Authorized!

最后,在我们的前端补充上相关信息,在点击登录后应该可以看到如下信息:

线上体验地址:https://sample.authing.cn/aws/

Enjoy!
Authing.cn - 领先的身份认证云

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/104010.html

相关文章

  • IDaaS | 使用 Authing + Lambda 轻松替代 AWS Cognito

    摘要:你可以试着注册一个账号然后进行登录,登录完成后可以在控制台中观察到登录状况。编写函数编写函数推荐使用这个,控制台中的函数编写堪称让人痛不欲生。在控制台中安装在中引入包后会一起打包上传到运行时中。 showImg(https://segmentfault.com/img/remote/1460000020183336?w=800&h=422); Amazon Web Services(A...

    DangoSky 评论0 收藏0
  • AWS-Lambda 使用入门

    摘要:例如,控制台使用调用类型,因此当您使用控制台调用函数时,控制台将显示返回的值。如果别名用于调用函数,将为别名指向的版本。 场景:现在需要开发一个前后端分离的应用,后端采用 RESTful API 最为方便,但是如果这个后端服务会在一天中的某些时候有高并发的情况,使用什么样的架构最为简单呢? 刚思考这个问题的时候我想到的解决方案可能有以下几种: 使用CDN内容分发网络,减少主服务器的...

    RobinQu 评论0 收藏0
  • 【译】Serverless架构 - 1

    摘要:在这个思想指导下,并且将大量行为移到前端,此类架构移除了对传统部署在应用后永远在线的服务系统。在这种情况下,这种系统可以明显减少运维成本和复杂度,代价是对厂商的依赖和目前不太成熟的支持服务。可以认为这就是。是目前最流行的实现,但还有其他的。 原文: https://martinfowler.com/arti... Serverless架构特指那些依赖第三方服务(即后端即服务或者是Baa...

    ysl_unh 评论0 收藏0
  • 无服务器架构(Serverless Architectures)(上)

    摘要:原文作者无服务器架构是指一个应用大量依赖第三方服务后端即服务,,简称,或者把代码交由托管的短生命周期的容器中执行函数即服务,,简称。这些服务最早被称为,下文将对此简称为。是目前的热门实现之一,下文将对此简称为。它同样经由网关暴露给外部使用。 译注: 为了便于对照参考,Serverless、BaaS 等术语文中不做翻译。 原文很长,这里分成上下两篇。翻译过程在 GitHub 上进行。...

    ConardLi 评论0 收藏0

发表评论

0条评论

Vultr

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<