首页 > 解决方案 > “ReplyError: ERR unkown command 'json.set' ? 使用 redis rejson, google oauth

问题描述

使用 NodeJS express 服务器通过服务器端的 OpenID 协议处理 GOOGLE oauth,我试图让回调在 google 使用查询字符串中的身份验证代码 + 将用户重定向到 API 后运行此清单。

  1. 只允许具有特殊学校域的电子邮件通过(这是一个学校项目)。
  2. 交换包含 Auth Token、Refresh Token 等的 Token Object。
  3. 通过 cookie 存储 Auth 令牌
  4. 通过redis缓存将整个令牌对象存储在JSON OBJECT中......这就是我被抓到的地方。(使用 redis 模块 REJSON.REJSON 因为数据库中的用户数据可以添加到内存中的对象以缓存用户数据...)
  5. 逻辑中的下一步包括检查用户是否存在于 mongodb 中,如果不存在则在 mongodb 中创建用户,重定向到浏览器,携带 cookie,使用 auth 令牌检查 redis 缓存以维护持久会话......最终将通过哈希,httpOnly 确保安全, https 等...

希望这能让您了解我的逻辑,但这似乎是一个 REDIS 或 REDIS 相关模块问题。

要使用 Redis REJSON 模块,我正在运行一个暴露于 6379 端口的 redis-redisjson docker 映像,并且 express 服务器(在 4000 端口上运行)将成为 redis 缓存服务器的客户端......

节点模块 Redis-rejson 允许将 redisJSON 命令映射到对 javascript 友好的名称。而 Node 模块 Redis 是 node 的 redis 客户端库。

//This is a minimalist web-framework for NodeJS
var express = require("express");

//axios - handles HTTP requests
const axios = require("axios");

//redis - use for caching
//rejson - store JSON via redis
const redis = require("redis"),
      rejson = require("redis-rejson");
      rejson(redis);

//set ports
const PORT = process.env.PORT || 4000;
const REDIS_PORT = process.env.PORT || 6379;

const client = redis.createClient(REDIS_PORT);

// Init Express app 
var app = express();

  //callback route from google

 app.get("/signin/callback", (req, res, next) => {
    //declare vars from query string api return for later use
    //console.log(req.query);
    let hd = req.query.hd;
    let authCode = req.query.code;

    //Only allow GUHSD email domains
    if(hd !== 'guhsd.net') {
        console.log('you are shall not pass')
        // The return is for stopping execution of controller
        return res.redirect(301, 'http://localhost:3000/?error=invalid_domain');
    } 

    //GET client_id and client_secret FROM JSON

     //Make POST REQ to exchange AuthCode for JSON token object
     axios.post('https://oauth2.googleapis.com/token', {
        client_id: '<taken out as to not expose>',
        client_secret: '<taken out as to not expose>',
        code: authCode,
        grant_type: 'authorization_code',
        redirect_uri: 'http://localhost:4000/signin/callback'
        })
        .then((response) => {
            console.log('Your token must to be here');

            // Logic after exchange 
            //1. Start Sessions - Client HTTP only Cookie & Server Redis Cache
            //2. Check if ACC exists - if so redirect client :)
            //3. if ACC DOES NOT exist, create new acc!! then redirect client :)

            async function passOver() {
                console.log(response.data);
                    // example: 
               //  {
               //     access_token: 'ya29.a0AfH6SMBSm3D99XAhmlxHtYOVVm7gZT9h8Bd8I9RgpYPhK0qXQwGT4fuMWhDsqDpcTfQuIg6bvY-tFUISX4Wm2Is-jprW2sstjmD4hjc2cQh5uIYODg1Re6v80FKENDdEAinTm9kid0QaKiaRxJQHt1deap-Ik1mcy2s',   
               //     expires_in: 3599, 
               //     refresh_token: '1//06qroj95zjugXCgYIARAAGAYSNwF-L9IrzWEi6q0wDpeUVKmnPZYZjZpsMS-KVT4fY9NzHgZCUxgY8fg2J_Nil2vJpMz52y-2pyY',
               //     scope: 'openid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email',
               //     token_type: 'Bearer',
               //     id_token: 'eyJhbGciOiJSUzI1NiIsImtpZCI6Ijk2MGE3ZThlODM0MWVkNzUyZjEyYjE4NmZhMTI5NzMxZmUwYjA0YzAiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIyNDM1NjY4NDcxMDItdTRkazg1Y21qcjEybWgya25ycHYzaW5zMnRjcnBzOHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiIyNDM1NjY4NDcxMDItdTRkazg1Y21qcjEybWgya25ycHYzaW5zMnRjcnBzOHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTA1MzUzMTYxMjcxODg4Mjg5NjYiLCJoZCI6Imd1aHNkLm5ldCIsImVtYWlsIjoiMzMwMjk0QGd1aHNkLm5ldCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJhdF9oYXNoIjoidEhoVEl6eWp3MHcyRFpVaVpUMmxCQSIsIm5hbWUiOiJERVZJTiBQUk9WRU5DRSIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vLVhTaV9oU2JsVWo0L0FBQUFBQUFBQUFJL0FBQUFBQUFBQUFBL0FNWnV1Y25HXzBwS3N0eVo3cEI0Wk0yZlpMQkNIUEU3UkEvczk2LWMvcGhvdG8uanBnIiwiZ2l2ZW5fbmFtZSI6IkRFVklOIiwiZmFtaWx5X25hbWUiOiJQUk9WRU5DRSIsImxvY2FsZSI6ImVuIiwiaWF0IjoxNTkwNjEwODE4LCJleHAiOjE1OTA2MTQ0MTh9.Nt2eGzCsKrUmyztqYWiZ16-1S7OCRcUFlSFvhVAy9HusYfLqp0nz3diUkuP-D_27BCtBsZCQ0JC1evPISwLX9H1hJs_GKYSD12s-ovJ8S0AzghY0M-AOuYxhGvKautusmXYfHvhfcPj7IKhPo_IXBl3x-ryOtRRrpJR6c30QPl1JUae74sAcLk8H1stLgqptrrRTgJWYdTXxJHSrZcR8RsLw2aY4GwuPGEX-AD6h51IZlNTl_6qNpaIt_7mFSUV-iF1PECAotfhKHdAryZCFRBq4XE6uJnYq3WnOVAMAEYwqT543pxarXOmLuAwhDqqewyuCXsjlbyhBys-4iEhYAg'
               //   }
               //expires_in is in seconds... cookie takes milliseconds as expires arg. 

                //send to client, store in cookie(browser's session starts)

                res.cookie(
                    "tokenResponse", `${response.data.access_token}`, {
                        maxAge: `${response.data.expires_in}`
                    }
                )

              var keyObject = JSON.stringify(response.data);
              //set redis key to user
              var user = 'user';
                //store response.data in redis cache(server's session starts)
                client.json_set(user, '.', keyObject, function (err) {
                    if (err) { throw err; }
                    console.log('Set JSON at key ' + user + '.');
                    client.json_get(user, '.access_token', function (err, value) {
                      if (err) { throw err; }
                      console.log('value of .', value); //outputs JSON
                      client.quit();
                    });
                  });
                res.json(keyObject);

                //redirect from server to frontend
              //  await res.redirect('http://localhost:3000');
             }

             //call passover
             passOver();


        })
        //if err, log & redirect user to client
        .catch((error) => {
             console.log(error.request);
             console.log(error.response.data);
            return res.redirect(301, 'http://localhost:3000/?error=google_failed_exchange');
        }); 
 });

//start the express server
app.listen(4000, () =>
console.log(`App started on port ${PORT}`)
);

module.exports = app;

我得到的错误是

                   if (err) { throw err; }
                               ^        

ReplyError: ERR unknown command 'json.set'
    at parseError (E:\docker-node-react-nginx\backend\node_modules\redis-parser\lib\parser.js:179:12)
    at parseType (E:\docker-node-react-nginx\backend\node_modules\redis-parser\lib\parser.js:302:14) {
  command: 'JSON.SET',
  args: [
    'user',
    '.',
    '{"access_token":"ya29.a0AfH6SMCSN3_0fXFexXYUqrkfhlJ5FmdkO-eqKeiXeSnRzlwD5aBDpBF7y-pXMDBY1YFqXLf-JU0mc5FXVo8nZER-6wB-hAm7qW_0w4Z3TcfoQfOT7ZXg8ZqK2DPyW8TnsZELWe9eDBYvFqM0lTWyWe3Z9ZqUXgv6Kz4","expires_in":3599,"refresh_token":"1//06FRrH2GkKtP2CgYIARAAGAYSNwF-L9IrnL1ZpMhxaiYWYuSnI7p6DG0uFIO3Vu2qt40Scio5SAlGT0mvBZ8hWvaPcJEEnjaunDw","scope":"https://www.googleapis.com/auth/userinfo.email openid https://www.googleapis.com/auth/userinfo.profile","token_type":"Bearer","id_token":"eyJhbGciOiJSUzI1NiIsImtpZCI6ImZiOGNhNWI3ZDhkOWE1YzZjNjc4ODA3MWU4NjZjNmM0MGYzZmMxZjkiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIyNDM1NjY4NDcxMDItdTRkazg1Y21qcjEybWgya25ycHYzaW5zMnRjcnBzOHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiIyNDM1NjY4NDcxMDItdTRkazg1Y21qcjEybWgya25ycHYzaW5zMnRjcnBzOHUuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTA1MzUzMTYxMjcxODg4Mjg5NjYiLCJoZCI6Imd1aHNkLm5ldCIsImVtYWlsIjoiMzMwMjk0QGd1aHNkLm5ldCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJhdF9oYXNoIjoiV2kwRVdlSl9fQmVsaGV1RF9SVEl6USIsIm5hbWUiOiJERVZJTiBQUk9WRU5DRSIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vLVhTaV9oU2JsVWo0L0FBQUFBQUFBQUFJL0FBQUFBQUFBQUFBL0FNWnV1Y25HXzBwS3N0eVo3cEI0Wk0yZlpMQkNIUEU3UkEvczk2LWMvcGhvdG8uanBnIiwiZ2l2ZW5fbmFtZSI6IkRFVklOIiwiZmFtaWx5X25hbWUiOiJQUk9WRU5DRSIsImxvY2FsZSI6ImVuIiwiaWF0IjoxNTkwNjI0NDIyLCJleHAiOjE1OTA2MjgwMjJ9.IyF5uB2ldLBQLu3CVMDpXf8szEK_BbR8SPrtdpJR_Y3bHklJ8e3JYGQT9AWjkcSy0I4DNUhkXiFk25HvZ06u2ekGd_adSknUVNwZe_N1IQTlMF1m-oqWbaRtnr4oxerQg_YunZDD4z_Lh5ecSDVz4X8H39uO7jrAvY1CdnZfZ4D2Je8aV1Zns5JahKhOTopPcy5sE1dSBNPqqGvUiY9h0MQHne9byUz9jMvog3YI-8-uexjC_JWsbzMFjE65ze5_cUpApYB5tUrNTjqvhiYgcimIPOXoto_VIHHEEoho5uHOkUQw_UVXleUa9vI77W1j7U7HnH-h_3C5ylx7UEDm6Q"}'
  ],
  code: 'ERR'
}
[nodemon] app crashed - waiting for file changes before starting...

标签: node.jsredisgoogle-oauthopenidredisjson

解决方案


import Redis from 'ioredis'
const connOpts = {
  port: process.env.REDIS_PORT,
  host: process.env.REDIS_HOST
 }
const client = new Redis(connOpts)
const sets = (id, keyPaths, input) => {
  
  return client.send_command('JSON.SET', id, keyPaths, JSON.stringify(input)).then((res) => {
    return res
  }).catch((e) => {
    console.error('redis insertion error', e)
  })
}
sets("134", ".", {"access_token":"ya29"}

推荐阅读