【问题标题】:Better way to secure REST resources in Spring?在 Spring 中保护 REST 资源的更好方法?
【发布时间】:2018-04-25 17:26:39
【问题描述】:

我有一个 RESTful 服务,它公开了 /user/{id} 等资源

现在,用户可以提供凭据、获取令牌并访问资源。但是,一旦通过身份验证,用户就可以访问任何id 的资源。

意思是,user1可以访问/user/1user/2等URI。我最终在控制器方法中使用了Principal,并开始使用用户尝试访问的id 检查Principalid

此外,用户有多个与之关联的资源。比如说,user1 拥有 res1 和 res2,user2 拥有 res3 和 res4。这些可以通过/user/1/res/2 访问。我需要一种方法来阻止/user/1/res/3,因为 res3 归 user1 而非 user2 所有。

但我相信这个问题很常见,我对我的解决方案并不十分相信。

有没有更好的方法来处理这个问题?

谢谢

【问题讨论】:

  • 你想要什么?不想让用户访问任何 {id}
  • 不,让用户访问自己的 id 而不是其他人的。 User1 的 id 为 1,应该能够访问 /user/1 而不是 /user/2。
  • 您实际上需要分配角色和权限来完成这项任务。我们在项目中使用 JWT 来做到这一点。
  • 您能详细说明一下吗?也许写一个答案?我的理解是所有用户都将拥有 USER_ROLE。将如何提供帮助?
  • 这是很常见的问题。所以我不会为你创建答案。只需查看帖子以获取帮助javabycode.com/spring-framework-tutorial/spring-security/…

标签: java spring rest spring-security


【解决方案1】:

如果所有用户都只能访问他们自己的 ID,那么您根本不应该暴露资源 /user/{id}。 如果我理解正确,只需公开/user 就足够了,从 Principal 或 session 等中找到用户 ID 并返回结果。

如果你真的想这样做,你可以自定义实现@PreAuthorize。从博客获得此代码。

@PreAuthorize("isUsersRes(#id)")
@RequestMapping(method = RequestMethod.GET, value = "/users/{id}")
public UsersfindById(@PathVariable long id) {
return Users.findOne(id);
}

公共类 CustomMethodSecurityExpressionRoot 扩展 SecurityExpressionRoot 实现 MethodSecurityExpressionOperations {

public CustomMethodSecurityExpressionRoot(Authentication authentication) {
    super(authentication);
}

isUsersRes 的实现

public class CustomMethodSecurityExpressionRoot 
extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
public boolean isMember(Long OrganizationId) {
 //user logic
}

查看完整博客here

【讨论】:

    【解决方案2】:

    这是有多种解决方案的常见问题。它也不是单独与 REST 相关的问题。自从应用程序存在以来,我们就有了这个。员工可以看到他的工资单、休假记录等,但不能看到其他员工的。 我最喜欢的一种解决方案是“深度安全”。这个想法来自我几十年来在银行系统中看到的这项工作。这需要首先在 DB 层中得到支持。 您将需要像此示例这样的表格设计(或任何您的应用的实体层次结构):

    Organisation
    -Dept
    --user
    

    所有非主表都需要与这些实体之一建立关系。示例:

    Payslip -> user
    Leave record -> user
    Manager -> dept
    HR Manager -> org
    

    等等……

    您将需要另一个表来绘制基本访问级别(如果我们需要实现不同的子访问级别,这可能会变得复杂)

    user1:dept2:org1
    user2:dept2:org1
    

    (我已经看到一些实现将这个表的信息作为加密访问令牌的一部分发送,如果访问必须是无会话的,则在每个访问请求上使用该令牌。)

    您没有提到框架/语言,但大多数语言都有数据库层。例如,如果 DB 层是 hibernate-java。有拦截器 (https://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/Interceptor.html#onPrepareStatement(java.lang.String)) 可用于修改正在执行的查询。 对 DB 的每个查询都将带有这些关系键的附加 where 子句。 我们可以在这个基本架构之上使用 Spring AOP、REST 拦截器和许多其他技术来加强这种安全性。 想法是,无论更高层代码使用什么查询,数据库层都不会返回登录用户主体无法访问的数据。 如果这已经到位,则为

    调用 REST GET

    /payslip/user1/Jan-2017

    会以 404 而不是 403 结束。 期望通过一个框架或一组肤浅的拦截器来解决这个问题既是冒险的,也不是未来的证明。随着 url 模式的发展,我们最终会不断调整拦截器。

    添加表格示例:

    ACL table
    user, uid, dept, org
    --------------------
    jhon, 1  , 1   , 1
    mary, 2  , 2   , 1
    will, 3  , 2   , 1
    
    Payslip table
    --------------
    month, net, deductions,..., uid
    -------------------------------------
    Jan  , 200, 15.5      ,..., 3  
    Feb  , 200, 15.5      ,..., 3
    
    Project table
    -------------
    pname, pstart, pbudget, dept
    ------------------------------------
    mark1, 12/21 , 20000  , 2
    markx, 12/31 , 40000  , 2
    

    【讨论】:

    • 有趣。您能否在表格上分享更多详细信息以映射基本访问级别?很明显,user1:dept2:org1 意味着 user1 可以访问 org1 中的 dept2。但是我们如何将其映射到不同的资源?
    【解决方案3】:

    您想要的是用户角色和权限 + 跨用户控制。要了解用户角色和权限,请参阅this

    此外,您可能还需要将他们的用户 ID 与资源 ID 进行交叉检查。由于您不能让 user1 的资源 ID 1 被 user2 查看,因此您需要将 userID 添加为资源 ID 的一部分,例如:- /user/user_id_1。 否则我们没有逻辑的方式来区分哪些资源适用于哪些用户。

    【讨论】:

      猜你喜欢
      • 2016-09-01
      • 2017-08-18
      • 2015-11-11
      • 1970-01-01
      • 2020-11-30
      • 2012-12-26
      • 2015-03-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多