在ServiceStack中进行属性级别授权的最佳方法? [英] Best way to do property level authorization in ServiceStack?

查看:58
本文介绍了在ServiceStack中进行属性级别授权的最佳方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在Angular中开发SPA,因此我已经使用ServiceStack创建了REST服务。我还使用ServiceStack的默认身份验证和授权解决方案,该解决方案允许我使用 Authenticate 属性装饰服务,还可以授权角色。

I'm currently developing a SPA in Angular, and so I've created a REST service using ServiceStack. I am also using ServiceStack's default authentication and authorization solution, which allows me to decorate services with the Authenticate attribute, and also allows me to authorize roles.

但是,由于我的应用程序具有用户,并且用户拥有资源,因此我需要一种方法来限制未经授权的用户执行某些操作。此外,我希望能够为每个离散实体创建一个服务,该服务可以根据用户的授权级别正确确定哪些内容可以安全地写入数据库以及哪些内容可以安全地返回给用户。

However, since my application has users, and users own resources, I need a way to restrict non-authorized users from performing certain actions. Furthermore, I would like to be able to create a single service for each discrete entity which can properly figure out what is safe to write to the database and what is safe to return to the user depending on their level of authorization.

例如,假设我创建了一个服务来处理 Group 实体上的操作。我允许执行的操作之一是获取有关其详细信息:

So as an example, let's say I've created a service to handle operations on a Group entity. One of the actions I allow on a Group is to get the details for it:


  • 路由: api / groups / {Id}

  • 响应:名称说明 CoverImageUrl 成员

  • Route: api/groups/{Id}
  • Response: Name, Description, CoverImageUrl, Members

但是,根据用户是谁,我希望限制返回的数据:

However, depending on who the user is, I wish to restrict what data is returned:


  • 未通过身份验证:名称 CoverImageUrl

  • 经过认证:名称 CoverImageUrl 解密

  • 请求的组成员:完全访问权限

  • 网站管理员:完全访问权限

  • Not authenticated: Name, CoverImageUrl
  • Authenticated: Name, CoverImageUrl, Decription
  • Member of requested group: Full access
  • Admin of website: Full access

因此,执行此操作的一种简单方法是创建3个不同的响应DTO,每种响应类型一个。然后,在服务本身中,我可以检查用户是谁,检查他们与资源的关系,并返回适当的响应。这种方法的问题在于,我会重复很多次自己,并会创建仅是主 DTO子集的DTO。

So one simple approach to doing this is to create 3 different response DTOs, one for each type of response. Then in the service itself I can check who the user is, check on their relation to the resource, and return the appropriate response. The problem with this approach is that I would be repeating myself a lot, and would be creating DTOs that are simply subsets of the "master" DTO.

对我来说,理想的解决方案是以某种方式装饰DTO上的每个属性,例如:

For me, the ideal solution would be some way to decorate each property on the DTO with attributes like:


  • [CanRead( Admin ,所有者,成员)]

  • [CanWrite( Admin,所有者)]

  • [CanRead("Admin", "Owner", "Member")]
  • [CanWrite("Admin", "Owner")]

然后在请求过程中的某个位置,它将根据用户和用户的身份来限制写入数据库的内容仅序列化允许用户读取的主 DTO的子集。

Then somewhere during the request, it would limit what is written to the database based on who the user is and would only serialize the subset of the "master" DTO that the user is permitted to read.

有人知道我如何在ServiceStack中获得理想的解决方案,甚至什至可以更好?

Does anyone know how I can attain my ideal solution within ServiceStack, or perhaps something even better?

推荐答案

对我来说,最实用的解决方案实际上很简单。基本思想是,任何需要行级授权的服务都应实现 GetUserRole 方法,在我的情况下,该方法将返回用户的最大权限。

What ended up being the most pragmatic solution for me was actually pretty simple. The basic idea is that whichever service requires row-level authorization should implement a GetUserRole method, which in my case returns the user's most permissive role.

protected string GetUserRole(Domain.Group entity)
{
    var session = SessionAs<AuthUserSession>();
    var username = session.UserName;

    if (session.Roles.Contains("Admin"))
    {
        return "Admin";
    }

    if (entity.Id == default(int) || entity.Leader.Username.Equals(username))
    {
        return "Leader";
    }

    // More logic here...

    return session.IsAuthenticated ? "User" : "Anonymous";
}

然后,我可以使用用户角色来弄清楚该让他们写什么:

Then I can use the user's role to figure out what to let them write:

var entityToWriteTo = ... // code that gets your entity
var userRole = GetUserRole(entityToWriteTo);

if (new[] {"Admin"}.Contains(userRole))
{
    // write to admin-only entity properties
}

if (new[] {"Admin", "Leader"}.Contains(userRole))
{
    // write to admin or leader entity properties
}

// Etc.

同样的逻辑适用于读取:您填充一个具有根据其角色有条件设置的属性的DTO。稍后,当您将DTO返回给客户端时,您尚未设置的任何属性都不会被序列化,或者会被序列化为空值。

And the same logic applies for reads: You populate a DTO with properties set conditionally based on their role. Later on when you return the DTO back to the client, any properties that you haven't set either won't be serialized or will be serialized with a null value.

最终,该解决方案使您可以为资源使用单个服务,而不是使用各自的请求DTO创建多个服务。当然,您可以进行重构,以使此解决方案更加简化。例如,您可以将所有读取和写入隔离到代码的一部分,这将使服务本身不受角色检查和类似的事情影响。

Ultimately, this solution allows you to use a single service for a resource instead of creating multiple services each with their own request DTO. There are, of course, refactorings you can do that makes this solution more streamlined. For example, you can isolate all of your reads and writes to one part of your code which will keep the services themselves free of role checks and things like that.

这篇关于在ServiceStack中进行属性级别授权的最佳方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆