构建和部署一个Node.js应用程序到Heroku教程

158 阅读9分钟

Heroku是一个平台即服务(PaaS),支持多种语言。 最初,它只支持Ruby网站,但现在支持各种语言,包括Node.js的JavaScript。 Heroku还支持Docker,因此你可以将几乎所有的东西部署到它上面。

本教程将教你如何使用Node.js的Express框架建立一个小型应用程序。 然后,你将通过将Okta OIDC中间件与你的应用程序集成,使用Okta来保护该应用程序。 最后,你将学习如何将应用程序部署到Heroku,为你的Heroku网站更新你的Okta应用程序,并在Heroku中为你的应用程序设置环境变量。

本教程使用了以下技术,但不需要任何先前的经验:

当然,如果你只想看看代码,可以随时在GitHub上查看

创建你的Okta应用程序

安装Okta CLI并运行okta login 。然后,运行okta apps create 。选择默认的应用程序名称,或者根据你的需要改变它。 选择Web,然后按回车键

选择节点。 然后,将重定向URI改为http://localhost:3000/authorization-code/callback ,并接受默认的注销重定向URI:http://localhost:3000

Okta CLI是做什么的?

Okta CLI将在您的Okta机构中创建一个OIDC网络应用。它将添加您指定的重定向URI,并授予Everyone组的访问权。当它完成后,您会看到如下输出。

Okta application configuration has been written to: /path/to/app/.okta.env

运行cat .okta.env (或Windows上的type .okta.env ),查看你的应用程序的发行者和凭证。

export OKTA_OAUTH2_ISSUER="https://dev-133337.okta.com/oauth2/default"
export OKTA_OAUTH2_CLIENT_ID="0oab8eb55Kb9jdMIr5d6"
export OKTA_OAUTH2_CLIENT_SECRET="NEVER-SHOW-SECRETS"

您的Okta域是发行方的第一部分,在/oauth2/default

注意:您也可以使用Okta管理控制台来创建您的应用程序。更多信息请参见创建一个节点应用程序

创建你的Express应用程序

现在是时候把你的注意力转向编写你的应用程序了。 你将使用express application generator 来构建它。 打开你的应用程序所在的文件夹并运行以下命令。

npx express-generator

安装你的依赖项

运行下面的命令来安装expression-generator的默认依赖项。

npm install

你将需要为你的应用程序安装一些额外的依赖项。

首先,你将添加dotenv ,以存放你的敏感信息和环境特定信息。

npm i dotenv@16.0.0

接下来,你将添加Bootstrap。 你将在你的jade 模板中使用Bootstrap库来编写前端。 Bootstrap是一个简单的UI框架,有许多样本可以帮助你快速开发HTML页面。

npm i bootstrap@5.1.3

你将需要Okta的oidc-middleware ,以帮助保护你的应用程序。 这个软件包使得与Okta的整合不费吹灰之力,快速而安全。 oidc-middleware 你将能够用你的Okta应用程序的细节来配置中间件,只需几行代码就能开始保护你的路由。使用express-session ,所以你也需要安装它。

npm i express-session@1.17.2
npm i @okta/oidc-middleware@4.3.0

编写你的服务器代码

express-generator包在为一个简单的Node.js应用程序提供支架方面做得很好。 在本教程中,你可以让大部分应用程序保持原样。 不过,你需要做一些改动。

打开.okta.env ,在你的Okta配置值之后添加以下内容到文件的结尾。

export APP_BASEURL="http://localhost:3000"

注意: 确保你把.okta.env 添加到你的.gitignore 文件。

接下来,打开app.js ,用以下代码替换该文件的内容。

require('dotenv').config({ path: '.okta.env' })

var createError = require("http-errors");
var express = require("express");
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");

var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");

const session = require("express-session");
const { ExpressOIDC } = require("@okta/oidc-middleware");

var app = express();

// session support is required to use ExpressOIDC
app.use(
  session({
    secret: "this should be secure",
    resave: true,
    saveUninitialized: false,
  })
);

const { OKTA_OAUTH2_ISSUER, OKTA_OAUTH2_CLIENT_ID, OKTA_OAUTH2_CLIENT_SECRET, APP_BASEURL } = process.env;

const oidc = new ExpressOIDC({
  issuer: OKTA_OAUTH2_ISSUER,
  client_id: OKTA_OAUTH2_CLIENT_ID,
  client_secret: OKTA_OAUTH2_CLIENT_SECRET,
  appBaseUrl: APP_BASEURL,
  redirect_uri: `${APP_BASEURL}/authorization-code/callback`,
  scope: "openid profile",
});

// ExpressOIDC attaches handlers for the /login and /authorization-code/callback routes
app.use(oidc.router);

// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "jade");

app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));

app.use(express.static(__dirname + "/node_modules/bootstrap/dist"));

app.use("/", indexRouter);
app.use("/users", usersRouter.userRoutes({ oidc: oidc }));

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get("env") === "development" ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render("error");
});

module.exports = app;

这段代码大部分都是来自Express-generator的模板,值得注意的是增加了对Okta OIDC的配置。 首先,你需要在这个文件的顶部启用dotenv 。 接下来,配置express-session ,并将其添加到你的应用程序中。 最后,你可以使用.env 的变量来配置你的Okta OIDC中间件。

打开你的routes/index.js 文件,用下面的代码替换该文件的内容。

var express = require("express");
var router = express.Router();

/* GET home page. */
router.get("/", function (req, res, next) {
  res.render("index", { loggedIn: req.isAuthenticated() });
});

module.exports = router;

这里你添加了一个名为loggedIn 的属性,供index.jade 模板使用。这个属性将帮助视图决定是否应该显示一个loginlogout 按钮。 主页的其他部分将是静态的。

打开你的routes/users.js 文件,用以下代码替换该文件的内容。

var express = require("express");
var router = express.Router();

function userRoutes(options) {
  const oidc = options.oidc;

  router.get("/", oidc.ensureAuthenticated(), function (req, res, next) {
    res.render("users/index", {
      loggedIn: true,
      title: "Express",
      user: req.userContext.userinfo,
    });
  });

  return router;
}

module.exports.userRoutes = userRoutes;

这个文件是Okta OIDC中间件的神奇之处。 你在路由定义中传入oidc.ensureAuthenticated 中间件。 这个函数将把未经认证的用户重新路由到你在app.js 文件中定义的登录路由。 因为你没有明确设置这个参数,它将引导用户到/login ,并触发认证流程。 在这里传递给视图的模型也包含了一些关于用户的信息,当用户登陆这个页面时,你会显示这些信息。

编写你的前端代码

现在你可以写一些客户端代码了。 默认情况下,Express-generator会使用jade 来设置一些视图。 Jade是Node.js的一个模板引擎。 该语言包含了一些条件和流程控制,以使从你的模型中进行模板化的HTML更容易。 语法相当简单易学,而且有许多HTML到Jade的转换器。

打开与你的项目一起生成的layout.jade 文件,用下面的代码替换这些代码。

doctype html
html
  head
    title= title
    link(rel='stylesheet' href='/stylesheets/style.css')
    link(rel='stylesheet' href='css/bootstrap.min.css')
    script(language='javascript' src='js/bootstrap.min.js')

  body
    .container
      header.d-flex.flex-wrap.justify-content-center.py-3.mb-4.border-bottom
        h5.d-flex.align-items-center.mb-3.mb-md-0.me-md-auto.text-dark.text-decoration-none 
          a(href="/") Home
        ul.nav.nav-pills
          if loggedIn
            li.nav-item 
              a(href="users").nav-link Profile
          li.nav-item
            if loggedIn
              form(action="logout" method="post")
                button(type="submit").nav-link.active Logout
            else 
              a(href="Users").nav-link.active Login

      block content

我们使用了HTML元素;类是连锁的,它们之间有一个. 。属性以括号为界。 这个简单的布局从服务器上获取loggedIn ,并决定是否应该显示一个loginlogout 按钮。 如果用户已经登录,它还会有条件地显示一个Profile 页面的标签。

你可以通过在你的views 文件夹中创建一个名为users 的新文件夹,并添加一个名为index.jade 的新文件来添加个人资料页面。 在其中添加以下代码。

extends ../layout

block content
  h1 Welcome #{user.name}
  P locale: #{user.locale}
  p UserName: #{user.preferred_username}

这个页面显示用户登录时从Okta检索到的用户信息。

views/index.jade 中的代码替换为以下内容。

extends layout

block content

  .container-fluid.py-5.bg-dark.text-white-50
    h1.display-5.fw-bold  Deploy a Node.JS Application to Heroku
    p.col-md-8.fs-4
        | A tutorial on how to deploy a 
        a(href="https://nodejs.org/en/" target="_blank") node.js 
        | application to 
        a(href="https://dashboard.heroku.com/apps"  target="_blank") Heroku
        | .  Secured by 
        a(href="https://www.okta.com/"  target="_blank") Okta
        | .  Written by 
        a(href="https://profile.fishbowlllc.com/"  target="_blank") Nik Fisher.
  br
  p
    | This repository shows you how to use Okta in a Node.js application and how to deploy the application to Heroku.
  p Truncated for the purposes of the article.

如前所述,这是一个静态页面。 你的下一步是部署该应用程序。 但在这之前,运行命令npm run start ,并导航到http://localhost:3000 ,查看你的应用程序,确保它在本地工作。

Your Home Page

部署到Heroku

确保你的所有代码都在运行,并检查到你的GitHub仓库。 导航到你的Heroku仪表盘。 如果这是你第一次使用Heroku,你应该还没有配置任何应用程序。 点击 "新建",然后创建新的应用程序。 将你的应用程序命名为okta-heroku-webapp-{yourUserName} 或其他你认为合适的独特名称。 按下创建应用程序

在新创建的应用程序的页面上,找到部署标签上的部署方法部分。 点击GitHub,将你的Heroku账户连接到GitHub账户。

接下来,在连接到GitHub部分,找到你为本教程创建的仓库,按连接

Connect Heroku to GitHub

你可以在自动部署下启用自动部署。 此外,你还可以将其配置为等待CI通过后再部署应用程序。 CI可以从你的GitHub账户中进行配置。 这一步对于本教程来说并不是严格必要的,但看看它是如何融入CI管道的也不错。

Enable Automatic Deploys

最后,在Manual Deploy下选择你想部署的分支,然后点击Deploy Branch。 稍等片刻,你应该收到来自Heroku的消息,说你的应用程序已成功部署。

点击查看按钮,就可以看到你的应用程序。 在这一点上,你的应用程序不会运行,因为它还没有被正确配置。 记下你的网站的URL,然后你要做一些改变。

配置你的环境变量

在你的Heroku应用程序中,点击设置。 找到名为Config Vars 的部分,点击Reveal Config Vars。 在这里你将添加你在本地使用的.okta.env 文件中的相同的键和值对。 你可以在下面看到我的文件,其中的数值被模糊掉了。

Configure your Environment Variables

配置您的Okta应用程序

接下来,你需要配置你的Okta应用程序,以接受来自Heroku的新URL。 在Okta管理面板上导航到您的应用程序,找到常规设置标签。 点击编辑。 在Login部分,为Sign-in redirect URIs添加一个值,与开发设置中的URI相匹配,但用Heroku应用程序的URL替换http://localhost:3000 。 例如,我的URI是https://okta-heroku-webapp-nfisher.herokuapp.com

为你的退出重定向URI添加一个类似的值,它应该是你的Heroku应用程序的主页。

现在返回到你的应用程序。 你应该能够登录并看到你的应用程序如期运行。

用Node和Heroku做更多事情

Heroku是一种快速、轻松地部署轻量级应用的好方法,可以滑入你现有的CI流程。 此外,它还可以免费开始使用。与Express和Okta一起,你可以快速建立安全的Web应用,并将它们部署到一个现代平台上。

在本教程中,你学会了如何使用Express-generator工具链构建一个Express应用程序。 然后你用Okta保证了它的安全。 接下来,你学会了如何在Heroku上创建一个应用程序,将其连接到GitHub仓库,并将你的应用程序从GitHub部署到Heroku。 最后,你学会了如何配置Heroku和Okta以使其相互配合。