您当前的位置: 首页 > 慢生活 > 程序人生 网站首页程序人生
权限与数据归属问题的几个方法(laravel11)
发布时间:2024-11-04 20:33:09编辑:雪饮阅读()
-
许多 Web 应用程序为其用户提供了一种使用应用程序进行身份验证和“登录”的方法。在 Web 应用程序中实现此功能可能是一项复杂且具有潜在风险的工作。因此,Laravel 致力于为您提供快速、安全、轻松地实施身份验证所需的工具。
Laravel 的身份验证工具的核心是由 “守卫” 和 “提供者” 组成的。守卫定义如何针对每个请求对用户进行身份验证。例如,Laravel 附带了一个守卫,它使用 session 存储和 cookie 来维护状态。session
提供程序定义如何从持久性存储中检索用户。Laravel 支持使用雄辩和 Database Query Builder 中。但是,您可以根据应用程序的需要自由定义其他提供程序。
应用程序的身份验证配置文件位于 。此文件包含几个有据可查的选项,用于调整 Laravel 身份验证服务的行为。config/auth.php
守卫和提供程序不应与 “roles” 和 “permissions” 混淆。要了解有关通过权限授权用户操作的更多信息,请参阅授权文档。
除了提供内置的认证服务中,Laravel 还提供了一种简单的方法来授权用户对给定资源的操作。例如,即使用户通过了身份验证,他们也可能无权更新或删除由您的应用程序管理的某些 Eloquent 模型或数据库记录。Laravel 的授权功能提供了一种简单、有序的方式来管理这些类型的授权检查。
Laravel 提供了两种主要的授权操作方式:盖茨和政策.将 gate 和策略视为 routes 和 controllers。门提供了一种简单的、基于闭包的授权方法,而策略(如控制器)则围绕特定模型或资源对逻辑进行分组。在本文档中,我们将首先探索 Gate,然后检查策略。
在构建应用程序时,您无需在专门使用 Gate 或专门使用 Policies 之间进行选择。大多数应用程序很可能包含一些 gate 和 policy 的混合,这完全没问题!门最适用于与任何模型或资源无关的操作,例如查看管理员控制面板。相反,当您希望为特定模型或资源授权操作时,应使用策略。
门是学习 Laravel 授权功能基础知识的好方法;但是,在构建健壮的 Laravel 应用程序时,您应该考虑使用政策来组织您的授权规则。
门只是确定用户是否有权执行给定操作的闭包。通常,门是使用 Facade 在类的方法中定义的。Gates 始终接收用户实例作为其第一个参数,并且可以选择接收其他参数,例如相关的 Eloquent 模型。bootApp\Providers\AppServiceProviderGate
在此示例中,我们将定义一个门来确定用户是否可以更新给定的模型。该 gate 将通过比较用户与创建帖子的用户的 IP 来实现此目的:App\Models\Postiduser_id
那么首先我们需要实现项目列表不仅仅可以显示当前用户的,也可以显示其他用户的,这样才能造成当前用户有可能操作其他用户的数据的情况。
那么项目列表界面index.vue中首先是加入一个切换按钮,可以切换所有用户或当前用户的。
<div>
<label id="listbox-label" class="block text-sm/6 font-medium text-gray-900">filter user</label>
<div class="relative mt-2">
<button type="button" class="relative w-full cursor-default rounded-md bg-white py-1.5 pl-3 pr-10 text-left text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 sm:text-sm/6" aria-haspopup="listbox" aria-expanded="true" aria-labelledby="listbox-label" @click="filterUserSwitch">
<span class="flex items-center">
<span class="ml-3 block truncate">{{getSelectedCategoryNameByChoiceUSer()}}</span>
</span>
<span class="pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2">
<svg class="h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" data-slot="icon">
<path fill-rule="evenodd" d="M10.53 3.47a.75.75 0 0 0-1.06 0L6.22 6.72a.75.75 0 0 0 1.06 1.06L10 5.06l2.72 2.72a.75.75 0 1 0 1.06-1.06l-3.25-3.25Zm-4.31 9.81 3.25 3.25a.75.75 0 0 0 1.06 0l3.25-3.25a.75.75 0 1 0-1.06-1.06L10 14.94l-2.72-2.72a.75.75 0 0 0-1.06 1.06Z" clip-rule="evenodd" />
</svg>
</span>
</button>
<ul class="absolute z-10 mt-1 max-h-56 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
<template v-if="state.showFilterUser">
<template v-for="(userItem,index) in datas.choiceUSerData">
<li :class="getMouseOverClassByFilterUser(index)" @mouseover="mouseOverFilterUser(index)" @mouseout="mouseOutFilter" id="listbox-option-0" role="option" @click="filterUserSelected(index)">
<div class="flex items-center">
<!-- Selected: "font-semibold", Not Selected: "font-normal" -->
<span class="ml-3 block truncate font-normal">{{userItem}}</span>
</div>
<span :class="getClassBySelectFilterUser(index)">
<template v-if="getFilterUSerIsActivated(index)">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" data-slot="icon">
<path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 0 1 .143 1.052l-8 10.5a.75.75 0 0 1-1.127.075l-4.5-4.5a.75.75 0 0 1 1.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 0 1 1.05-.143Z" clip-rule="evenodd" />
</svg>
</template>
</span>
</li>
</template>
</template>
</ul>
</div>
</div>
这里可以看到有调用了filterUserSwitch、getSelectedCategoryNameByChoiceUSer
、state.showFilterUser、datas.choiceUSerData、getMouseOverClassByFilterUser、mouseOverFilterUser、mouseOutFilter、filterUserSelected、getClassBySelectFilterUser、getFilterUSerIsActivated
那么这些的实现其实大多数和之前模态框中的下拉菜单逻辑一样。
过滤项目列表类型的下拉是否展开的实现
const filterUserSwitch=()=>{
state.showFilterUser=!state.showFilterUser;
}
const getSelectedCategoryNameByChoiceUSer=()=>{
for(var i in datas.choiceUSerData){
if(i==datas.choiceUSerDataSelectedIndex){
return datas.choiceUSerData[datas.choiceUSerDataSelectedIndex];
}
}
return datas.choiceUSerData[0];
}
const state = useForm({
state:false,
showCategory:false,
showFilterUser:false
});
将当前用户与所有用户作为数据源,并且包含鼠标悬停的默认索引
const datas=useForm({
choiceUSerData:['current user','all user'],
choiceUSerDataSelectedIndex:props.type,
mouseoverIndex:-1
});
const getMouseOverClassByFilterUser=(index)=>{
var strClass="relative cursor-default select-none py-2 pl-3 pr-9";
if(datas.mouseoverIndex==index){
strClass+=" bg-indigo-600 text-white";
}
if(datas.mouseoverIndex!=index){
strClass+=" text-gray-900";
}
return strClass;
}
const mouseOverFilterUser=(index)=>{
datas.mouseoverIndex=index;
}
const mouseOutFilter=()=>{
datas.mouseoverIndex=-1;
}
项目列表类型切换时就要从后台重新拉取数据
const filterUserSelected=(index)=>{
router.get(`/project/index`, {
type: index,
},{
onSuccess:()=>{
datas.choiceUSerDataSelectedIndex=index;
state.showFilterUser=false;
},
onError:(errors)=>{
console.log("获取项目列表切换后的数据出错errors",errors);
}
})
}
const getClassBySelectFilterUser=(index)=>{
var classStr="absolute inset-y-0 right-0 flex items-center pr-4";
if(index==datas.choiceUSerDataSelectedIndex){
classStr+=" text-indigo-600";
}
if(index!=datas.choiceUSerDataSelectedIndex){
classStr+=" text-white";
}
return classStr;
}
获取已选中的项目列表类型对于当前项目类型下拉是否匹配
const getFilterUSerIsActivated=(index)=>{
return datas.choiceUSerDataSelectedIndex==index;
}
可以看到我们这样实现了用户可以查看所有项目,所有用户的项目的可能性,所以此时如果是所有项目的情况下,就还需要在每个项目中标记项目所属。于是Project.vue单项目组件新增实现
<div>
<label>project user:</label><span class="text-gray-800">{{ project.user?.name }}</span>
</div>
基于以上两种情况,所以我们的项目控制器project里面的index方法的实现以及我想要无论是所有用户还是当前用户的情况下项目列表都是按最新数据进行优先排在前面,所以。
public function index(Request $request):Response
{
$type=$request->input("type",0);
if($type){
$projects=Project::with(["user","category"])->orderBy('id',"desc")->get();
}
if(!$type){
$projects=User::with([
"projects" => function ($query) {
$query->orderBy('id', 'desc');
},
"projects.user",
"projects.category"
])->find($request->user()->id)->projects;
}
$categories=project_category::get();
return Inertia::render('Project/Index', [
'projects' => $projects,
'categories'=>$categories,
'type'=>$type
]);
}
接下来才是权限的重头戏,我们就以注册第二个用户并创建一个项目,此时该用户去编辑第一个用户的某个项目并进行提交的场景为例子。
首先我们需要启动一个Gate服务提供者
Y:\root\example-app\app\Providers\ AppServiceProvider.php中boot方法实现如
public function boot(): void
{
Gate::define('update-post', function (User $user, Project $project) {
return $user->id === $project->uid;
});
Vite::prefetch(concurrency: 3);
}
这里就是定义了名为update-post的Gate,该Gate我们限定只能用户操作修改自己的项目,以用户id与项目中的uid进行对比。
这里别忘了Gate命名空间的导入
use App\Models\Project;
use App\Models\User;
use Illuminate\Support\Facades\Gate;
define的第一个参数就是用户实例,所以需要用户实例的mode,第二个对象就是进行限定的操作对象的实例,那么这里porject的model也是需要导入的。
另外补充下,刚才说是project.vue中要查看项目所属,所以Project.php模型中我们也是用了关联模型hasOne
public function user(): HasOne
{
return $this->hasOne(User::class,"id","uid");
}
那么Gate定义了,我们就要去使用Gate,并且因为我们是对比用户id和被操作的项目的uid,那么也就是说只能是在编辑的时候的限定,所以project控制器的update的实现如
public function update(ProjectUpdateRequest $request):RedirectResponse
{
$this->log(var_export($request->user(),true));
$this->log("uid:".$request->user()->id);
$id=$request->input("id");
$project=new Project();
if($id){
$project=Project::find($id);
}
if($id){
if (! Gate::allows('update-post', $project)) {
abort(403);
}
}
$file=$request->file('avatar');
if($file){
$path=$file->store('','public');
$url = Storage::url($path);
$project->avatar=$url;
}
if(!$file){
$project->avatar="/images/1.gif";
$avatar=$request->input("avatar");
if($avatar){
$project->avatar=$avatar;
}
}
$uid=$request->user()->id;
$name=$request->input("name");
$category=$request->input("category");
$project->uid=$uid;
$project->name=$name;
$project->category=$category;
$project->save();
return Redirect::route('project.index');
}
那最后就是我们用用户2去操作用户1的数据编辑进行提交时的效果了
关键字词:laravel
相关文章
- laravel11编辑项目的数据验证逻辑、同一页面多表单错
- 编辑保存项目的逻辑(laravel里的两种数据绑定)
- 编辑项目的模态框样式(laravel11)
- unique rule的进一步限定与项目分类悬浮状态条效果实
- 新建项目的数据验证(基于laravel11+inertia的数据验证
- 新建项目表单的模态框样式(laravel11+inertia+breeze
- laravel中的一对多关系使用与inertia的link使用
- 列出已有项目分类(laravel11+breeze+inertia实现增与
- 图片上传保存逻辑(laravel11+breeze+inertia+vue3实现
- 更改数据结构的两种方法(laravel的migrate)