Laravel:防止更改其他用户的项目

时间:2020-03-30 11:04:52

标签: laravel

我有三个模型。我想避免用户可以更改属于其他用户的Todo的待办事项。

class User extends Authenticatable
{
    public function todolists()
    {
        return $this->hasMany('App\Todolist');
    }

    public function todos()
    {
        return $this->hasManyThrough('App\Todo', 'App\Todolist');
    }
}

class Todolist extends Model
{
    public function user()
    {
        return $this->belongsTo('App\User');
    }

    public function todos()
    {
        return $this->hasMany('App\Todo');
    }
}

class Todo extends Model
{
    protected $casts = [
        'completed' => 'boolean',
    ];

    public function todolist()
    {
        return $this->belongsTo('App\Todolist');
    }
}

为避免用户可以查看其他用户的托儿和待办事项,我实现了以下内容:

 public function getTodosForTodolist(Todolist $todolist)
    {
        if (Auth::user()->id == $todolist->user_id) {
            $todos = Todo::where('todolist_id', $todolist->id )->get();
            return view('todo/index', ['todos' => $todos); 
        }
        else {
            abort(403, 'Unauthorized action.');
        }
    }

下一步是防止用户可以编辑其他用户的待办事项。当前在TodoController中,我只有以下内容:

public function edit(Todo $todo)
        {
         if (Auth::user()->todos->id == $todo->todolist->id) {
            return view('todo/edit', ['todo' => $todo]);
         }
    } 

这会出现以下错误:

此集合实例上不存在属性[id]。

错误是因为当前用户有多个待办事项。所以我如下更改了代码。

public function edit(Todo $todo)
    {
        if (Auth::user()->todos->first()->id == $todo->todolist->id) {
            return view('todo/edit', ['todo' => $todo]);
        }
        abort('403', 'Unauthorized action.'); 
}

这可行,但是这样做实在是非常错误的。

哪种更好的方法来实现用户可以查看/编辑/删除属于其他用户的项目?

2 个答案:

答案 0 :(得分:2)

我建议您将policies用于Todo和TodoList模型,并使用范围将dodo限制为一个用户,以防止应用程序中重复代码。

class ToDoListPolicy
{
    public function view(User $user, TodoList $post)
    {
        return $user->id === $todolist->user_id;
    }
}

class ToDoPolicy
{
    public function edit(User $user, Todo $toDo)
    {
        $toDo->loadMissing('todolist');
        
        return $user->id === $toDo->todolist->user_id;
    }
}

在您的 AuthServiceProvider.php

中注册它们
class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
        TodoList::class => ToDoListPolicy::class,
        Todo::class => ToDoPolicy::class
    ];
}

然后在您的操作中使用它们:

public function getTodosForTodolist(Todolist $toDoList)
{
    $this->authorize('view', $toDoList);
    
    $toDoList->loadMissing('todos');
    
    return view('todo.index', ['todos' => $toDoList->todos); 
}

class ToDoController extends Controller
{
    public function edit(Todo $toDo)
    {
        $this->authorize('edit', $toDo);
        
        return view('todo.edit', compact('toDo'));
    } 
}

以及将查询限制为特定用户的范围:

class Todo extends Model {
    // ...
    
    public function scopeByUser(Builder $query, ?User $user = null)
    {
        if (! $user) {
            $user = Auth::user();
        }
        
        $query->whereHas('todolist', function (Builder $toDoListQuery) use ($user) {
            $toDoListQuery->where('user_id', $user->id);
        });
    }
}

在评论中回答your questions

问题1:我必须将Auth::user()->can('view', $todolist);放在if-else子句中才能起作用。猜猜这是它的工作方式吗?

第二季度:$this->authorize('edit', $todo)Auth::user()->can('edit', $todo)有什么区别?

对不起,这是我的错误。如果授权失败,则Auth::user()->can()返回一个布尔值,而$this->authorize()(这是通常包含在BaseController中的AuthorizesRequests特性的一种方法)throws an exception

答案 1 :(得分:1)

如果要让每个用户只能使用自己的Todo,则需要添加Global Scope。此实现将使您的应用程序感到Todos(除了已登录用户之外的其他用户)不存在。

Global Scope可用于许多模型,这意味着它将减少样板代码。

https://laravel.com/docs/7.x/eloquent#global-scopes