以下是克隆本教程中使用的应用源代码的命令
git clone --branch v1.2.0 https://github.com/redis-developer/mobile-banking-solutions
用户成功输入登录凭据后,手机银行应用会使用服务器创建的 token
和 sessionId
来代表用户身份。该 token
在 Redis 中存储用户会话期间,并在登录响应中发送给银行应用客户端(手机/浏览器)。然后客户端应用在每个请求中发送该 token
到服务器,服务器在处理请求之前对其进行验证。
Redis Stack 支持 JSON 数据类型,允许您对 JSON 和 更多 内容进行索引和查询。因此您的会话存储不局限于简单的键值字符串化数据。
会话存储在用户导航应用的整个会话期间保存与每个用户相关的关键信息。手机银行会话数据可能包括但不限于以下信息:
user
、 admin
、 supervisor
、 super-admin
等。express-session
和 connect-redis-stack
库的集成)。阅读我们的电子书,它回答了这个问题: JSON Web Token (JWT) 安全吗? 它讨论了何时以及如何安全地使用 JWT,并提供了久经验证的会话管理解决方案。
以下是克隆本教程中使用的应用源代码的命令
git clone --branch v1.2.0 https://github.com/redis-developer/mobile-banking-solutions
下载以上源代码并运行以下命令以启动演示应用
docker compose up
Docker 启动并运行后,在浏览器中打开 http://localhost:8080/ URL 查看应用
此应用利用了 Redis 核心数据结构、JSON、TimeSeries、搜索和查询功能。填充的数据随后用于展示可搜索的交易概览(包含实时更新)以及个人财务管理概览(包含实时余额和最大支出者更新)。
在 app/server.js
的应用启动时,计划了一个 cron 任务,用于定期创建随机银行交易并将其填充到 Redis 中。
//cron job to trigger createBankTransaction() at regular intervals
cron.schedule('*/10 * * * * *', async () => {
const userName = process.env.REDIS_USERNAME;
createBankTransaction(userName);
//...
});
balanceAfter
值记录在键为 balance_ts
的 TimeSeries 中。bigspenders
中关联的 fromAccountName
成员会按交易金额递增。请注意,该金额可以是正数或负数。let balance = 100000.0;
const BALANCE_TS = 'balance_ts';
const SORTED_SET_KEY = 'bigspenders';
export const createBankTransaction = async () => {
//to create random bank transaction
let vendorsList = source.source; //app/transactions/transaction_sources.js
const random = Math.floor(Math.random() * 9999999999);
const vendor = vendorsList[random % vendorsList.length]; //random vendor from the list
const amount = createTransactionAmount(vendor.fromAccountName, random);
const transaction = {
id: random * random,
fromAccount: Math.floor((random / 2) * 3).toString(),
fromAccountName: vendor.fromAccountName,
toAccount: '1580783161',
toAccountName: 'bob',
amount: amount,
description: vendor.description,
transactionDate: new Date(),
transactionType: vendor.type,
balanceAfter: balance,
};
//redis json feature
const bankTransaction = await bankTransactionRepository.save(transaction);
console.log('Created bankTransaction!');
// ...
};
const createTransactionAmount = (vendor, random) => {
let amount = createAmount(); //random amount
balance += amount;
balance = parseFloat(balance.toFixed(2));
//redis time series feature
redis.ts.add(BALANCE_TS, '*', balance, { DUPLICATE_POLICY: 'first' });
//redis sorted set as secondary index
redis.zIncrBy(SORTED_SET_KEY, amount * -1, vendor);
return amount;
};
使用 RedisInsight 查看示例 bankTransaction
数据
下载 RedisInsight 以查看您的 Redis 数据或在工作台中试用原始 Redis 命令。
Redis 已集成到许多会话管理库中,我们将在此演示中使用 connect-redis-stack 库,它为您的 express-session 应用提供 Redis 会话存储。
以下代码演示了如何配置 Redis 会话以及如何与 express-session
集成。
import session from 'express-session';
import { RedisStackStore } from 'connect-redis-stack';
/* configure your session store */
const store = new RedisStackStore({
client: redis, //redis client
prefix: 'redisBank:', //redis key prefix
ttlInSeconds: 3600, //session expiry time
});
const app = express();
// ...
app.use(
session({
store: store, //using redis store for session
resave: false,
saveUninitialized: false,
secret: '5UP3r 53Cr37', //from env file
}),
);
//...
app.listen(8080, () => console.log('Listening on port 8080'));
让我们看一下 /perform_login
API 代码,该代码在 登录页面 点击登录按钮时触发。
由于 connect-redis-stack 是 Express 中间件,会话在请求开始时自动创建,并在 HTTP(API) 响应结束时更新,如果 req.session
变量被修改。
app.post('/perform_login', (req, res) => {
let session = req.session;
console.log(session);
/*
Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
}
*/
//hardcoded user for demo
if (req.body.username == 'bob' && req.body.password == 'foobared') {
//on successful login (for bob user)
session = req.session;
session.userid = req.body.username; //create session data
res.redirect('/index.html');
} else {
res.redirect('/auth-login.html');
}
});
在上述代码中,登录成功后(针对“bob”用户), session.userid
变量被赋值,因此在 Redis 中创建了一个包含分配数据的会话,客户端 cookie 中仅存储了 Redis 键(sessionId)。
connect.sid
(仅包含 sessionId)现在,客户端的每个其他 API 请求, connect-redis-stack 库都会根据客户端 cookie (sessionId) 从 Redis 加载会话详情到 req.session
变量。
参考以下 /transaction/balance
API 代码以演示会话存储。
我们需要修改 req.session
变量以更新会话数据。我们添加更多会话数据,例如用户的当前余额。
/* fetch all transactions up to an hour ago /transaction/balance */
transactionRouter.get('/balance', async (req, res) => {
const balance = await redis.ts.range(
BALANCE_TS,
Date.now() - 1000 * 60 * 5,
Date.now(),
);
let balancePayload = balance.map((entry) => {
return {
x: entry.timestamp,
y: entry.value,
};
});
let session = req.session;
if (session.userid && balancePayload.length) {
//adding latest BalanceAmount to session
session.currentBalanceAmount = balancePayload[balancePayload.length - 1]; //updating session data
}
res.send(balancePayload);
});
currentBalanceAmount
字段('x' 表示时间戳,'y' 表示该时间戳下的余额)希望本教程能帮助您了解如何在会话管理中更好地使用 Redis,尤其是在手机银行场景下。有关此主题的其他资源,请查看以下链接: