首页 > 解决方案 > 快速会话中的存储能力?

问题描述

我已经看到了一个明确的例子,其中一个能力通过中间件存储在 req 对象中。然后它使用以下方法来评估权限:

ForbiddenError.from(req.ability).throwUnlessCan('read', article);

我想实现类似的事情。我的想法是将能力保存在与 socket io websockets 共享的快速会话中。通过分享req.session= socket.handshake.session。我的方法如下,我从前端应用程序发出请求以获取规则以更新前端的能力。后端将能力保存在 express 会话中:

// abilities.js file
import { Ability } from '@casl/ability';

export const defineAbilitiesFor = (rules) => {
  return new Ability(rules);
};

export default defineAbilitiesFor;
// handler for express route to get permissions from the frontend
export const getPermissions = async (req, res) => {
...
  rules.push({
    action: ['view'],
    subject: views,
  });
  // manage all own processes
  rules.push({
    action: ['manage'],
    subject: 'Process',
    conditions: {
      userId: req.kauth.grant.access_token.content.sub,
    },
  });
// store ability in session
  req.session.rules = defineAbilitiesFor(rules);
  const token = jwt.sign({ token: packRules(rules) }, 'secret');
  if (token) {
    return res.status(200).json(token);
  } else {
    return res.status(400).json('Error');
  }
...

然后,当发生 websocket 请求时,我想在后端检查用户是否有权执行该操作:

ForbiddenError.from(socket.handshake.session.rules).throwUnlessCan('view', 'Process');

但是,这会引发以下错误:

TypeError: this.ability.relevantRuleFor is not a function
    at ForbiddenError.throwUnlessCan

会话对象似乎具有正确的能力对象。当我 console.log socket.handshake.session.rules,我得到以下输出:

{
  h: false,
  l: {},
  p: {},
  '$': [
    { action: [Array], subject: 'Process', conditions: [Object] },
    { action: [Array], subject: [Array] },
    { action: [Array], subject: 'Process', conditions: [Object] }
  ],
  m: {}
}

罐头功能和我尝试的所有其他方法都不起作用。我认为将普通规则存储为会话中的对象,然后在每个请求起作用之前更新能力类,但我不想这样做。我想将能力存储在会话中,这样我只需要执行 throwUnlessCan 或 can 函数。

这甚至可能吗?如果可以,你会怎么做?

感谢到目前为止。

标签: expresssessionexpress-sessioncasl

解决方案


无需存储整个Ability实例,您只需存储其规则!rules 是一个普通的 js 对象数组,因此可以很容易地序列化。所以,将代码更改为:

export const getPermissions = async (req, res) => {
...
  rules.push({
    action: ['view'],
    subject: views,
  });
  // manage all own processes
  rules.push({
    action: ['manage'],
    subject: 'Process',
    conditions: {
      userId: req.kauth.grant.access_token.content.sub,
    },
  });
// store ability RULES in session
  req.session.rules = rules;
  const token = jwt.sign({ 
    token: packRules(rules)  // packRules accepts an array of RawRule! not an Ability instance
  }, 'secret');

  if (token) {
    return res.status(200).json(token);
  } else {
    return res.status(400).json('Error');
  }

Ability在其他处理程序中使用,请添加中间件:

function defineAbility(req, res, next) {
  if (req.session.rules) {
    req.ability = new Ability(req.session.rules);
    next();
  } else {
    // handle case when there is no rules in session yet
  }
}

// later

app.get('/api/users', defineAbility, (req, res) => {
  req.ability.can(...);
  // or
  ForbiddenError.from(req.ability).throwUnlessCan(...);
})

推荐阅读