تأمين تطبيقك يختلف عن تمكين أو رفض الوصول فقط عند السطح. وكممارس ، يتوجب عليك تطبيق التفويض الشديد التقني
(FGA) لإدارة الصلاحيات على مستوى أكثر تفاصيل والتي تحدد من يمكنه القيام بما يتعلق به وتحديد الظروف التي يتم بموجبها القيام بذلك.
يسمح لك FGA بإنشاء قواعد تحكم في الوصول التفصيلية التي تحدد من يمكنه القيام بما يتعلق به وتحديد الظروف التي يتم بموجبها القيام بذلك.
ستتعلم في هذا التورية كيفية تطبيق التفويض الشديد التقني
في الجافا والإنتاج السريع باستخدام Permit.io.
هذه هي المصادر الكودية (تذكر أن عليك أن تضع نجمة 🌟 لها).
أتمنى أن تستمتع بما أنا كتبت في المدونة السابقة حول بناء تطبيق للمحادثات الفيديوية الخاصة باستخدام Stream و Next.js. تلك المدونات تعكس رحلتي في إنشاء DevTools Academy، منصة تهدف إلى مساعدة المطورين على اكتشاف أدوات المطورين الرائعة.
هذه التورية تحمل جهود أخرى لتقديمك إلى أداة المطور المفيدة جدًا التي استكشفتها مؤخرًا.
جدول محتويات:
ما هو التفويض؟
Permit.io هي حلول تخصيص متقدم وقابلة للاستخدام للتفويض في المستويات التطبيقية تسمح لك بتنفيذ
أمن
,قابلية تنظيم
,تفويض
بعد دقائق فقط ، لذا يمكنك تركيز على ما يهم أكثر.
الأحداث السابقة
للتعلم بشكل كامل عن الدرس التعليمي ، يجب أن يكون لديك فهم أساسي ل Java
و Spring Boot
. سوف يحتاج أيضًا إلى التالي:
- Permit.io: أداة للمطورين تبسم التنفيذ للتفويض التعلقي بالفعل.
-
Spring Boot Starter Web: يوفر مكونات أساسية لبناء تطبيقات ويشمل متواليات RESTful.
-
Gradle: أداة بناء لإدارة ال依存ين.
-
JDK 11 أو أحدث: تشمل إحتياج إلى نسخة من المجموعة التطويرية للجافا لتتمكن من تجميع وتشغيل تطبيقك المتوازي.
-
Postman أو cURL: أدوات لاختبار نقاط البوابة
API
الخاص بك.
ما هو التأكيد الدقيق الصلاحياتي؟
التأكيد الدقيق الصلاحياتي يوفر السيطرة على الموارد بتحديد من يمكن أن يتمكن من وصول الىها وإلى أي حد وتحت أشرطة معينة.
على عكس التفويض الخشن (الذي يتعامل مع الوصول بناءً على فئات مثل أدوار المستخدم
مثل “مسؤول
” أو “مستخدم
“)، يمنحك التفويض الدقيق المرونة لتحديد الوصول على مستوى تفصيلي، لموارد أو إجراءات محددة وحتى السمات.
في التفويض الدقيق
توجد 3 أنواع من نماذج السياسات لإدارة التفويض؛ التحكم في الوصول القائم على الأدوار (RBAC)، التحكم في الوصول القائم على السمات (ABAC)، والتحكم في الوصول القائم على العلاقات (ReBAC).
دعونا نلقي نظرة على كل من هذه النهج ونرى كيف يمكنك تنفيذها في تطبيقك.
التحكم في الوصول القائم على الأدوار (RBAC)
RBAC هو نهج أمني يتحكم في الوصول إلى الموارد بناءً على أدوار المستخدمين داخل المؤسسة. يعمل هذا النموذج على تبسيط الأذونات من خلال تنظيم المستخدمين في أدوار وإدارة التحكم في الوصول وفقًا لهذه الأدوار المحددة.
المفاهيم الرئيسية في RBAC:
المستخدمون: الأشخاص الذين يستخدمون النظام مثل الموظفين أو العملاء.
الأدوار: مجموعة من الأذونات أو امتيازات الوصول المخصصة لمجموعة من المستخدمين بناءً على مسؤولياتهم أو مهامهم مثل المسؤول أو المدير أو العميل.
الأذونات: الحقوق الممنوحة للمستخدمين للتفاعل مع الموارد، مثل القراءة أو الكتابة أو الحذف.
التحكم في الوصول القائم على السمات (ABAC)
ABAC هو نموذج تحكم في الوصول مرن ومتكيف يقرر من يمكنه أو لا يمكنه الوصول إلى الموارد بناءً على السمات، مثل تفاصيل المستخدم. يسمح لك نموذج ABAC بتحديد تفويض دقيق بناءً على سمات المستخدم.
المفاهيم الرئيسية في ABAC:
السمات: الخصائص أو الخصائص المستخدمة لاتخاذ قرارات التحكم في الوصول. عادةً ما يتم تصنيف السمات إلى:
-
سمات المستخدم: معلومات عن المستخدم (على سبيل المثال، الدور، والقسم، وظيفة، العمر، إلخ).
-
سمات الموارد: خصائص الموارد (على سبيل المثال، نوع الملف، مستوى تصنيف البيانات، تاريخ الإنشاء، المالك).
-
سمات الإجراء: الإجراء الذي يحاول المستخدم القيام به (على سبيل المثال، قراءة، كتابة، حذف، موافقة).
- سمات البيئة: معلومات سياقية حول طلب الوصول (على سبيل المثال، الوقت من اليوم، الموقع، نوع الجهاز، عنوان IP).
تحكم الوصول بالاعتماد على العلاقات (ReBAC)
ReBAC هو نظام تحكم في الوصول يمنح الأذونات للوصول إلى الموارد بناءً على العلاقة بين الكيانات داخل النظام. يركز النهج على تعريف وإدارة تحكم الوصول عن طريق رسم خرائط حول كيفية ارتباط المستخدمين بالموارد والكيانات الأخرى مثل المنظمات أو المجموعات.
المفاهيم الرئيسية ل ReBAC:
الكيانات: المستخدمون ، والموارد (مثل الملفات والوثائق) ، والكيانات الأخرى ، مثل المجموعات أو الوحدات التنظيمية.
العلاقات: الاتصالات التي تحدد العلاقة بين كيانين. قد يكون المستخدم “المالك” للوثيقة أو “عضوًا” في الفريق ، على سبيل المثال.
السياسات: القواعد التي تستخدم العلاقات لتحديد حقوق الوصول. يمكن للمستخدم الوصول إلى مورد أو تنفيذ إجراء عليه إذا كان لديه علاقة معينة معه.
كيفية تنفيذ تفويض دقيق
الآن بعد أن أصبحت على دراية أساسية بـ RBAC
و ABAC
و ReBAC
، دعونا نرى كيف يمكننا تنفيذ هذه النماذج في تطبيق تجاري إلكتروني.
تنفيذ تحكم الوصول المبني على الأدوار
الخطوة 1: انتقل إلى Permit.io ، ثم أنشئ حسابك وفراغ عملك.
بشكل افتراضي، سيتم عرض مشروع يشمل عدة بيئات: تطوير
و تنفيذ
.
خطوة 2: أنشئ مورد يسمى منتجات. لإنشاء المورد، قم بفتح لسان الجانب الأيسر ومن ثم فتح لسان السياسات ومن ثم قم بالنقر على زر إنشاء مورد ومن ثم أنشئ مورد يدعى منتجات بالإجراءات قراءة
، إنشاء
، تعديل
، و حذف
.
خطوة 3: أنشئ مورد آخر يدعى تقييمات بالإجراءات قراءة
، إنشاء
، تعديل
، و حذف
.
خطوة 4: فتح لسان محرر السياسات. سوف ترى أن تم إنشاء 3 roles يدعى admin
، editor
، و viewer
.
-
تم تمكين ال role
admin
لـإنشاء
،حذف
،قراءة
أوتعديل
منتج أو مراجعة. - تم تمكين ال role
editor
لـإنشاء
،قراءة
أوتعديل
منتج أو مراجعة ولكن لا يمكنه الحذف أي شيء. -
دور
المشاهد
لديه إذنلإنشاء
وقراءة
منتج أومراجعة
ولكن ليسأو
تحديث
أي منها.
تنفيذ التحكم في الوصول المستند إلى السمات
الخطوة 1: افتح علامة التبويب الموارد، ثم انقر فوق زر إضافة السمات.
-
أضف سمة تسمى البائع
-
أضف سمة تسمى العميل
الخطوة 2: افتح علامة تبويب قواعد ABAC، ثم أنشئ مجموعة موارد ABAC جديدة تسمى المنتجات الخاصة التي تعتمد على مورد المنتجات. بعد ذلك، أضف شرطًا يمنح الأذونات فقط للمستخدم الذي أنشأ منتجًا بناءً على سمة البائع.
الخطوة 3: أنشئ مجموعة موارد ABAC أخرى تسمى المراجعات الخاصة التي تعتمد على مورد المراجعات.
تنفيذ التحكم في الوصول المستند إلى العلاقات
الخطوة 1: افتح علامة تبويب الموارد وقم بتحرير مورد المنتجات. أضف دور البائع
في قسم خيارات ReBAC
. ثم قم بتعيين المنتجات كأصل للمراجعات في قسم العلاقات.
الخطوة 2: قم بتحرير مورد المراجعات بإضافة دور العميل في قسم خيارات ReBAC
، كما هو موضح أدناه:
الخطوة 3: انتقل إلى علامة تبويب محرر
السياسة
وأضف:
-
إذن الدور
البائع
لتحديث وحذف منتجاته الخاصة. -
إذن دور
العميل
لتحديث وحذف مراجعاتهم الخاصة على المنتجات.
كيفية تنفيذ FGA في Java و SpringBoot
الآن بعد أن حددنا سياسات RBAC
و ABAC
و ReBAC
في واجهة Permit.io على الويب، دعونا نتعلم كيفية تطبيقها في تطبيق نظام إدارة التجارة الإلكترونية باستخدام واجهة برمجة تطبيقات Permit.io.
هناك الكثير من الكود القادم، لذا تأكد من قراءة التعليقات الشاملة التي تركتها في كل كتلة كود. ستساعدك هذه على فهم ما يحدث في هذا الكود بشكل أكثر شمولاً.
الخطوة 1: إعداد تطبيق التجارة الإلكترونية
لإعداد تطبيق التجارة الإلكترونية واستنساخ الكود المصدري باستخدام git.
git clone https://github.com/tyaga001/java-spring-fine-grained-auth.git
تثبيت حزمة SDK الخاصة بـ Permit
لتثبيت حزمة SDK الخاصة بـ Permit، قم بإضافة SDK تحت كتلة التبعيات في ملف build.graddle
.
## Dependencies
To set up the necessary dependencies for your Spring Boot project, include the following in your `build.gradle` file:
```groovy
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
// أضف هذا السطر لتثبيت Permit.io Java SDK في مشروعك
implementation 'io.permit:permit-sdk-java:2.0.0'
}
تكوين الSDK الموافق
يمكنك تكوين الموافق SDK
المستخدم باستخدام الشيء ال下例:
package com.boostmytool.store.config;
import io.permit.sdk.Permit;
import io.permit.sdk.PermitConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration // يعرف التسمية هذه السياسة كمجال للتكوينات البرمجية للرباط الأولي
public class PermitClientConfig {
@Value("${permit.api-key}") // يُرمي المفتاح الرئيسي للموافق من خلال الخصائص التطبيقية
private String apiKey;
@Value("${permit.pdp-url}") // يُرمي عنوان PDP (نقطة القرار السياسي) للموافق من خلال الخصائص التطبيقية
private String pdpUrl;
/**
* ينشئ بندقية موافق بالتكوينات الخاصة
* @return مصدر بندقية الموافق
*/
@Bean
public Permit permit() {
return new Permit(
new PermitConfig.Builder(apiKey) // تكوين PermitConfig مع المفتاح الرئيسي
.withPdpAddress(pdpUrl) // تعيين عنوان المركز التدابيري
.withDebugMode(true) // تفعيل وضعية التصدير للمعلومات التفصيلية
.build() // بناء جهة PermitConfig
);
}
}
تتزام المستخدمين مع الSDK
لبدء تنفيذ تنظيم الصلاحيات، يجب أولاً تزام المستخدم مع Permit، ومن ثم تخصيصهم لدور.
في الشيء ال下例، توفر UserService تلك السياسة أدوات لتسجيل دخول المستخدمين، التسجيل، تخصيص الدور، والتصويت، مع معالجة الأخطاء المحتملة عند التفاعل مع الموافق API.
package com.boostmytool.store.service;
import com.boostmytool.store.exception.ForbiddenAccessException;
import com.boostmytool.store.exception.UnauthorizedException;
import io.permit.sdk.Permit;
import io.permit.sdk.api.PermitApiError;
import io.permit.sdk.api.PermitContextError;
import io.permit.sdk.enforcement.Resource;
import io.permit.sdk.enforcement.User;
import org.springframework.stereotype.Service;
import java.io.IOException;
@Service // يحدد هذا الفئة كخدمة Spring، مما يجعلها مرشحة للمسح الضوئي للمكونات
public class UserService {
private final Permit permit;
// حقن المنشئ لـ Permit SDK
public UserService(Permit permit) {
this.permit = permit;
}
/**
* يحاكي تسجيل دخول المستخدم عن طريق إنشاء وإرجاع كائن مستخدم Permit.
*
* @param key المفتاح الفريد للمستخدم
* @return كائن المستخدم
*/
public Object login(String key) {
return new User.Builder(key).build();
}
/**
* يتعامل مع تسجيل المستخدم عن طريق إنشاء ومزامنة مستخدم Permit جديد.
*
* @param key المفتاح الفريد للمستخدم
* @return كائن المستخدم الذي تم إنشاؤه ومزامنته
*/
public User signup(String key) {
var user = new User.Builder(key).build();
try {
permit.api.users.sync(user); // يزامن المستخدم الجديد مع خدمة Permit
} catch (PermitContextError | PermitApiError | IOException e) {
throw new RuntimeException("Failed to create user", e); // يتعامل مع الاستثناءات أثناء إنشاء المستخدم
}
return user;
}
/**
* يعين دورًا للمستخدم داخل البيئة "الافتراضية".
*
* @param user كائن المستخدم لتعيين الدور له
* @param role الدور المراد تعيينه
*/
public void assignRole(User user, String role) {
try {
permit.api.users.assignRole(user.getKey(), role, "default"); // يعين الدور في البيئة "الافتراضية"
} catch (PermitApiError | PermitContextError | IOException e) {
throw new RuntimeException("Failed to assign role to user", e); // يتعامل مع الاستثناءات أثناء تعيين الدور
}
}
/**
* يتحقق مما إذا كان المستخدم مصرحًا له بتنفيذ إجراء معين على مورد.
*
* @param user كائن المستخدم الذي يطلب التصريح
* @param action الإجراء المراد التصريح له
* @param resource المورد الذي سيتم تنفيذ الإجراء عليه
* @throws UnauthorizedException إذا لم يكن المستخدم مسجل الدخول
* @throws ForbiddenAccessException إذا تم رفض الوصول للمستخدم
*/
public void authorize(User user, String action, Resource resource) {
if (user == null) {
throw new UnauthorizedException("Not logged in"); // يرمي استثناءً إذا لم يكن المستخدم مسجل الدخول
}
try {
var permitted = permit.check(user, action, resource); // يقوم بفحص التصريح
if (!permitted) {
throw new ForbiddenAccessException("Access denied"); // يرمي استثناءً إذا تم رفض الوصول
}
} catch (PermitApiError | IOException e) {
throw new RuntimeException("Failed to authorize user", e); // يتعامل مع الاستثناءات أثناء التصريح
}
}
}
في الشيء التالي في الشفرة أعطيت ال clase UserController نقاط الانتهاء لل API REST للتسجيل وتخصيص الدور. وهي تتفاعل مع التسخير الخاص بالمستخدمين للتخليص من قضايا المستخدمين وتقدم الردود HTTP المناسبة.
package com.boostmytool.store.controllers;
import com.boostmytool.store.exception.UnauthorizedException;
import com.boostmytool.store.service.UserService;
import io.permit.sdk.enforcement.User;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController // توضح أن هذه الصفقة تقوم بمعالجة طلبات HTTP وترسل ردود JSON
@RequestMapping("/api/users") // مسار الرابط الأساسي لجميع العمليات المتعلقة بالمستخدمين
public class UserController {
private final UserService userService;
// تحميل التسخير المبني على الخدمة التجارية للممارسات الخاصة بالمستخدمين
public UserController(UserService userService) {
this.userService = userService;
}
/**
* تقوم بمعالجة solicitudes de inscripción de usuario.
* نقطة الانتهاء: POST /api/users/signup
*
* @param key مفتاح فريد للمستخدم الجديد
* @return توليد جسم المستخدم المنشور
*/
@PostMapping("/signup")
public User signup(@RequestBody String key) {
return userService.signup(key); // تمتص في الطريقة السابقة في UserService لخلق مستخدم جديد
}
/**
* تقوم بتخصيص دور للمستخدم المتصل بالويب.
* نقطة الانتهاء: POST /api/users/assign-role
*
* @param request solicitud HTTP، تستخدم لتوفير المستخدم الحالي
* @param role دور يتم تخصيصه للمستخدم الحالي
*/
@PostMapping("/assign-role")
public void assignRole(HttpServletRequest request, @RequestBody String role) {
// توفير المستخدم الحالي من خلال خصائص ال solicitud
User currentUser = (User) request.getAttribute("user");
// تأتي استثناء إذا لم يكن المستخدم متصلاً
if (currentUser == null) {
throw new UnauthorizedException("Not logged in");
}
// تخصيص دور معين للمستخدم الحالي
userService.assignRole(currentUser, role);
}
}
إنشاء نقطة تنفيذ السياسات RBAC ABAC و ReBAC
في الشفرة أدناه، تتخذ ProductService الصفات إدارة ال operatios CRUD للمنتجات والمراجعات، مع التعامل مع الصلاحيات والأدوار عبر API الصلاحية.
كل عملية تشمل تحقيقات التوافر المستخدم
للمستخدمين، مع معالجة من الأعطال المناسبة لأخطأء API الصلاحية وأحالة عدم إيجاد الموارد.
package com.boostmytool.store.service;
import com.boostmytool.store.exception.ResourceNotFoundException;
import com.boostmytool.store.model.Product;
import com.boostmytool.store.model.Review;
import io.permit.sdk.Permit;
import io.permit.sdk.api.PermitApiError;
import io.permit.sdk.api.PermitContextError;
import io.permit.sdk.enforcement.Resource;
import io.permit.sdk.enforcement.User;
import io.permit.sdk.openapi.models.RelationshipTupleCreate;
import io.permit.sdk.openapi.models.ResourceInstanceCreate;
import io.permit.sdk.openapi.models.RoleAssignmentCreate;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Service // يعلم هذه الفئة كخدمة Spring
public class ProductService {
private final List<Product> products = new ArrayList<>(); // قائمة في الذاكرة لتخزين المنتجات
private final AtomicInteger productIdCounter = new AtomicInteger(); // عداد لتوليد معرفات المنتجات الفريدة
private final AtomicInteger reviewIdCounter = new AtomicInteger(); // عداد لتوليد معرفات المراجعات الفريدة
// بناة لموارد بيرميت (المنتج والمراجعة)
private final Resource.Builder productResourceBuilder = new Resource.Builder("product");
private final Resource.Builder reviewResourceBuilder = new Resource.Builder("review");
private final UserService userService; // خدمة للتعامل مع عمليات المستخدم
private final Permit permit; // مثيل بيرميت SDK للتعامل مع التفويض وإدارة الموارد
// بناء لحقن التبعيات
public ProductService(UserService userService, Permit permit) {
this.userService = userService;
this.permit = permit;
}
// طريقة لتفويض المستخدم لإجراء معين على مورد
private void authorize(User user, String action, Resource resource) {
userService.authorize(user, action, resource);
}
// يفوض المستخدم لإجراء إجراء معين على منتج محدد
private void authorize(User user, String action, Product product) {
var attributes = new HashMap<String, Object>();
attributes.put("vendor", product.getVendor()); // إضافة سمة البائع إلى المنتج
userService.authorize(user, action, productResourceBuilder.withKey(product.getId().toString()).withAttributes(attributes).build());
}
// يفوض المستخدم لإجراء إجراء معين على مراجعة محددة
private void authorize(User user, String action, Review review) {
var attributes = new HashMap<String, Object>();
attributes.put("customer", review.getCustomer()); // إضافة سمة العميل إلى المراجعة
userService.authorize(user, action, reviewResourceBuilder.withKey(review.getId().toString()).withAttributes(attributes).build());
}
// يسترجع منتجًا بواسطة معرفه، ويطرح استثناء إذا لم يتم العثور عليه
private Product getProductById(int id) {
return products.stream().filter(product -> product.getId().equals(id))
.findFirst().orElseThrow(() -> new ResourceNotFoundException("Product with id " + id + " not found"));
}
// يسترجع جميع المنتجات، ويتحقق مما إذا كان المستخدم مفوضًا لـ "قراءة" المنتجات
public List<Product> getAllProducts(User user) {
authorize(user, "read", productResourceBuilder.build()); // يجب أن يكون للمستخدم إذن "قراءة"
return new ArrayList<>(products); // إرجاع نسخة من قائمة المنتجات
}
// يسترجع منتجًا بواسطة معرفه، ويتحقق مما إذا كان المستخدم مفوضًا لـ "قراءة" المنتج
public Product getProduct(User user, int id) {
authorize(user, "read", productResourceBuilder.build());
return getProductById(id);
}
// يضيف منتجًا جديدًا، ويفوض المستخدم، ويقوم بإنشاء موارد وتعيينات الأدوار في بيرميت
public Product addProduct(User user, String content) {
authorize(user, "create", productResourceBuilder.build()); // التحقق مما إذا كان المستخدم يمكنه إنشاء منتج
Product product = new Product(productIdCounter.incrementAndGet(), user.getKey(), content); // إنشاء منتج جديد
try {
// إنشاء مثيل مورد في بيرميت وتعيين دور "البائع" للمستخدم لهذا المنتج
permit.api.resourceInstances.create(new ResourceInstanceCreate(product.getId().toString(), "product").withTenant("default"));
permit.api.roleAssignments.assign(new RoleAssignmentCreate("vendor", user.getKey()).withResourceInstance("product:" + product.getId()).withTenant("default"));
} catch (IOException | PermitApiError | PermitContextError e) {
throw new RuntimeException("Failed to create resource instance or role assignment: " + e.getMessage());
}
products.add(product); // إضافة المنتج إلى القائمة في الذاكرة
return product;
}
// يحدث محتوى المنتج، ويتحقق مما إذا كان المستخدم مفوضًا لـ "تحديث" المنتج
public Product updateProduct(User user, int id, String content) {
Product product = getProductById(id); // الحصول على المنتج بواسطة معرفه
authorize(user, "update", product); // التحقق مما إذا كان المستخدم يمكنه تحديث المنتج
product.setContent(content); // تحديث محتوى المنتج
return product;
}
// يحذف منتجًا، ويتحقق مما إذا كان المستخدم مفوضًا لـ "حذف" المنتج
public void deleteProduct(User user, int id) {
boolean isDeleted = products.removeIf(product -> {
if (product.getId().equals(id)) {
authorize(user, "delete", product); // التحقق مما إذا كان المستخدم يمكنه حذف المنتج
return true;
} else {
return false;
}
});
if (!isDeleted) {
throw new ResourceNotFoundException("Product with id " + id + " not found");
}
try {
permit.api.resourceInstances.delete("product:" + id); // إزالة مثيل مورد المنتج من بيرميت
} catch (IOException | PermitApiError | PermitContextError e) {
throw new RuntimeException(e);
}
}
// يضيف مراجعة إلى منتج، وينشئ مثيل مورد وعلاقة في بيرميت
public Review addReview(User user, int productId, String content) {
authorize(user, "create", reviewResourceBuilder.build()); // التحقق مما إذا كان المستخدم يمكنه إنشاء مراجعة
Product product = getProductById(productId); // الحصول على المنتج بواسطة معرفه
Review review = new Review(reviewIdCounter.incrementAndGet(), user.getKey(), content); // إنشاء مراجعة جديدة
try {
// إنشاء مثيل مورد للمراجعة وتعيين العلاقة مع المنتج
permit.api.resourceInstances.create(new ResourceInstanceCreate(review.getId().toString(), "review").withTenant("default"));
permit.api.relationshipTuples.create(new RelationshipTupleCreate("product:" + productId, "parent", "review:" + review.getId()));
} catch (IOException | PermitApiError | PermitContextError e) {
throw new RuntimeException(e);
}
product.addReview(review); // إضافة المراجعة إلى المنتج
return review;
}
// يحدث محتوى المراجعة، ويتحقق مما إذا كان المستخدم مفوضًا لـ "تحديث" المراجعة
public Review updateReview(User user, int productId, int reviewId, String content) {
Product product = getProductById(productId); // الحصول على المنتج بواسطة معرفه
Review review = product.getReviews().stream().filter(c -> c.getId().equals(reviewId))
.findFirst().orElseThrow(() -> new ResourceNotFoundException("Review with id " + reviewId + " not found"));
authorize(user, "update", review); // التحقق مما إذا كان المستخدم يمكنه تحديث المراجعة
review.setContent(content); // تحديث محتوى المراجعة
return review;
}
// يحذف مراجعة، ويتحقق مما إذا كان المستخدم مفوضًا لـ "حذف" المراجعة
public void deleteReview(User user, int productId, int reviewId) {
Product product = getProductById(productId); // الحصول على المنتج بواسطة معرفه
boolean isDeleted = product.getReviews().removeIf(review -> {
if (review.getId().equals(reviewId)) {
authorize(user, "delete", review); // التحقق مما إذا كان المستخدم يمكنه حذف المراجعة
return true;
} else {
return false;
}
});
if (!isDeleted) {
throw new ResourceNotFoundException("Review with id " + reviewId + " not found");
}
try {
permit.api.resourceInstances.delete("review:" + reviewId); // إزالة مثيل مورد المراجعة من بيرميت
} catch (IOException | PermitApiError | PermitContextError e) {
throw new RuntimeException(e);
}
}
}
في الشيء التالي يتخذ ProductController التعلم الخاص بمعالجة solicitudes HTTP متعلقة بالمنتجات وتعليقاتها. إنه يقدم نقاط نهائية لإدارة المنتجات (مثل creating
, updating
, deleting
, و retrieving
) ولإدارة تعليقات المنتجات.
package com.boostmytool.store.controllers;
import com.boostmytool.store.model.Product;
import com.boostmytool.store.model.Review;
import com.boostmytool.store.service.ProductService;
import io.permit.sdk.enforcement.User;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController // يشير إلى أن هذه الفئة هي فئة تحكم في Spring REST
@RequestMapping("/api/products") // عنوان URL الأساسي لجميع نقاط النهاية في هذا المتحكم
public class ProductController {
private final ProductService productService; // نسخة من ProductService لمعالجة العمليات ذات الصلة بالمنتجات
@Autowired // يقوم بتوصيل ProductService تلقائياً
public ProductController(ProductService productService) {
this.productService = productService;
}
// طلب GET لاسترجاع جميع المنتجات
@GetMapping
public List<Product> getAllProducts(HttpServletRequest request) {
User currentUser = (User) request.getAttribute("user"); // يحصل على المستخدم المصادق عليه من الطلب
return productService.getAllProducts(currentUser); // يتصل بـ ProductService للحصول على جميع المنتجات للمستخدم
}
// طلب GET لاسترجاع منتج بواسطة معرفه
@GetMapping("/{id}")
public Product getProductById(HttpServletRequest request, @PathVariable("id") int id) {
User currentUser = (User) request.getAttribute("user"); // يحصل على المستخدم المصادق عليه من الطلب
return productService.getProduct(currentUser, id); // يتصل بـ ProductService للحصول على المنتج بواسطة المعرف للمستخدم
}
// طلب POST لإضافة منتج جديد
@PostMapping
@ResponseStatus(HttpStatus.CREATED) // يضبط حالة الاستجابة على 201 (تم الإنشاء)
public Product addProduct(HttpServletRequest request, @RequestBody String content) {
User currentUser = (User) request.getAttribute("user"); // يحصل على المستخدم المصادق عليه من الطلب
return productService.addProduct(currentUser, content); // يتصل بـ ProductService لإضافة منتج جديد
}
// طلب PUT لتحديث منتج موجود بواسطة معرفه
@PutMapping("/{id}")
public Product updateProduct(HttpServletRequest request, @PathVariable("id") int id, @RequestBody String content) {
User currentUser = (User) request.getAttribute("user"); // يحصل على المستخدم المصادق عليه من الطلب
return productService.updateProduct(currentUser, id, content); // يتصل بـ ProductService لتحديث المنتج بواسطة المعرف
}
// طلب DELETE لحذف منتج بواسطة معرفه
@DeleteMapping("/{id}")
public String deleteProduct(HttpServletRequest request, @PathVariable("id") int id) {
User currentUser = (User) request.getAttribute("user"); // يحصل على المستخدم المصادق عليه من الطلب
productService.deleteProduct(currentUser, id); // يتصل بـ ProductService لحذف المنتج بواسطة المعرف
return "Deleted product with id " + id; // يعيد رسالة نجاح بعد الحذف
}
// طلب POST لإضافة مراجعة جديدة إلى منتج بواسطة معرف المنتج
@PostMapping("/{id}/review")
public Review addReview(HttpServletRequest request, @PathVariable("id") int id, @RequestBody String content) {
User currentUser = (User) request.getAttribute("user"); // يحصل على المستخدم المصادق عليه من الطلب
return productService.addReview(currentUser, id, content); // يتصل بـ ProductService لإضافة مراجعة إلى المنتج
}
// طلب PUT لتحديث مراجعة موجودة بواسطة معرف المنتج والمراجعة
@PutMapping("/{id}/review/{reviewId}")
public Review updateReview(HttpServletRequest request, @PathVariable("id") int id, @PathVariable("reviewId") int reviewId, @RequestBody String content) {
User currentUser = (User) request.getAttribute("user"); // يحصل على المستخدم المصادق عليه من الطلب
return productService.updateReview(currentUser, id, reviewId, content); // يتصل بـ ProductService لتحديث المراجعة
}
// طلب DELETE لحذف مراجعة بواسطة معرف المنتج والمراجعة
@DeleteMapping("/{id}/review/{reviewId}")
public String deleteReview(HttpServletRequest request, @PathVariable("id") int id, @PathVariable("reviewId") int reviewId) {
User currentUser = (User) request.getAttribute("user"); // يحصل على المستخدم المصادق عليه من الطلب
productService.deleteReview(currentUser, id, reviewId); // يتصل بـ ProductService لحذف المراجعة
return "Deleted review with id " + reviewId + " from product " + id; // يعيد رسالة نجاح بعد الحذف
}
}
خطوة 2: حصل على مفتاح بيانات ال环境
في لوحة المراقبة التي توفر لنا، قم بنسخ مفتاح API
البيانات للبيئة النشطة.
ومن ثم أضف المفتاح API
وURL
من نقطة قرار السياسة في ملف application.yaml
.
permit:
pdpUrl: 'http://localhost:7766'
apiKey: "Your Permit environment API Key"
خطوة 3: تنفيذ نقطة قرار السياسة (PDP)
يتم تنفيذ نقطة قرار السياسة (PDP) في واحدة من مناطقك الفرعية التي تختار تقديمك المقابل لل autorización. سيتأكد الPDP من تأخير صفر، وأداء عظيم، وتوفر عالٍ، وأمن مستقبلي.
قم باستخدام الأوامر التالية لتحميل حاجز Permit.io من موقع Docker
Hub.
docker pull permitio/pdp-v2:latest
ثم قم بتشغيل الحاجز.
docker run -it -p 7766:7000 --env PDP_DEBUG=True --env PDP_API_KEY=<YOUR_API_KEY> permitio/pdp-v2:latest
خطوة 4: تشغيل التطبيق
يمكنك تشغيل التطبيق بواسطة الأوامر التالية لـGradle
:
./gradlew bootRun
مشاهدة وإنشاء المنتجات
دعونا نتفاعل مع نقاط النهاية التي توفرها التطبيق بواسطة REQBIN.
أولاً، قم بإنشاء مستخدم جديد بواسطة نقطة الانتهاء /api/users/signup
.
curl -X POST "http://localhost:8080/api/users/signup" -H "Content-Type: application/json" -d 'johndoe'
ينبغي أن تستطيع رؤية المستخدم في مشروعك في Permit ، أسفل المجالات > جميع المستأجرين.
في البداية، لا يوجد للمستخدم أي دورات، لذلك لا يمكنه فعل أي شيء كثير. على سبيل المثال، محاولة إlisting منتجات ستنتج رد 403 Forbidden كما يمكن رؤية أدناه. تعني رمز خطأ 403 يعني عدم وجود صلاحيات المستخدم للوصول إلى المورد المطلوب، وهذه المنتجات في هذه الحالة. يمكنك أن تتعلم المزيد عن الفرق بين رموز الخطأ 401 و403 هنا.
لكي يتمكن المستخدم من مشاهدة قائمة بالمنتجات، يجب تخصيصه لدور المشاهدين باستخدام الأمر التالي:
curl -X POST "http://localhost:8080/api/users/assign-role" \
-H "Authorization: Bearer johndoe" \
-H "Content-Type: application/json" \
-d 'viewer'
يجب أن تروا أن تم تخصيص johndoe
لدور المشاهدين وهو ما يظهر أدناه:
ولأن المشاهدين يمكنهم إنشاء منتج، يمكنك استخدام الأمر التالي لإنشاء منتج بواسطة johndoe
.
curl -X POST "http://localhost:8080/api/products" -H "Authorization: Bearer johndoe" -H "Content-Type: application/json" -d 'MacBook'
يجب أن تروا أن تم إنشاء منتج جديد بالمُعرف 1 وأن المستخدم johndoe
تم إضافته كمورد.
إضافة مراجعات إلى المنتجات
لإضافة مراجعات إلى المنتجات، قم بإنشاء مستخدم آخر يُدعى jane
.
curl -X POST "http://localhost:8080/api/users/signup" -H "Content-Type: application/json" -d 'jane'
لكي يتمكن المستخدم من إضافة مراجعة إلى المنتجات، تخصيصه لدور viewer
باستخدام الأمر التالي:
curl -X POST "http://localhost:8080/api/users/assign-role" \
-H "Authorization: Bearer jane" \
-H "Content-Type: application/json" \
-d 'viewer'
ثم يمكنك إضافة مراجعة إلى المنتج الذي أضفه johndoe
باستخدام الأمر التالي:
curl -X POST "http://localhost:8080/api/products/1/review" -H "Authorization: Bearer jane" -H "Content-Type: application/json" -d 'The product was in good quality'
تهانينا! لقد انجزتم المشروع لهذا التوريتال.
الخطوات القادمة
مع ذلك أنك تعلمت ك
هذه بعض الموارد القيمة:
قبل أن ننتهي
أتمنى أن تجد هذا التورية ذكياً.
هذه بعض من مقالاتي الأخرى الحديثة التي قد تستمتع بها:
لمزيد من الدروس حول أدوات المطورين الرائعة، تأكد من زيارة مدونتي DTA.
تابعني على تويتر للحصول على تحديثات مباشرة حول مشاريعي الأخرى.
التشفير السعيد.
Source:
https://www.freecodecamp.org/news/fine-grained-authorization-in-java-and-springboot/