有一些 restful apis,如下所示:
api/v1/billing/invoices/{invoiceNumber}
api/v1/billing/transactions/{transactionNumber}
而且,每张发票或交易都属于一个特定的帐户。
在实现 restful API 时,我们必须满足:每个帐户只能查看自己的发票或交易。
我们应该如何在 restful apis 中隔离数据?
当然,我们可以将account number
传递给 api,例如:
api/v1//billing/invoices/{invoiceNumber}?accoutNumber=XXX
api/v1/billing/{accountNumber}/invoices/{invoiceNumber}
但是Invoice Number
已经能够唯一地标识资源。所以我不希望问题变得复杂。
还有其他方法可以解决这个问题吗?
你在这里混合了很多东西。
这不是REST 问题,这是一个安全问题。更准确地说,它是 OWASP 2013 年 10 大不安全直接对象漏洞。
让我们简单一点:您有一个这样的 URL
.../superSensitiveStuff/1
并且你想阻止"1
"的所有者访问".../superSensitiveStuff/2
">
据我所知,有三种方法可以解决这个问题:
在请求 URL 中强制实施完整性。此策略并非适用于所有情况,它仅适用于客户端向服务器先前通信的资源发出请求的情况。在这种情况下,服务器可能会添加如下所示的查询参数
.../superSensitiveStuff/1?sec=HMAC(.../superSensitiveStuff/1)
其中 HMAC 是一个加密哈希函数。如果缺少参数,服务器将删除请求,如果它在那里,服务器将能够验证它是否正是授权的URL,因为HMAC值保证了其完整性(有关其他信息,请点击上面的链接)。
使用不可预测的引用。这里的问题是用户可以猜出另一个 id。 ">嗯...我有资源编号 1,让我检查资源编号 2是否存在"。如果你删除序列并移动到长随机数,这是很难做到的。资源将成为
.../superSensitiveStuff/195A23FR3548...32OT465
这很好,因为它有效且便宜。
利用RBAC-ABAC混合方法。RBAC 代表 基于角色的访问控制,这就是您正在使用的。第二个首字母缩略词的前导 A 代表属性。这意味着访问权限是根据用户角色和属性提供的。在这种情况下是 userId,因为它必须经过身份验证才能访问私有资源。简而言之,当用户请求特定的
.../superSensitiveStuff
资源时,当您拥有该资源的所有权信息时,将从存储库加载该资源。例如,它可能是一个DB,而你的SuperSensitiveStuff java商业模式可能是这样的。public class SuperSensitiveStuff { private String userId; private String secretStuff; ... }
现在,在控制器中,您可以执行以下操作
String principal = getPrincipal(); //you request the logged userId SuperSensitiveStuff resource = myService.load(id); //you load the resource using the {id} in the request path if (resource.getUserId.equals(principal)) return resource //200 ok, this is an authorized access else throw new EvilAttemptException() //401 unauthorized, cheater detected