پیاده سازی کنترل دسترسی مبتنی بر نقش در SvelteKit

فهرست مطالب
پیش نیازها
قبل از شروع، باید پیش نیازهای زیر را داشته باشید:
- دانش اولیه SvelteKit: آشنایی با مفاهیم اصلی SvelteKit، مانند مسیریابی، کامپوننت ها و فروشگاه ها، مفید خواهد بود.
- Node.js و npm: باید Node.js و npm (Node Package Manager) را روی سیستم خود نصب کنید. اگر قبلاً این کار را نکرده اید، می توانید آنها را از وب سایت رسمی Node.js دانلود و نصب کنید.
- Git: مطمئن شوید که Git را روی دستگاه خود نصب کرده اید. اگر نه، می توانید آن را از وب سایت Git دانلود کنید.
آشنایی با کنترل دسترسی مبتنی بر نقش (RBAC)
RBAC یک مدل امنیتی است که مجوزهای کاربر را بر اساس نقش آنها در یک برنامه سازماندهی می کند.
هر نقش با مجموعهای از مجوزها مرتبط است و به کاربران نقشهای خاصی اختصاص داده میشود تا مشخص کنند به چه چیزی میتوانند دسترسی داشته باشند.
نقش ها و مجوزها
نقشها مجموعهای از مجوزهای مرتبط را نشان میدهند که مشخص میکنند کاربر چه کاری میتواند در یک برنامه انجام دهد.
به عنوان مثال، یک برنامه کاربردی ممکن است نقش هایی مانند Admin
، Editor
، و Viewer
هر کدام با مجموعه های مختلفی از مجوزها.
از سوی دیگر، مجوزها، اقدامات یا عملیات خاصی هستند که یک کاربر با یک نقش خاص می تواند انجام دهد.
این موارد می تواند از مشاهده و ویرایش داده ها تا مدیریت حساب های کاربری یا پیکربندی های سیستم را شامل شود.
راه اندازی پروژه SvelteKit
قبل از شروع پیاده سازی RBAC، به یک پروژه SvelteKit آماده کار با آن نیاز داریم.
برای ساده نگه داشتن کارها، از یک پروژه تجارت الکترونیکی آزمایشی به عنوان الگوی شروع خود استفاده خواهیم کرد. کد کامل این پروژه را می توانید در این مخزن GitHub پیدا کنید.
برای دریافت قالب استارتر، دستورات زیر را اجرا کنید:
git clone https://github.com/TropicolX/svelte-rbac-demo.git
cd svelte-rbac-demo
git checkout starter
npm install
ساختار پروژه باید مانند شکل زیر باشد:
در مرحله بعد، اجازه دهید به فایلها و دایرکتوریهای ضروری که برای پروژه با آنها کار میکنیم، بپردازیم:
-
/lib
: این فهرست شامل تمام ابزارها و مؤلفه های ما خواهد بود. -
/routes
: شامل تمام مسیرهای موجود در برنامه ما است.-
/(protected)
: این پوشه حاوی تمام مسیرهای محافظت شده ما است. فقط کاربرانی که وارد سیستم شده اند ممکن است بتوانند در این مسیرها پیمایش کنند. -
/login
: این مسیر به صفحه ورود است. -
/unauthorized
: این مسیری است که کاربران را هنگامی که دسترسی به یک صفحه غیرمجاز ندارند به آن هدایت می کنیم. -
+layout.svelte
: این شامل طرح UI و منطق است که برای هر صفحه اعمال می شود. -
+page.svelte
: این مسیر صفحه اصلی ما است.
-
-
hooks.server.js
: این فایل حاوی تمام منطق سمت سرور است که برای احراز هویت کاربران در هنگام رفتن به یک مسیر محافظت شده و هدایت آنها در صورت لزوم نیاز داریم. همچنین داده های کاربر را روی سرور واکشی می کند و در سرور ذخیره می کندevent.locals
هدف – شی.
بررسی اجمالی پروژه
برای شروع پروژه، دستور زیر را در پوشه اصلی اجرا کنید:
npm run dev
شما باید صفحه ای مانند صفحه زیر را ببینید:
بعد، بیایید وارد برنامه شویم. روی دکمه “ورود به سیستم” در گوشه سمت راست بالای صفحه کلیک کنید.
میتوانید بهعنوان مدیر یا مشتری با استفاده از اطلاعات کاربری زیر وارد سیستم شوید:
-
مدیر:
-
پست الکترونیک:
admin@gmail.com
-
کلمه عبور:
admin123
-
پست الکترونیک:
-
مشتری:
-
پست الکترونیک:
john@mail.com
-
کلمه عبور:
changeme
-
پست الکترونیک:
پس از ورود به سیستم، به صفحه اصلی هدایت می شوید و می توانید دو پیوند جدید را در نوار ناوبری مشاهده کنید:
این ما را به مشکل فعلی برنامه تجارت الکترونیکی ما میرساند: باید اطمینان حاصل کنیم که فقط ادمینها میتوانند به صفحه مدیریت دسترسی داشته باشند و به همین ترتیب، فقط مشتریان میتوانند به صفحه نمایه دسترسی داشته باشند.
علاوه بر این، ما باید اطمینان حاصل کنیم که فقط مدیران و مشتریان با مجوزهای لازم می توانند اقدامات خاصی را در صفحات مربوطه خود انجام دهند.
به عنوان مثال، فقط یک مدیر با مجوز ایجاد محصولات باید بتواند این کار را در صفحه مدیریت انجام دهد.
یکپارچه سازی تنظیمات و ابزارهای RBAC
اکنون که پروژه Svelekit را راه اندازی کرده و با آن آشنا شده ایم، اجازه دهید پیاده سازی RBAC را در برنامه خود آغاز کنیم. ما با تعریف نقش ها و مجوزها در برنامه شروع می کنیم.
تعریف نقش ها و مجوزها
در پروژه SvelteKit یک فایل جدید به نام ایجاد کنید constants.js
در src
دایرکتوری و کد زیر را اضافه کنید:
// src/constants.js
export const ROLES = {
ADMIN: "admin",
CUSTOMER: "customer",
};
export const PERMISSIONS = {
CREATE_PRODUCTS: "create:products",
UPDATE_PRODUCTS: "update:products",
DELETE_PRODUCTS: "delete:products",
READ_PROFILE: "read:profile",
UPDATE_PROFILE: "update:profile",
};
در کد بالا، نقشها و ثابتهای مجوز برنامه را تعریف کردیم.
در مرحله بعد، باید مجوزهای هر نقش را تعریف کنیم. حرکت به lib
دایرکتوری و ایجاد یک rolePermissions.js
فایل با کد زیر:
// src/lib/rolePermissions.js
import { PERMISSIONS, ROLES } from "../constants";
export const rolePermissions = {
[ROLES.ADMIN]: [
PERMISSIONS.CREATE_PRODUCTS,
PERMISSIONS.UPDATE_PRODUCTS,
PERMISSIONS.DELETE_PRODUCTS,
],
[ROLES.CUSTOMER]: [PERMISSIONS.READ_PROFILE, PERMISSIONS.UPDATE_PROFILE],
};
کد بالا هر یک از نقش ها را با مجوزهای خاص تنظیم می کند. ما می خواهیم که کاربران ادمین بتوانند محصولات را ایجاد، به روز رسانی و حذف کنند.
از سوی دیگر، ما می خواهیم مشتریان بتوانند جزئیات پروفایل خود را بخوانند و به روز کنند.
پیاده سازی توابع سودمند RBAC
در مرحله بعد، اجازه دهید برخی از توابع کاربردی ایجاد کنیم تا بررسی کنیم آیا یک کاربر دارای نقش یا مجوز خاصی است یا خیر. این توابع کنترل دسترسی را در سراسر برنامه اعمال می کنند.
در lib
دایرکتوری، ایجاد یک rbacUtils.js
فایل و کد زیر را اضافه کنید:
// src/lib/rbacUtils.js
import { rolePermissions } from "./rolePermissions";
// Function to check if a user has a specific role
export function checkRole(user, requiredRole) {
if (!user) {
return false;
}
return user.role === requiredRole;
}
// Function to check if a user has specific permissions
export function checkPermissions(user, requiredPermissions) {
if (!user) {
return false;
}
const userPermissions = rolePermissions[user.role];
return userPermissions?.includes(requiredPermissions);
}
// Function to check if a user has both a specific role and permissions
export function checkRoleAndPermissions(
user,
requiredRole,
requiredPermissions
) {
return (
checkRole(user, requiredRole) &&
checkPermissions(user, requiredPermissions)
);
}
با وجود این پیکربندیها، میتوانیم RBAC را در برنامه خود ادغام کنیم.
ناوبری پویا بر اساس نقش های کاربر
رابط کاربری ناوبری اولین ناحیه ای است که در برنامه ما RBAC را پیاده سازی می کنیم. ما باید اطمینان حاصل کنیم که کاربران فقط پیوندهای ناوبری مربوط به نقش خود را می بینند.
ابتدا به سمت +layout.svelte
فایل در routes
پوشه و کد زیر را اضافه کنید:
<script>
...
import Loading from "$lib/components/Loading.svelte";
import { checkRole } from "$lib/rbacUtils";
import { ROLES } from "../constants";
import { user, cart, products } from "../stores";
import "../app.css";
...
$: isAdmin = checkRole($user, ROLES.ADMIN);
$: isCustomer = checkRole($user, ROLES.CUSTOMER);
</script>
...
در اینجا دو متغیر تعریف کردیم isAdmin
و isCustomer
، که به ترتیب بررسی می کند که آیا کاربر مدیر یا مشتری است. ما همچنین از اعلانهای واکنشی برای تعریف متغیرها استفاده کردیم تا هرگونه تغییر در دادههای کاربر، به عنوان مثال، اگر کاربر از سیستم خارج شود، مقادیر آنها را بهروزرسانی کند.
در مرحله بعد، بیایید پیوندهای ناوبری خود را به روز کنیم:
...
<header
class="bg-gray-900 text-white py-4 px-6 md:px-12 flex items-center justify-between"
>
...
<nav class="hidden md:flex items-center space-x-6">
{#if isAdmin}
<a class="hover:text-gray-300" href="/admin">Admin</a>
{/if}
{#if isCustomer}
<a class="hover:text-gray-300" href="/user">Profile</a>
{/if}
</nav>
...
</header>
...
اکنون، لینکهای پیمایش برای صفحههای ادمین و نمایه به صورت مشروط بر اساس نقش کاربر نمایش داده میشوند.
ایمن سازی مسیرها بر اساس نقش های کاربر
در بخش قبل، ناوبری سمت مشتری پویا را بر اساس نقش های کاربر اضافه کردیم. با این حال، کاربران همچنان می توانند با استفاده از ناوبری مرورگر به مسیرهای غیرمجاز حرکت کنند. برای جلوگیری از این امر، باید مستقیماً با افزودن حفاظت مبتنی بر نقش، مسیرها را ایمن کنیم.
ما می توانیم این کار را در Svelte با اضافه کردن a انجام دهیم +layout.server.js
فایل در هر مسیر برای اجرای سمت سرور load
تابع. تابع بارگذاری هر زمان که کاربر صفحه را بارگیری کند اجرا می شود و بررسی می کند که آیا کاربر بر اساس نقش خود به مسیر دسترسی دارد یا خیر. اگر آنها مجاز نباشند، به آن هدایت می شوند /unauthorized
صفحه
بیایید با پیاده سازی این در صفحه مدیریت شروع کنیم. سر به سمت admin
دایرکتوری در (protected)
پوشه، ایجاد یک +layout.server.js
فایل با کد زیر:
// src/routes/(protected)/admin/+layout.server.js
import { redirect } from "@sveltejs/kit";
import { checkRole } from "$lib/rbacUtils";
import { ROLES } from "../../../constants";
/** @type {import('./$types').LayoutServerLoad} */
export function load({ locals }) {
const user = locals.user;
const isAdmin = checkRole(user, ROLES.ADMIN);
if (!isAdmin) {
redirect(307, "/unauthorized");
}
}
حال، اگر یک کاربر غیر ادمین سعی کند به مسیر مدیریت دسترسی پیدا کند، به درستی هدایت می شود.
بعد، بیایید خود را ایمن کنیم user
مسیر در داخل (protected)
پوشه، به user
دایرکتوری و ایجاد یک +layout.server.js
فایل با کد زیر:
// src/routes/(protected)/user/+layout.server.js
import { redirect } from "@sveltejs/kit";
import { checkRoleAndPermissions } from "$lib/rbacUtils";
import { PERMISSIONS, ROLES } from "../../../constants";
/** @type {import('./$types').LayoutServerLoad} */
export function load({ locals }) {
const user = locals.user;
const isCustomerAndCanViewProfile = checkRoleAndPermissions(
user,
ROLES.CUSTOMER,
PERMISSIONS.READ_PROFILE
);
if (!isCustomerAndCanViewProfile) {
redirect(307, "/unauthorized");
}
}
در اینجا، ما با استفاده از آن کارها را کمی متفاوت انجام می دهیم checkRoleAndPermissions
عملکردی برای اطمینان از اینکه کاربر هم مشتری است و هم اجازه دارد یک نمایه را قبل از دسترسی به صفحه نمایه بخواند.
این تابع می تواند در سناریوهای پیچیده تری که نقشی ممکن است دارای مجوزهای اضافی یا سفارشی متفاوت از مجوزهای پیش فرض باشد، مفید باشد.
در ادامه به نحوه مدیریت مجوزهای نقش در کامپوننت ها می پردازیم.
مدیریت مجوزها در کامپوننت ها
اکنون که مسیرهای خود را ایمن کردهایم، قدم بعدی محافظت از اقدامات و ویژگیهای موجود در آن صفحات است تا اطمینان حاصل شود که تنها کاربرانی که مجوزهای لازم را دارند میتوانند به آنها دسترسی داشته باشند.
به عنوان مثال، می توانید سه اقدام اصلی را در صفحه مدیریت انجام دهید:
- یک محصول ایجاد کنید
- یک محصول را ویرایش کنید
- حذف یک محصول
در حال حاضر، یک ادمین به صورت پیشفرض اجازه انجام تمام این اقدامات را دارد. اما فرض کنید بعداً در توسعه، تغییری برای لغو آن ایجاد شد create:products
اجازه از نقش مدیر پیش فرض و تنها به چند مدیر منتخب داده می شود.
در آن صورت، اطمینان از اینکه فقط آن ادمینهای خاص میتوانند محصولی را ایجاد کنند، بسیار مهم است.
ما می توانیم مجوزهای پروژه خود را با استفاده از checkPermissions
تابع سودمند
به عنوان مثال، در +page.svelte
فایل در admin
دایرکتوری، ما می توانیم کد زیر را اضافه کنیم تا مطمئن شویم که فقط مدیران با آن create:product
مجوز می تواند به دکمه “افزودن محصول جدید” دسترسی داشته باشد:
<script>
import { checkPermissions } from "$lib/rbacUtils";
import { PERMISSIONS } from "../../../constants";
import { user, products } from "../../../stores";
$: canCreateProducts = checkPermissions($user, PERMISSIONS.CREATE_PRODUCTS);
</script>
<div class="container mx-auto px-4 md:px-8 py-12">
<div class="flex flex-wrap items-center justify-between mb-8">
<h2 class="text-3xl md:text-4xl font-bold">Admin Dashboard</h2>
{#if canCreateProducts}
<button
class="mt-4 min-[457px]:mt-0 inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 h-10 px-4 py-2 bg-gray-900 text-white hover:bg-gray-800"
>
Add new product
</button>
{/if}
</div>
...
</div>
اگر حذف کنیم PERMISSIONS.CREATE_PRODUCTS
از مجوزهای مدیریت در rolePermissions.js
فایل، میتوانیم اعمال تغییر را مشاهده کنیم:
// src/lib/rolePermissions.js
...
export const rolePermissions = {
[ROLES.ADMIN]: [
PERMISSIONS.UPDATE_PRODUCTS,
PERMISSIONS.DELETE_PRODUCTS,
],
...
};
بیایید با ایمن کردن ویژگی ویرایش و حذف نیز یک قدم جلوتر برویم:
<script>
...
$: canDeleteProducts = checkPermissions($user, PERMISSIONS.DELETE_PRODUCTS);
$: canUpdateProducts = checkPermissions($user, PERMISSIONS.UPDATE_PRODUCTS);
</script>
...
<tbody class="[&_tr:last-child]:border-0">
{#each $products as product (product.id)}
<tr
class="border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted"
>
...
<td class="p-4 align-middle [&:has([role=checkbox])]:pr-0">
<div class="flex items-center space-x-2">
{#if canUpdateProducts}
<button
class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 w-10"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="h-4 w-4"
>
<path
d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"
></path>
</svg>
</button>
{/if}
{#if canDeleteProducts}
<button
class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 w-10"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="h-4 w-4"
>
<path d="M3 6h18"></path>
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"
></path>
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"
></path>
</svg>
</button>
{/if}
</div>
</td>
</tr>
{/each}
</tbody>
...
در مرحله بعد، بیایید همین سیاست را در صفحه نمایه خود اجرا کنیم. حرکت به +page.svelte
فایل در user
دایرکتوری و کد زیر را اضافه کنید:
<script>
import { checkPermissions } from "$lib/rbacUtils";
import { PERMISSIONS } from "../../../constants";
import { user } from "../../../stores";
$: canEditProfile = checkPermissions($user, PERMISSIONS.UPDATE_PROFILE);
let name = $user.name;
let email = $user.email;
</script>
<div class="h-[calc(100svh-64px)] w-full flex items-center justify-center">
<div
class="rounded-lg border bg-card text-card-foreground shadow-sm w-[28rem] max-w-md"
>
...
<div class="p-6 space-y-4">
<div class="space-y-2">
...
<input
...
disabled={!canEditProfile}
/>
</div>
<div class="space-y-2">
...
<input
...
disabled={!canEditProfile}
/>
</div>
<div class="space-y-2">
...
<input
...
disabled={!canEditProfile}
/>
</div>
<div class="space-y-2">
...
<input
...
disabled={!canEditProfile}
/>
</div>
</div>
<div class="flex items-center p-6">
<button
...
disabled={!canEditProfile}
>
Save
</button>
</div>
</div>
</div>
در اینجا، ما از canEditProfile
متغیر برای تعیین اینکه آیا فیلدهای ورودی و دکمه ذخیره غیرفعال می شوند یا خیر. اگر مقدار باشد false
، فیلدهای ورودی و دکمه ارسال غیرفعال می شود و بالعکس.
و با آن، ما باید یک برنامه وب تجارت الکترونیک کاملاً ایمن با استفاده از RBAC داشته باشیم!
نتیجه
در این آموزش، نحوه پیاده سازی Role-Based Access Control (RBAC) در برنامه های Sveltekit را بررسی کردیم.
ما با یادگیری در مورد اصول RBAC، مانند نقش ها و مجوزها و اهمیت آنها در توسعه وب، شروع کردیم.
سپس اجرای آن را مورد بررسی قرار دادیم که شامل پیکربندی نقشها و مجوزهای سفارشی، یکپارچهسازی ابزارهای RBAC، ایمنسازی مسیرها و مدیریت مجوزها در اجزا بود.
همانطور که به توسعه برنامه های SvelteKit خود ادامه می دهید، به یاد داشته باشید که به طور منظم پیکربندی RBAC خود را برای انطباق با نیازهای در حال تغییر و کاهش خطرات امنیتی بالقوه مرور و به روز کنید.
علاوه بر این، برای حفظ یکپارچگی و اثربخشی اجرای RBAC خود، نظارت مستمر، ثبت و آزمایش را در اولویت قرار دهید.
شما می توانید کد پروژه کامل را در این Repo GitHub پیدا کنید.