Java 限制对DDD中对象所有者的访问

Java 限制对DDD中对象所有者的访问,java,security,domain-driven-design,authorization,Java,Security,Domain Driven Design,Authorization,假设有一个对象TaskList,它只能由其所有者编辑和删除。其他用户只能通过执行任务并更新其状态来完成任务 我想到了以下几种选择: 检查web应用程序控制器中的所有权和访问权 让存储库返回在某些操作上引发异常的代理对象,但控制器(或视图)仍需要知道哪些操作(以链接或表单字段的形式)应该可见 将调用者(用户)传递给域对象的方法,以便域对象本身可以检查调用者是否被允许 使用的技术是Java 还有其他/更好的主意吗 关于安全性和DDD的有趣文章 使用Spring框架 我现在接受了我自己的答案

假设有一个对象
TaskList
,它只能由其所有者编辑和删除。其他用户只能通过执行任务并更新其状态来完成任务

我想到了以下几种选择:

  • 检查web应用程序控制器中的所有权和访问权
  • 让存储库返回在某些操作上引发异常的代理对象,但控制器(或视图)仍需要知道哪些操作(以链接或表单字段的形式)应该可见
  • 将调用者(用户)传递给域对象的方法,以便域对象本身可以检查调用者是否被允许
使用的技术是Java

还有其他/更好的主意吗

关于安全性和DDD的有趣文章

  • 使用Spring框架

我现在接受了我自己的答案,因为这是我实际使用的答案,但欢迎进一步的建议

我不会将所有权/权限模型编码到
任务列表
域对象中。这种业务逻辑应该是外部的。我也不喜欢代理对象的概念。虽然它肯定会工作,但它会混淆调试,并且在本例中,至少是不必要的复杂。我也不会在控制器中检查它

相反,我将创建一个业务逻辑对象,用于监督
TaskList
的权限。因此,
TaskList
将有一个owner字段,但您将有如下内容:

public class TaskListAccessor {
    private TaskList taskList;
    private User reader;

    public void updateStatus(Status status) {
        // everyone can do this
        taskList.updateStatus(status);
    }

    /** Return true if delete operation is allowed else false */
    public boolean isDeleteAllowed() {
        return taskList.getOwner().equals(reader);
    }

    /** Delete the task.  Only owners can do this.  Returns true if worked else false */
    public boolean delete() {
        if (isDeleteAllowed()) {
           taskList.delete();
           return true;
        } else {
           return false;
        }
    }
    // ... other accessors with other is*Allowed methods
}
如果需要要求
TaskList
对象上的所有操作都通过访问器进行,则可以创建一个factory类,该类是唯一使用包构造函数或其他方法创建
TaskList
的类。也许工厂是唯一一个使用DAO从数据存储中查找任务列表的人


然而,如果有太多的方法以这种方式控制,那么代理可能更容易。在这两种情况下,建议使用
TaskList
作为接口,实现类由代理或访问器隐藏。

我发现为每个受保护的域类创建访问器类(如“Gray”建议的)不必要地复杂。我的解决方案可能并不完美,但简单易用,更重要的是健壮。您不能忘记使用某个对象或检查外部条件

public class TaskList {

    private SystemUser owner;
    private List<Task> tasks = new ArrayList<>();

    public TastList(SystemUser owner) {
        this.owner = owner;
    }

    public void Add(Task task) {
        Guard.allowFor(owner); 
        tasks.add(task);
    }
}
公共类任务列表{
私有系统用户所有者;
私有列表任务=新建ArrayList();
公共用户列表(系统用户所有者){
this.owner=所有者;
}
公共作废添加(任务){
(业主)的保安许可证;
任务。添加(任务);
}
}
Guard
知道当前用户(例如从本地线程),并将其与作为参数传递给
allowFor(owner)
的所有者进行比较。如果拒绝访问,将引发安全异常

这是一种简单、健壮甚至易于维护的方法,因为如果底层身份验证发生更改,则只需更改保护