RESTful服务中的资源级别授权

12

假设/users/{id}是RESTful服务中的资源URL。

基本身份验证已启用,只有经过身份验证的用户才能访问该URL。

示例场景:

User_1User_2是具有userId 1和2的经过身份验证的用户。 由于两者都经过身份验证,因此他们都可以访问以下内容:

  • /users/1
  • /users/2

但期望是User_1应该只能访问/users/1而不能访问/users/2或其他userId。

问题: 如何在RESTful服务中进行资源级别的授权?

注:我正在使用Jax-RS(带有Apache CXF实现)实现RESTful,如果您能够用Jax-RS解释会很有帮助。

- Barath

编辑:

正如Donal所提到的,我不是在寻找基于角色的授权,而是资源级别的授权。

举个例子,假设/users/{id}/photos/{photoId}是另一个资源URL。应该给予User_1仅对属于他自己的照片的访问权限。如果photoId为2,属于user_2,则在请求/users/1/photos/2时应为User_1返回http_404错误代码。[由于User_1也是经过身份验证的用户,因此他可以调用/users/2/photos/2,因此我们必须基于身份验证参数而非资源URL来确定用户ID]

我唯一能想到的解决方案是,在每个查询中包含确定授权的唯一ID,例如:

使用SELECT * FROM PHOTO_TBL, USER_TBL WHERE PHOTO_ID=2 AND USER_ID=1 AND USER_ID=PHOTO_ID;而不是SELECT * FROM PHOTO_TBL WHERE PHOTO_ID=2;

使用这些资源传递属于特定用户的数据。[需要一种机制来防止客户端修改用于授权的唯一标识符(在此情况下为userId),因为所有请求都是无状态请求]

注意:每个查询都应该足够智能,以理解安全问题并包含额外的连接。将安全逻辑绑定到每个业务函数是一个糟糕的设计。

我还没有研究Spring安全性以及如何在这种用例中使用它。


正如您在标签中所注明的,这是身份验证之后的授权问题。可以将其实现为应用程序的一部分,也可以作为中介代理来比较 URL 中的用户 ID 和身份验证标头中的用户 ID。 - Szocske
@Szocske:这就是值得将其放入应用程序中的地方。但是您可以使用Spring AOP(和Spring Security,当然)使其更容易。唯一稍微棘手的部分是意识到这确实不是基于角色的访问控制,因此SpringSec的RBAC支持并不相关。(遗憾的是,这是最好的教程材料...) - Donal Fellows
好的,现在我看到了关于图片ID的编辑,它需要与用户表连接。然而,在这种情况下,URL中很少需要用户ID :-) - Szocske
@Szocske:是的,URL中的userId不是必需的。 - Barath
@Barath 你解决了这个问题吗? - Sagar
我也在寻找类似的解决方案。@Barath,你能解决这个问题吗?如果可以,能详细说明一下吗? - Pradeep
2个回答

3
我建议不要在URL中使用用户ID(如果通过Basic Auth头部进行'限制',则可以通过Basic auth头部进行'指定')。这将降低引入直接对象引用漏洞的风险 - https://www.owasp.org/index.php/Top_10_2010-A4-Insecure_Direct_Object_References)。
在这种情况下,您可以有以下一种URL:
/users/CURRENT
/me

由于照片是子资源,因此您可以在用户内部使用“序列号”创建照片。在 SQL 数据库中,这意味着在用户和照片列上具有“复合键”。
/users/CURRENT/photo/{user_photo_seq}
/me/photo/{user_photo_seq}

你的SQL语句将会是这个样子:

SELECT * FROM PHOTO_TBL WHERE USER_ID=<BasicAuthUsername> AND PHOTO_ID=<path param value>;

一个关于“基本认证头”的好解释:

http://en.wikipedia.org/wiki/Basic_access_authentication


你能解释一下什么是基本身份验证头吗? - Jadiel de Armas

1

JAX-RS规定了子资源的处理方式,将请求处理委托给其他对象-子资源。

使用子资源只需要关注根资源,嵌套的资源也会被保护。

在示例中,您可以看到UserResource及其所有子资源仅对经过授权的用户可用。

@Path("/user/{userId}")
public class UserResource {

  private final String userId;

  public UserResource(@PathParam("userId") String userId, @Context SecurityContext securityContext) {
    this.userId = userId;

    boolean authorized = /* authorization code */;

    if (!authorized) { throw new WebApplicationException(Status.UNAUTHORIZED); }
  }

  @Path("photo")
  public PhotoResource getPhotoResource() {
    return new PhotoResource(userId);
  }

}

public class PhotoResource {

  private final String userId;

  public PhotoResource(String userId) {
    this.userId = userId;
  }

  @GET
  public Response listAll() { /* ... */ }

  @GET
  @Path("{photoId}")
  public Response present() { /* ... */ }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接