laravel - Laravel:防止更改其他用户的项目
问题描述
我有三个模型。我想避免用户可以从属于其他用户的 todolists 更改待办事项。
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');
}
}
为了避免用户可以查看其他用户的 todolists 和 todo 项,我实现了以下内容:
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.');
}
这行得通,但这样做感觉非常错误。
实现用户可以查看/编辑/删除属于其他用户的项目的更好方法是什么?
解决方案
我建议您为您的 Todo 和 TodoList 模型使用策略以及将 todo 限制为一个用户的范围,以防止您的应用程序中出现重复代码:
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);
});
}
}
在评论中回答您的问题。
Q1:我必须添加
Auth::user()->can('view', $todolist);
一个 if-else 子句才能使其工作。猜猜这就是它的工作方式?
$this->authorize('edit', $todo)
Q2:和有什么区别Auth::user()->can('edit', $todo)
?
对不起,那是我这边的一个错误。Auth::user()->can()
返回一个布尔值,而$this->authorize()
(通常包含在 BaseController 中的 AuthorizesRequests 特征的一种方法)如果授权失败则抛出异常。
推荐阅读
- vue.js - Vue 对话框在激活/调用时执行
- java - 将音频从 java 服务器实时流式传输到 Android 客户端
- python - Python - 从json中提取数据的问题
- java - Java String 构造函数是如何实现的?
- xamarin.android - Xamarin.Android 资源文件中的新行字符
- javascript - 当 popover 指令起作用时,什么会导致 popover 组件失败
- javascript - 找不到 Discord.js 音乐机器人 ffmpeg?
- ios - 如何在不拖放的情况下将 tableview 控制器中的 tableview 行重新排序到指定的索引行?
- assembly - 汇编 - 访问浮点数组的第一个索引返回 0
- c# - 不区分大小写的重音devexpress过滤器gridview c#