【问题标题】:How to use a private API key with Nuxt (on the client)?如何使用 Nuxt 的私有 API 密钥(在客户端)?
【发布时间】:2021-12-05 23:07:20
【问题描述】:

问题已解决

如果您遇到同样的问题,请查看接受的答案,这是使用 serverMiddleware 实现它的一种方法


我正在使用需要私钥的 API。我将密钥存储在 .env 文件中,并在 nuxt 配置文件中调用它,如下所示:

privateRuntimeConfig: {
        secretKey: process.env.MY_SECRET_KEY
},

我的 API 调用是在我的索引页面上的 asyncData() 挂钩内完成的。当我加载此页面或重新加载它时它工作正常,但每次我使用导航返回此页面时,我都会遇到错误(我使用缓冲区将我的 API 密钥转换为 base64)

第一个参数必须是字符串、Buffer、ArrayBuffer、Array 或类似数组的对象。

经过一番研究和调试,我发现我的私钥当时不可用,并且我的 api 调用中使用的“秘密”值是“未定义”。

我不明白的是为什么这在初始加载/重新加载时有效,但在页面导航上无效?有没有办法在不使用后端的情况下修复它? (用于 SEO 的 SSR 和使用私钥而不暴露它们的能力是我在项目中使用 Nuxt 的主要原因)

这是我的代码:

async asyncData({ $content, store, $config }) {
    const secret = Buffer.from($config.secretKey).toString('base64')
    const request = await fetch('https://app.snipcart.com/api/products', {
        headers: {
            'Authorization': `Basic ${secret}`,
            'Accept': 'application/json'
        }
    })
    const result = await request.json()
    store.commit('products/addProducts', result)
    const stocks = store.getters['products/getProducts']



    return { stocks }
},

【问题讨论】:

    标签: nuxt.js


    【解决方案1】:

    更新:

    当网站从 API 调用所在页面之外的任何其他页面被拳头访问时,它不起作用,因为商店不会有来自 API 调用的任何数据)


    好吧,现在我觉得自己很笨。我找到了让它工作的方法。我想花时间解释我的问题有助于我理解如何解决它。

    对于那些遇到类似问题的人,我通过使用 If 语句包装我的 API 调用来修复它。

    if ($config.secretKey) {
        const secret = Buffer.from($config.secretKey).toString('base64')
        const request = await fetch('https://app.snipcart.com/api/products', {
            headers: {
                'Authorization': `Basic ${secret}`,
                'Accept': 'application/json'
            }
        })
        const result = await request.json()
        store.commit('products/addProducts', result)
    }
    const stocks = store.getters['products/getProducts']
    

    这样,我可以跳过 API 调用并从我的 vuex 存储中访问值。

    【讨论】:

    • 即使这适用于您的情况,如果您尝试访问 /cart 或任何其他需要此令牌的路径,则下一次调用将不可行。
    • 是的,当我尝试清理缓存 + 在访问这条路线之前从另一条路线重新加载时,我发现了这一点。这就是我删除这篇文章的原因(但因为它可以防止其他人犯同样的错误,所以我用更新行把它放回去了)
    【解决方案2】:

    更新

    查看@nuxtjs/snipcart module's key 键,因为它是buildModules,你完全可以把它放在那里,因为它只在构建期间可用(仅在 Node.js 上)!

    要了解更多信息,Snipcart 确实有很多博客文章,这篇基于 Nuxt 的文章可能有助于清理:https://www.storyblok.com/tp/how-to-build-a-shop-with-nuxt-storyblok-and-snipcart


    您最初确实有您的密钥,因为您在进入页面或硬刷新页面时会到达服务器。

    如果您在水合后导航,它将是客户端导航,因此您将无法访问私钥。最后,如果您的密钥真的是私有的(现在,一些 API 提供可以公开的密钥),您需要通过某些方式解决它。

    查看 Snipcart:https://docs.snipcart.com/v3/api-reference/authentication,它明确指出密钥应该在

    出现在您编译的前端资产(HTML、JavaScript)中

    同时,如果您需要再次调用后端(尝试访问 products 以外的其他内容),则需要进行第二次调用。

    使用 Nuxt2,您现在无法每次都访问后端,因为您将停留在 SPA 上下文中(Nuxt 基本上是 server then client Vue 应用程序)。但是您可以将令牌写入 cookie,甚至更好,使用后端作为代理来隐藏此特定密钥(甚至是无服务器函数)。

    可以在我的其他答案中找到更多信息:https://stackoverflow.com/a/69575243/8816585

    【讨论】:

    • 我正在使用当前版本的商店中的模块,但我仍然没有找到如何安全地使用私有 API 密钥。我需要访问每个产品/变体的剩余库存,以便直接在产品页面上显示“缺货”/“仅剩 2 个”消息,我认为没有办法做到这一点这不涉及私有 API 密钥。
    【解决方案3】:

    感谢@kissu 的(非常)快速的回答 :)

    因此,根据您所说的以及您对该主题的其他回答,我在我的服务器文件夹中的 Nuxt 中制作了一个服务器中间件。

    server/snipcart.js

    const bodyParser = require('body-parser')
    const axios = require('axios')
    const app = require('express')()
    
    app.use(bodyParser.json())
    app.all('/getProducts', (request, response) => {
        
        const url = 'https://app.snipcart.com/api/products'
        const secret = Buffer.from(process.env.SNIPCART_SECRET).toString('base64')
        const config = {
            headers: {
                'Authorization': `Basic ${secret}`,
                'Accept': 'application/json'
            }
        }
    
        axios
            .get(url, config)
            .then(res => {
                const products = {}  
                res.data.items.forEach(
                    item => {
                        const productId = item.userDefinedId.replace(/-/g, '')
                        const stocks = {}
    
                        item.variants.forEach(
                            variant => {
                                const size = variant.variation[0].option
                                const stock = variant.stock
                                stocks[size] = stock
                            }
                        )
                        products[productId] = stocks
                        
                    }
                )
                response.json(products)
            })
            .catch( err => response.json(err) )
    })
    
    module.exports = app  
    

    如果我错了,请纠正我,但我认为这与使用服务器作为代理基本相同,对吧?基于 Nuxt 生命周期钩子,serverMiddleware 一个只在服务器上运行,所以我的 API 密钥不应该暴露给客户端? (我仍然需要做一些重构来清理代码,但至少它可以工作)(https://nuxtjs.org/docs/concepts/nuxt-lifecycle/#server & https://nuxtjs.org/docs/configuration-glossary/configuration-servermiddleware/

    nuxt.config.js

    serverMiddleware: [
      { path: "/server", handler: "~/server/snipcart.js" }
    ]
    

    index.vue(我的 snipcart API 调用之前是在其中进行的,我想现在我应该直接从需要数据的产品卡组件中移动这个调用):

    async asyncData({ $content, store, $axios }) {
     
        await $axios
            .get('/server/getProducts')
            .then(res => store.commit('products/addProducts', res.data))
            .catch(err => console.log(err))
     
        const stocks = store.getters['products/getProducts']
    
        return {stocks, masterplanProducts }
    },
    

    PS : Snipcart 确实提供了一个公共 API 密钥,但用途非常有限。为了访问每个产品的剩余库存,我必须使用私钥(它允许进行一些其他操作,例如删除产品/访问订单等)

    【讨论】:

    • 作为 Daniel 的 stated here,您确实可以使用 serverMiddleware 来拥有某种本地 REST API,它将您的调用转发到外部 API 而不会暴露您的公共令牌。我对此没有任何经验,但这是一个很棒的方法,做得很好!
    • 如果可以,请随时将您的答案标记为已接受。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-28
    • 2020-09-16
    • 2016-01-17
    • 2019-11-27
    • 1970-01-01
    • 2021-05-12
    相关资源
    最近更新 更多