学习

使用 Redis 进行移动银行身份验证和会话存储

Will Johnston
作者
Will Johnston, Redis 开发者增长经理
Prasan Kumar
作者
Prasan Kumar, Redis 技术解决方案开发人员
GITHUB 代码

以下是克隆本教程中使用的应用程序源代码的命令

git clone --branch v1.2.0 https://github.com/redis-developer/mobile-banking-solutions

什么是移动银行的身份验证和会话存储?#

用户成功输入其登录凭据后,移动银行应用程序会使用服务器创建的 token 和 sessionId 来代表用户的身份。该 token 存储在 Redis 中,持续用户会话期间,并发送到银行应用程序客户端(移动/浏览器)的登录响应中。然后,客户端应用程序在每次向服务器发出请求时发送 token ,服务器在处理请求之前验证该 token 。

注意

Redis Stack 支持 JSON 数据类型,并允许您索引和查询 JSON 以及 更多。因此,您的会话存储不仅限于简单的键值字符串化数据。

会话存储存储与每个用户在其会话期间浏览应用程序相关的关键信息。移动银行会话数据可能包括但不限于以下信息

  • 用户的个人资料信息,例如姓名、出生日期、电子邮件地址等。
  • 用户的权限,例如 useradminsupervisorsuper-admin 等。
  • 其他与应用程序相关的数据,例如最近的交易、余额等。
  • 会话过期时间,例如从现在起一小时、从现在起一周等。

为什么您应该使用 Redis 进行移动银行会话管理?#

  • 弹性:Redis Cloud 提供了令人难以置信的弹性,正常运行时间为 99.999%。毕竟,身份验证令牌存储必须提供全天候可用性。这确保了用户能够不间断地全天候访问他们的应用程序。
  • 可扩展性:令牌存储需要高度可扩展,以便在大量用户同时进行身份验证时不会成为瓶颈。Redis Cloud 提供了 < 1 毫秒的延迟 ,并且具有令人难以置信的高吞吐量(高达 1 亿次操作/秒),这使得身份验证和会话数据访问的速度快得多!
  • 与常用库和平台集成:由于 Redis 开源已集成到大多数会话管理库和平台中,因此 Redis Cloud 在从开源 Redis 升级时可以无缝集成(例如,本教程中演示了 express-session 和 connect-redis-stack 库的集成)
提示

阅读我们的电子书,它回答了以下问题: JSON Web 令牌 (JWT) 安全吗? 它讨论了何时以及如何安全地使用 JWT,并提供了经过实战检验的会话管理解决方案。

使用 Redis 构建会话管理#

GITHUB 代码

以下是克隆本教程中使用的应用程序源代码的命令

git clone --branch v1.2.0 https://github.com/redis-developer/mobile-banking-solutions

下载上面的源代码,并运行以下命令以启动演示应用程序

docker compose up

docker up 并运行后,在浏览器中打开 https://localhost:8080/ 网址以查看应用程序

数据播种#

该应用程序利用了 Redis 核心数据结构、JSON、TimeSeries、搜索和查询功能。播种的数据后来用于显示可搜索的交易概述(实时更新),以及个人理财管理概述(实时余额和最大支出者更新)。

在应用程序启动时,在 app/server.js 中,会安排一个 cron 定期创建随机银行交易,并将这些交易播种到 Redis 中。

app/server.js
//cron job to trigger createBankTransaction() at regular intervals

cron.schedule('*/10 * * * * *', async () => {
  const userName = process.env.REDIS_USERNAME;

  createBankTransaction(userName);

  //...
});
  • 交易生成器会创建一个随机的银行借记或贷记,这将反映在(默认)初始用户余额 100,000.00 美元中。
  • 交易数据 作为 JSON 文档存储在 Redis 中。
  • 为了捕获 随时间推移的余额, balanceAfter 值将记录在具有 balance_ts 键的 TimeSeries 中,用于每次交易。
  • 为了跟踪 最大支出者,在排序集 bigspenders 中,与之关联的 fromAccountName 成员将根据交易金额递增。请注意,此金额可以是正数或负数。
app/transactions/transactionsGenerator.js
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 会话存储。

以下代码说明了使用 express-session 配置 Redis 会话。

app/server.js
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'));

登录 API(会话 ID 生成)#

让我们看看 /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');
  }
});

在上面的代码中 - session.userid 变量在成功登录(对于“bob”用户)时被分配一个值,因此会在 Redis 中创建一个会话,分配数据,并且只将 Redis 键(sessionId)存储在客户端 cookie 中。

  • 成功登录后的仪表盘页面
  • Redis 中的会话条目
  • 在仪表盘页面中打开开发者工具以检查客户端 cookie connect.sid (只包含 sessionId)

现在,对于来自客户端的每个其他 API 请求, connect-redis-stack 库会确保根据客户端 cookie(sessionId)从 Redis 加载会话详细信息到 req.session 变量。

余额 API(会话存储)#

考虑以下 /transaction/balance API 代码来演示会话存储。

我们必须修改 req.session 变量以更新会话数据。让我们添加更多会话数据,例如用户的当前余额金额。

app/routers/transaction-router.js
/* 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);
});
  • 在 Redis 中更新会话条目,添加 currentBalanceAmount 字段(“x”表示时间戳,“y”表示该时间戳的余额金额)
  • 在仪表盘 UI 中验证最新余额金额

准备好将 Redis 用于会话管理了吗?#

希望本教程能帮助您了解如何使用 Redis 进行更好的会话管理,特别是在移动银行领域。有关该主题的更多资源,请查看下面的链接。

其他资源#