node.js - “ReplyError: ERR unkown command 'json.set' ? 使用 redis rejson, google oauth
问题描述
使用 NodeJS express 服务器通过服务器端的 OpenID 协议处理 GOOGLE oauth,我试图让回调在 google 使用查询字符串中的身份验证代码 + 将用户重定向到 API 后运行此清单。
- 只允许具有特殊学校域的电子邮件通过(这是一个学校项目)。
- 交换包含 Auth Token、Refresh Token 等的 Token Object。
- 通过 cookie 存储 Auth 令牌
- 通过redis缓存将整个令牌对象存储在JSON OBJECT中......这就是我被抓到的地方。(使用 redis 模块 REJSON.REJSON 因为数据库中的用户数据可以添加到内存中的对象以缓存用户数据...)
- 逻辑中的下一步包括检查用户是否存在于 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...
解决方案
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"}
推荐阅读
- r - 在R(dplyr)中进行多次分组后的百分比
- flask - 如果我点击 chrome 上的撤消按钮,如何清除会话。(烧瓶)
- python - Python获取类字段的类型?
- django - ImportError:无法从部分初始化的模块“delivery_api.main.serializers”导入名称“MealSerializer”
- python - 在 DRF 中过滤多个条件的数据
- angular - Angular Hybrid App:Angular 组件在本地渲染,但不在生产环境中渲染
- github - 根据拉取请求自动过滤文件
- android - 上传文件时出现 Sentry android 错误 - RAM 包的源映射类型无效,跳过
- java - 如何使用spring-boot从mongoDB中排除嵌套字段?
- amazon-web-services - AWS:使用变量将标签分配给秘密