如何基于配置实现按用户名限制期货资产访问的安全逻辑
实现方案
你现有代码的逻辑方向是对的,核心问题出在异步权限加载的时序错误、变量误用两个点上,createUserFuturesAssetsConstraints本身的结构不需要大改,按下面的步骤修正即可:
首先修复已知的逻辑错误
- 你之前把权限存储对象
userPermittedFutures和权限查询函数getPermittedFuturesAssets搞混了,在接口里判断的是函数上的属性,根本读不到用户权限 getPermittedFuturesAssets重试的时候没有传入traderId,导致权限查询始终失败- 全局获取当前用户的方式在并发请求场景下会出现用户串号的问题,应该直接从请求对象
req.user里取当前登录用户
修正后的完整代码
1. 先改造权限查询函数为Promise异步形式,保证能等待权限加载完成
const userPermittedFutures = {}; // 改造为返回Promise的异步函数,支持等待权限加载完成 const getPermittedFuturesAssets = (traderId) => { return new Promise((resolve, reject) => { if (!pgClient) { setTimeout(() => { getPermittedFuturesAssets(traderId).then(resolve).catch(reject); }, 1000); return; } const query = { name: 'fetch-futures-assets-permissions', text: 'SELECT trader_id, pair, pair_limit ' + 'FROM common.accounts_pairs_permits app ' + 'WHERE exists(SELECT * FROM future.instrument ap WHERE ap.symbol = app.pair) AND trader_id = $1', values: [traderId], }; pgClient.query(query, (err, res) => { if (err) { console.error("Can't get account permitted futures permits. Retrying in a second..."); setTimeout(() => { getPermittedFuturesAssets(traderId).then(resolve).catch(reject); }, 1000); return; } // 把用户权限写入全局缓存 for (let row of res.rows) { addUserFuturesConstraints(row.trader_id, row.pair, row.pair_limit); } resolve(); }); }) } function addUserFuturesConstraints(trading_id, futuresAsset, limit) { if (!userPermittedFutures[trading_id]) { let user_permitted_limits = {} user_permitted_limits[futuresAsset] = createUserFuturesAssetsConstraints(limit); userPermittedFutures[trading_id] = user_permitted_limits; } else { userPermittedFutures[trading_id][futuresAsset] = createUserFuturesAssetsConstraints(limit); } } function createUserFuturesAssetsConstraints(pairQuantityLimit) { return { pairQuantityLimit: parseFloat(pairQuantityLimit), splitConstraints: [], } }
2. 修正/futures接口的资产过滤逻辑
app.get('/futures', checkAuthenticated, async (req, res) => { try { // 先从请求对象取当前用户,避免并发串号 const currentUser = req.user.username; const traderId = traderIdMnemonicMapping[currentUser]; if (!traderId) { return res.status(403).send({ status: 'fail', message: '未找到对应用户交易权限' }); } // 先加载用户权限 await getPermittedFuturesAssets(traderId); // 查询全量期货资产 const query = { name: 'fetch-futures-portfolios', text: 'SELECT ' + 'future.instrument.symbol ' + '....... ' + 'WHERE future.exchange.name = $1', values: ['Futures'], }; const futuresPortfolios = await pgClient.query(query); const allFutures = futuresPortfolios.rows.map(item => item.symbol); // 过滤用户有权限的资产 const allowedAssets = userPermittedFutures[traderId] ? Object.keys(userPermittedFutures[traderId]) : []; // 取全量资产和用户权限的交集,避免返回数据库里不存在的资产 const allowedFuturesAssetsForUser = allFutures.filter(asset => allowedAssets.includes(asset)); res.status(200).send(allowedFuturesAssetsForUser); } catch (err) { console.log("接口处理失败: " + err.message); res.status(503).send({ status: 'fail', message: err.message }); } });
3. 交易接口直接挂载现有校验中间件即可
所有需要限制资产访问的交易接口,直接加isUserAbleToTradeFutures中间件就行,现有逻辑不需要改:
// 示例:下单接口加权限校验 app.post('/futures/order', checkAuthenticated, isUserAbleToTradeFutures, async (req, res) => { // 你的下单逻辑 })
校验逻辑说明
- 普通用户访问
/futures接口时,只会返回数据库中存在、且该用户有权限的资产列表 - 用户发起交易请求时,中间件会自动校验请求的资产是否在用户权限列表内,同时检查交易额度是否超过限制
- 管理员用户不受上述规则限制,可访问所有资产
内容的提问来源于stack exchange,提问作者user1285928
相关产品推荐
相关产品推荐

