作者:Lee Brandt

翻译:疯狂的技术宅

原文:https://scotch.io/tutorials/b...

未经容许严禁转载

Node.js 的创建者 Ryan Dahl 创立了一个用于设计 Web 应用程序的新框架。他回过头来,利用在最后编写 Node 时还不可用的新技术,纠正了预先发现的一些谬误。这就是 Deno(发音为 DEH-no),一个用 TypeScript 编写的 “相似 Node 的” Web 利用的框架。在本文中,我将疏导你创立一个带有身份验证的根本 Web 利用。

要点

  • 创立你的 Deno 利用
  • 用 Deno 构建实在的 Web 利用
  • 为你的 Deno 利用增加性能
  • 用 Okta 增加身份验证
  • 运行 Deno 程序

你简直能够在 Deno 网站上找到所需的所有信息,以及无关以后可用于 Deno 的所有第三方库的信息。以后框架最大的毛病应该是:它只是在 2020 年 5 月 13 日发行了1.0版,因而即便有很多根本库,也没有 Node 的库那么多。不过对于那些精通 Node 的人,向 Deno 过渡应该很容易。

你能够在 https://deno.land/#installation 中找到装置阐明。

创立你的 Deno 利用

我找不到任何根本的脚手架库,所以只能从一个空文件夹开始。在程序的根文件夹中,创立一个名为 index.ts 的文件,这将作为你 Deno 程序的终点。咱们将会应用 Opine,它是 Deno 的 Express 克隆版本,可简化构建和路由。

与 Deno 不同的是,没有用于引入第三方库的包管理器。你能够通过应用库的残缺 URL 来实现此操作。在 index.ts 文件顶部执行此操作,而后设置一个根本的 Web 应用程序。

import { opine } from 'https://deno.land/x/opine@0.12.0/mod.ts';const app = opine();app.get('/', (req, res) => {  res.send('Deno Sample');});app.listen(3000);console.log('running on port 3000');

而后,在终端下切换到程序文件夹中,并输出以下内容来运行这个十分根本的程序:

deno run -A index.ts

-A 是用于开发目标的快捷选项。在默认状况下,Deno 齐全处于锁定状态,所以须要把参数传递给 run 命令以容许拜访,例如 --allow-net 容许联网, --allow-read 容许程序从文件系统读取。这里的 -A 容许所有内容,从而无效地禁用了所有安全性。当你运行这个程序而后转到 http://localhost:3000 时,空白页上将会呈现 Deno Sample 字样。

用 Deno 构建实在的 Web 利用

尽管这是一个良好的开始,但并没有太大用处。你还须要增加一些“实在”的性能,这些性能比“真实世界”要多一些,接下来批改 index.ts 文件,使其内容为:

import { opine, serveStatic } from 'https://deno.land/x/opine@0.12.0/mod.ts';import { renderFileToString } from 'https://deno.land/x/dejs@0.7.0/mod.ts';import { join, dirname } from 'https://deno.land/x/opine@main/deps.ts';import { ensureAuthenticated } from './middleware/authmiddleware.ts';import users from './controllers/usercontroller.ts';import auth from './controllers/authcontroller.ts';const app = opine();const __dirname = dirname(import.meta.url);app.engine('.html', renderFileToString);app.use(serveStatic(join(__dirname, 'public')));app.set('view engine', 'html');app.get('/', (req, res) => {  res.render('index', { title: 'Deno Sample' });});app.use('/users', ensureAuthenticated, users);app.use('/auth', auth)app.listen(3000);console.log('running on port 3000');

你会留神到很多的 import 语句,这些语句引入了一些第三方库。在这里,我用的是 dejs,这是 Deno 的 EJS 端口。我还引入了 Opine 库中的一些用于解决目录名称的类。我在前面将会介绍本地导入的这三个文件。当初你只须要晓得导入了它们。

opine() 实例化上面的代码行创立对本地目录的援用。上面的三行代码将视图引擎设置为 DEJS,用来解决相似 HTML 的文件,这很像 EJS 对 Node 的解决形式。下一部分已稍作更改以渲染这些 HTML 模板文件,并且最初两行代码引入了一些内部路由。须要留神的一件事是 /users 路由具备 ensureAuthenticated() 中间件性能。这将迫使用户先登录,而后能力拜访该页面。

为你的 Deno 利用增加性能

接下来创立一些在下面代码所缺失的局部。从路由开始。在程序的根目录中创立一个名为 controllers 的文件夹。而后在该文件夹内增加一个 usercontroller.ts 文件,内容如下:

import { Router } from 'https://deno.land/x/opine@0.12.0/mod.ts';const users = new Router();// users routesusers.get('/me', (req, res) => {  res.render('users/me', { title: 'My Profile', user: res.app.locals.user });});export default users;

这是一个简略的路由文件。它从 Opine 获取路由,并创立一个新实例来挂起路由。而后有代码为 /me 增加路由以在 users/me 中渲染 HTML 视图。render() 调用还将题目和登录用户传递到页面。该页面将受到爱护,以便始终有用户能够拜访。

接下来,创立一些点击路由时可能显示的视图。在根文件夹中,增加一个 views 文件夹。在其中创立一个 shared 文件夹和一个 users 文件夹。在 shared 文件夹中,创立一个 header.htmlfooter.html 文件。在 users 文件夹中增加 me.html 文件。最初,在 views 文件夹自身中创立一个 index.html 文件。

这些是非常简单的办法,然而它演示了如何创立可被其余视图重用的视图。在 shared/header.html 文件中增加以下内容:

<!DOCTYPE html><html lang="en"><head>  <meta charset="utf-8">  <meta name="viewport" content="width=device-width,initial-scale=1">  <title><%= title %></title></head><body>  

这将输入 HTML 页面的顶部,并将题目注入页面。接下来,将以下内容增加到 shared/footer.html 文件中:

</body></html>

当初你能够在 index.html 文件中应用这些局部变量:

<%- await include('views/shared/header.html', { title }); %><a href="/users/me">My Profile</a><%- await include('views/shared/footer.html'); %>

这包含页脚和页眉局部的内容,并向个人资料页面增加了链接。 users/me.html 文件的内容是:

<%- await include('views/shared/header.html', { title }); %><h1>My Profile</h1><ul><% for(var p in user){ %>  <li><strong><%= p %>: </strong><%= user[p] %></li><% } %></ul><%- await include('views/shared/footer.html'); %>

同样,此页面蕴含页眉和页脚,并循环遍历 user 对象的属性。当然这不是一个丑陋的个人资料页面,然而它可能使你晓得身份验证步骤是否全副无效。

用 Okta 增加身份验证

如果你还没有Okta帐户,能够在此处取得收费的开发人员帐户。登录 Okta 后进入仪表板。你须要创立一个 Okta 利用,以利用 Okta 作为我的项目的身份提供者。

单击菜单中的 Applications,而后单击 Add Application。这将带你进入应用程序向导。抉择 Web 作为你的平台,而后单击 Next。下一页是 Application Settings 页面。为你的应用程序命名(我命名为 DenoExample)。将所有 URL 更改为应用端口 3000 而不是 8080,而后将 Login Redirect URIs 更改为 http://localhost:3000/auth/callback。最初,单击 Done 在 Okta 中创立应用程序。

进入新创建的应用程序页面后,确保你位于 General Settings 选项卡上并滚动到底部,直到看到 Client Credentials 局部。咱们先临时应用这些值,所以不要敞开这个窗口。

回到你的应用程序中,在程序的根目录中创立一个名为 .env 的新文件。该文件的内容将是:

issuer=https://{yourOktaOrgUrl}/oauth2/defaultclientId={yourClientID}clientSecret={yourClientSecret}redirectUrl=http://localhost:3000/auth/callbackstate=SuPeR-lOnG-sEcReT

从 Okta 应用程序的 Client Credentials 局部复制客户端 ID 和客户端密钥。而后返回到信息中心,从菜单下方的右侧复制你的 Okta org URL。

当初你能够开始用 Okta 进行身份验证了。可怜的是你必须手动创立它。不过这是一个很棒的练习,能够帮忙你理解 OAuth 和 OIDC 的工作形式。在程序的根文件夹中,创立一个名为 middleware 的新文件夹,并增加一个名为 authmiddleware.ts 的文件。而后增加以下内容:

import { config } from 'https://deno.land/x/dotenv/mod.ts';export const ensureAuthenticated = async (req:any, res:any, next:any) => {  const user = req.app.locals.user;  if(!user){    const reqUrl = req.originalUrl;    const {issuer, clientId, redirectUrl, state} = config();    const authUrl = `${issuer}/v1/authorize?client_id=${clientId}&response_type=code&scope=openid%20email%20profile&redirect_uri=${encodeURIComponent(redirectUrl)}&state=${state}:${reqUrl}`;    res.location(authUrl).sendStatus(302);  }  next();}

首先,导入一个用于读取 .env 文件的库 dotenv。而后实现 ensureAuthenticated() 中间件,该中间件将启动身份验证过程的第一步。它首先检用户是否登录。如果已登录,则它只调用 next(),因为无事可做。

如果没有以后登录的用户,它将从 .env 文件构建一个由 Issuer,clientId,redirectUrl 和 state 属性组成的 URL。它调用发行者 URL 的 /v1/authorize 端点。而后重定向到该 URL。这是 Okta 托管的登录页面。有点像当你重定向到 Google 并用其作为身份提供者登录的机制。登录实现后将要调用的 URL 是 .env 文件中的 URL http://localhost:3000/auth/callback 。我还标记了用户重定向到 state 查问参数时要应用的原始 URL。一旦他们登录,这将会很容易把他们间接疏导回去。

接下来,你将须要实现 auth/callback 路由来解决登录页面的后果,并替换将从 Okta 收到的受权代码。在 controllers 文件夹中创立一个名为 authcontroller.ts 的文件,其内容如下:

import { Router } from 'https://deno.land/x/opine@0.12.0/mod.ts';import { config } from "https://deno.land/x/dotenv/mod.ts";const auth = new Router();// users routesauth.get('/callback', async (req, res) => { const { issuer, clientId, clientSecret, redirectUrl, state } = config(); if (req.query.state.split(':')[0] !== state) {   res.send('State code does not match.').sendStatus(400); } const tokenUrl: string = `${issuer}/v1/token`; const code: string = req.query.code; const headers = new Headers(); headers.append('Accept', 'application/json'); headers.append('Authorization', `Basic ${btoa(clientId + ':' + clientSecret)}`); headers.append('Content-Type', 'application/x-www-form-urlencoded'); const response = await fetch(tokenUrl, {   method: 'POST',   headers: headers,   body: `grant_type=authorization_code&redirect_uri=${encodeURIComponent(redirectUrl)}&code=${code}` }); const data = await response.json(); if (response.status !== 200) {   res.send(data); } const user = parseJwt(data.id_token); req.app.locals.user = user; req.app.locals.isAuthenticated = true; res.location(req.query.state.split(':')_[_1] || '/').sendStatus(302);});function parseJwt (token:string) {  const base64Url = token.split('.')_[_1];  const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');  const jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {      return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);  }).join(''));  return JSON.parse(jsonPayload);};export default auth;

实际上,这里产生的事比你设想的要少得多。首先从 Opine 引入 Router,而后再次读取 .env 文件。接着他们像在 usercontroller.ts 文件中一样实例化路由器。接下来是解构 config 对象,可能更易于应用它的值。接下来,我查看了状态查问参数以确保其匹配。这有助于确保 Okta 是发送受权码的人。而后用 req.query.code 从查问字符串中提取受权码。

接下来是对 token 端点的调用。你将在 POST 申请中将受权码发送给 Okta,以替换 ID Token。因而,这里我为申请构建了一些标头。最重要的是 Authorization 标头,其值为 Basic {yourClientId}:{yourClientSecret},客户端 ID 和明码是 base64 编码的。而后,应用这些标头和带有 authorization_codegrant_type(与以前雷同的重定向 URL)的主体,以及带有我刚从 Okta 收到的受权代码的 Token 端点,对 Token 端点进行 POST 调用。

fetch() 调用返回一个用 then() 函数解析的 promise。我失去 response 对象的JSON值,为了确保调用胜利,用上面的 parseJwt() 函数解析 id_token 值并将其粘贴到名为 user 的局部变量中。最初在重定向到身份验证之前,将用户发送到他们最后申请的 URL。

运行 Deno 程序

当初用以下命令从终端再次运行该程序:

deno run -A index.ts

一旦运行,你将可能单击主页上的配置文件链接,并将其重定向到 Okta 的托管登录页面。登录后,将会间接回到个人资料页面,你会看到 ID Token 的属性显示在列表中。


本文首发微信公众号:前端先锋

欢送扫描二维码关注公众号,每天都给你推送陈腐的前端技术文章

欢送持续浏览本专栏其它高赞文章:

  • 深刻了解Shadow DOM v1
  • 一步步教你用 WebVR 实现虚拟现实游戏
  • 13个帮你进步开发效率的古代CSS框架
  • 疾速上手BootstrapVue
  • JavaScript引擎是如何工作的?从调用栈到Promise你须要晓得的所有
  • WebSocket实战:在 Node 和 React 之间进行实时通信
  • 对于 Git 的 20 个面试题
  • 深刻解析 Node.js 的 console.log
  • Node.js 到底是什么?
  • 30分钟用Node.js构建一个API服务器
  • Javascript的对象拷贝
  • 程序员30岁前月薪达不到30K,该何去何从
  • 14个最好的 JavaScript 数据可视化库
  • 8 个给前端的顶级 VS Code 扩大插件
  • Node.js 多线程齐全指南
  • 把HTML转成PDF的4个计划及实现

  • 更多文章...