【问题标题】:How do I add Basic Authentication to Nextjs node server?如何将基本身份验证添加到 Nextjs 节点服务器?
【发布时间】:2021-01-26 17:02:06
【问题描述】:

不想想向某些外部请求或 OAuth 添加身份验证。只是对节点服务器的服务器端基本身份验证。

在编写 Next.js 时似乎不支持它:https://github.com/vercel/next.js/discussions/17719

它支持身份验证,但我想它必须添加到应用程序的每个路由中:https://nextjs.org/docs/authentication

【问题讨论】:

  • 您真的是指“(基本身份验证)[developer.mozilla.org/en-US/docs/Web/HTTP/Authentication]”吗?它实际上只是一个 HTTP 标头 Authorization,它是 user 和 pw 的 base64 版本,如果它丢失了,则需要一个 401 响应,这应该提示用户的浏览器。 @Su-AuHwang 有一个很好的例子来说明如何做到这一点。

标签: node.js next.js


【解决方案1】:

当前版本的 Nextjs 允许您通过在 pages 目录中添加一个 _middleware.js/ts 文件来运行添加自定义中间件。 在那里,您可以简单地检查 Auth-headers 并决定调用 next 以允许它或返回 401 状态代码以拒绝它。 这是从 Nextjs 示例(打字稿)复制的示例

import { NextRequest, NextResponse } from 'next/server'

export function middleware(req: NextRequest) {
  const basicAuth = req.headers.get('authorization')

  if (basicAuth) {
    const auth = basicAuth.split(' ')[1]
    const [user, pwd] = Buffer.from(auth, 'base64').toString().split(':')

    if (user === '4dmin' && pwd === 'testpwd123') {
      return NextResponse.next()
    }
  }

  return new Response('Auth required', {
    status: 401,
    headers: {
      'WWW-Authenticate': 'Basic realm="Secure Area"',
    },
  })
}

这里是完整示例项目的链接: https://github.com/vercel/examples/tree/main/edge-functions/basic-auth-password

【讨论】:

    【解决方案2】:

    编辑:如果您想在 Vercel 上托管下面的 ExpressJs 解决方案将不起作用,例如,它会花费一些成本,但可以提供带有内置边缘缓存和图像的最佳 Next.js 托管缩放和优化。因此,如果您想在 Vercel 上托管它而不是使用:https://nextjs.org/docs/authentication 为每条路由添加身份验证(除非从项目开始就完成)。

    如果您打算将它托管在支持 Node.js 和 Express.js 托管的其他地方(并且没有为 Next.js 提供任何特殊功能),那么以下是一个很好的解决方案。

    ExpressJs 解决方案:

    这篇文章不涉及存储基本身份验证凭据或使用多个凭据。这更像是一个起点,因为我在 google 上根本找不到任何东西。

    在下面的两个示例中,我们将使用 Express.js 来托管 NextJS(处于生产模式!)。

    在两个示例中启动服务器:node index.mjs(当前使用 node -v 14.9.0)

    首先:在根目录下创建index.mjs文件。

    (选项 1)使用最新的 NextJS:

    1. 使用 latest(撰写时为 9.5)NextJS + Express.js 节点服务器最新的 nextjs 已修复问题(我尚未验证) 与 CSP https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
    2. 在 index.mjs 中添加以下代码(安装所需模块):
        import next from 'next'
        import express from 'express'
    
        import auth from 'basic-auth'
    
        const dev = process.env.NODE_ENV === 'development'
        const app = next({ dev })
        const handle = app.getRequestHandler()
    
        app.prepare()
          .then(() => {
            const server = express()
    
            server.use(function (req, res, next) {
              const credentials = auth(req)
    
              if (!credentials || credentials.name !== '[change_me_username]' || credentials.pass !== '[change_me_password]') {
                res.status(401)
                res.header('WWW-Authenticate', 'Basic realm="example"')
                res.send('Access denied')
              } else {
                next()
              }
            });
    
            server.get('*', (req, res) => {
              return handle(req, res)
            })
    
            const port = 80;
            server.listen(port, (err) => {
              if (err) {
                throw err
              }
              console.log(`Ready on http://localhost:${port}`)
            })
          })
          .catch((ex) => {
            console.error(ex.stack)
            process.exit(1)
          })
    

    (选项 2)使用 NextJS 9.4(如果无法升级)

    此答案不涉及潜在 CSP 安全整体的潜在后果,最好和最简单的处理方法是升级到 NextJs 9.5 期间。

    1. 在 index.mjs 中添加以下代码(安装所需模块):
        import next from 'next'
        import express from 'express'
    
        import auth from 'basic-auth'
    
        const dev = process.env.NODE_ENV === 'development'
        const app = next({ dev })
        const handle = app.getRequestHandler()
    
        app.prepare()
          .then(() => {
            const server = express()
    
            server.use(function (req, res, next) {
              const credentials = auth(req)
    
              if (!credentials || credentials.name !== '[change_me_username]' || credentials.pass !== '[change_me_password]') {
                res.status(401)
                res.header('WWW-Authenticate', 'Basic realm="example"')
                res.send('Access denied')
              } else {
                // this is different from above NextJS 9.5, 9.4 requires inline js.
                res.setHeader(
                  'Content-Security-Policy',
                  "default-src * 'self' data: 'unsafe-inline' *"
                );
                next()
              }
            });
    
            server.get('*', (req, res) => {
              return handle(req, res)
            })
    
            const port = 80;
            server.listen(port, (err) => {
              if (err) {
                throw err
              }
              console.log(`Ready on http://localhost:${port}`)
            })
          })
          .catch((ex) => {
            console.error(ex.stack)
            process.exit(1)
          })
    
    1. next.config.js里面如果你用next-images,否则就放对象:module.exports = { // ... }
    const withImages = require('next-images')
    module.exports = withImages({
      async headers() {
        return [
          {
            source: '/:path*',
            headers: [
              {
                // // https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
                key: 'Content-Security-Policy',
                value: "default-src * 'self' data: 'unsafe-inline' *"
              }
            ]
          }
        ]
      }
    })
    

    【讨论】:

      猜你喜欢
      • 2011-06-10
      • 1970-01-01
      • 2018-06-03
      • 1970-01-01
      • 1970-01-01
      • 2021-09-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多