אבטחת היישומך הולכת יותר מרק אישור או דחיית גישה ברמה המושגית. כפילוגן, עליך ליישם סימנים נוספים של אישור
(FGA) כדי לנהל את ההגישות ברמה הדיוקנית יותר.
FGA מאפשרת לך להגדיר בדיוק של של בקרת הגישות שמספקת את המידע על מי יכול לעשות מה ותחת איזה תנאים.
בהדרכה זו, תלמדו איך ליישם סימנים נוספים של אישור
בעזרת ג 'וב וספינג באוט בשימוש בPermit.io.
הנה הקוד המקורי (שימו לב שאתם צריכים לתת לו כוכב 🌟).
אני מקווה שהשמעתם לבלוג הקודם שלי על בניית יישום סלולרי מקוון מותאם עם Stream ו Next.js. הבלוגים האלה משקפים את מסעי היצירה שלי בבניית DevTools Academy, משרד שמעלה למפתחים עבור הגישה לכלים הפיזיים המדהימים האלה.
ההדרכה הזו היא מאמץ נוסף להציג את הכלי המפותח המאוד מועיל למפתחים שחקרתי לאחרונה.
תוכן הדף:
מה היא האישור?
Permit.io היא פתרון רשת מלאה רצועות, שבו אתה יכול להגיע לשכבה שלם של
אישור
בתוך דקות ספורות, כך שתוכל להתמקד במה שחשוב ביותר.
תנאי קבע
כדי להבין את המדריך הזה באופן מלא, צריך להיות לך מושג בסיסי של ג' אורק
וספרינג בוט
. תדקדק גם בדברים הבאים:
-
Permit.io: כלי פיתוח שמשלט על היישום של FGA.
-
Spring Boot Starter Web
: מספק רכיבים חיוניים לבניית יישומי וеб, כולל API של RESTful.
-
Gradle
: כלי בנייה לניהול תלויות.
-
JDK 11 או מאוחר יותר
: גרסת קיט הפיתוח של ג'אווה הנדרשת להיקמה וריצה של אפליקציית Spring Boot.
-
Postman או cURL
: כלים לבדיקת נקודות הקצה של ה-API.
מהו אישור מדוקדק?
אישור מדוקדק מציע בקרת גישה למשאבים על ידי קביעה מי יכול לגשת אליהם, באיזו מידה ותחת תנאים מסוימים.
בניגוד לאישור גס (שמטפל בגישה לפי קטגוריות כגון תפקידי משתמש
כגון "מנהל
" או "משתמש
"), אישור עדין נותן לך את הגמישות להגדיר גישה ברמה דקה, למשאבים ספציפיים או פעולות ואפילו מאפיינים.
ב אישור מפורט
קיימים 3 סוגי מודלי מדיניות לניהול אישור; בקרת גישה מבוססת תפקיד (RBAC), בקרת גישה מבוססת תכונות (ABAC), ו בקרת גישה מבוססת יחסים (ReBAC).
בואו נביט, בכל אחד מגישות אלה ונראה איך אתה יכול ליישם אותם ביישומך.
בקרת גישה מבוססת תפקיד (RBAC)
RBAC היא גישת אבטחה ששולטת בגישה למשאבים בהתבסס על תפקידי המשתמשים בארגון. מודל זה מסדר מקבצי הרשאות על ידי ארגון משתמשים לתפקידים וניהול בקרת גישה לפי תפקידים אלה.
מושגים מפתח ב-RBAC:
משתמשים: אנשים שמשתמשים במערכת, כגון עובדים או לקוחות.
תפקידים: קבוצת הרשאות או הרשאות גישה שמוקצות לקבוצת משתמשים בהתבסס על אחריותם או משימותיהם, כגון מנהל, מנהל או לקוח.
הרשאות: הזכויות שניתנו למשתמשים לאינטראקציה עם משאבים, כגון קריאה, כתיבה או מחיקה.
בקרת גישה מבוססת תכונות (ABAC)
מודל שליטת גישה ABAC
מאפיינים: תכונות או מאפיינים שמשתמשים בכדי לקבע החלטות בקשר לשליטת גישה. מאפיינים בדרך כלל מווספים לקטגוריות:
-
מאפייני משתמש: מידע על המשתמש (לדוגמא, תפקיד, מחלקה, תיאור עבודה, גיל, וכן הלאה).
-
מאפיינים של משאב: תכונות של המשאב (לדוגמא, סוג קובץ, רמת סיווג מידע, תאריך יצירה, בעל).
-
מאפיינים לפעולה: הפעולה שהמשתמש מנסה לבצע (לדוגמא, קריאה, כתיבה, מחיקה, אישור).
-
מאפיינים סביבתיים: מידע הקשור לבקשת הגישה בהקשר (לדוגמא, שעה היום, מיקום, סוג המכשיר, כתובת IP).
בקרת גישה מבוססת יחסים (ReBAC)
ReBAC היא מערכת בקרת גישה המעניקה הרשאות לגישה למשאבים על בסיס היחסים בין ישויות בתוך מערכת. הגישה מדגישה הגדרה וניהול בקרת גישה על ידי מיפוי היחסים בין משתמשים למשאבים וישויות אחרות כגון ארגונים או קבוצות.
מושגי מפתח של ReBAC:
ישויות: משתמשים, משאבים (כגון קבצים ומסמכים) וישויות אחרות, כגון קבוצות או יחידות ארגוניות.
יחסים: הקשרים המגדירים את היחסים בין שתי ישויות. למשל, משתמש יכול להיות "בעלים" של מסמך או "חבר" בצוות.
מדיניות: כללים המשתמשים ביחסים לקביעת זכויות גישה. משתמש יכול לגשת למשאב או לבצע פעולה עליו אם יש לו יחס מסוים איתו.
איך ליישם אישור עדין
עכשיו שיש לכם הבנה בסיסית של RBAC
, ABAC
ו-ReBAC
, בואו נראה איך אפשר ליישם את המודלים האלה באפליקציית מסחר אלקטרוני.
יישום בקרת גישה מבוססת תפקידים
שלב 1: עבור ל-Permit.io, ואז צור חשבון ואת המרחב העבודה שלך.
במובן מסור, תראו פרוייקט שכולל שני סביבות: פיתוח
ו הפצה
.
שלב 2: יצירו משאב בשם מוצרים. על מנת ליצור את המשאב, פתחו לשם כתובת מדיניות בסרגל הצדדי משמאל ואז פתחו לשם כתובת משאבים בגב העל. אחר כך, לחצו על יצירת משאב ואז יצרו משאב בשם מוצרים עם פעולות קריאה
, יצירה
, עדכון
ו מחיקה
.
שלב 3: יצירו עוד משאב בשם משקפים עם פעולות קריאה
, יצירה
, עדכון
ו מחיקה
.
שלב 4: פתחו את כתובת מערך המדיניות. תראו שלושה תפקידים נוספו בשם admin
, editor
ו viewer
.
-
תפקיד ה
admin
מקבל את הרשאה ליצירה
,מחיקה
,קריאה
אועדכון
של מוצר או משקף. -
תפקיד
editor
מקבל את הרשאה ליצירה
,קריאה
אועדכון
של מוצר או משקף אבל לאמחיקה
כלל. -
לתפקיד
viewer
יש הרשאה לcreate
ולread
מוצר אוreview
אך לא לdelete
אוupdate
כלשהם.
יישום בקרת גישה מבוססת תכונות
שלב 1: פתח את לשונית משאבים, לחץ על כפתור הוסף תכונות.
-
הוסף תכונה בשם vendor
-
הוסף תכונה בשם customer
שלב 2: פתח את לשונית חוקי ABAC, ואז צור סט משאבים חדש בשם Own Products שתלוי במשאב המוצרים. לאחר מכן, הוסף תנאי שמעניק הרשאות רק למשתמש שיצר מוצר על בסיס תכונת ה-vendor.
שלב 3: צור סט משאבים נוסף בשם Own Reviews שתלוי במשאב הביקורות.
יישום בקרת גישה מבוססת קשרים
שלב 1: פתח את לשונית המשאבים וערוך את משאב המוצרים. הוסף תפקיד vendor
בסעיף האפשרויות של ReBAC
. לאחר מכן, הגדר מוצרים כהורה לביקורות בסעיף היחסים.
צעד 2: ערוך את משאב הביקורות על ידי הוספת תפקיד לקוח ב-ReBAC
אפשרויות, כפי שמוצג להלן:
צעד 3: עבור אל Policy
Editor
לשונית והוסף:
-
תפקיד
vendor
הרשאה לעדכן ולמחוק מוצרים שלהם. -
תפקיד
customer
הרשאה לעדכן ולמחוק את הביקורות שלהם על מוצרים.
כיצד ליישם FGA ביאווה ובSpringBoot
עכשיו שהגדרנו את מדיניות RBAC
, ABAC
ו-ReBAC
בממשק האינטרנט של Permit.io, בואו נלמד כיצד לאכוף אותן ביישום ניהול מסחר אלקטרוני באמצעות API של Permit.io.
יש הרבה קוד בדרך, אז וודא שאתה קורא את התגובות המפורטות שהשארתי לאורך כל בלוק הקוד. אלה יעזרו לך להבין טוב יותר מה קורה בקוד הזה.
שלב 1: הגדרת יישום המסחר האלקטרוני
כדי להגדיר את יישום המסחר האלקטרוני ולבצע את git clone של קוד המקור.
git clone https://github.com/tyaga001/java-spring-fine-grained-auth.git
התקנת חבילת Permit SDK
כדי להתקין את חבילת Permit SDK, עליך להוסיף את ה-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'
// הוסף את השורה הזו כדי להתקין את Java SDK של Permit.io בפרויקט שלך
implementation 'io.permit:permit-sdk-java:2.0.0'
}
אימוץ הסיסמה ל SDK
ניתן להתחיל את הלוואי לSDK
של Permit בעזרת הקוד הבא:
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 // מסמל לקבוצה הזאת שהיא מערכת הגדרות ל Spring IoC
public class PermitClientConfig {
@Value("${permit.api-key}") // מעבר מפתח ה-API של Permit מהנתונים של היישומון
private String apiKey;
@Value("${permit.pdp-url}") // מעבר לכתובת של PDP (נקודת ההחלטה על המדיניות) של Permit מהנתונים של היישומון
private String pdpUrl;
/**
* יוצר ג 'יין של לוואי של Permit עם הגדרות מותאמות
* @return מיקום לוואי של Permit
*/
@Bean
public Permit permit() {
return new Permit(
new PermitConfig.Builder(apiKey) // מיישם PermitConfig עם מפתח ה-API
.withPdpAddress(pdpUrl) // מעדכן את כתובת ה-PDP
.withDebugMode(true) // מאפשר מצב דיבוג עבור ריצוף העובדות בפורמטים מפורט
.build() // בונה את האובייקט של PermitConfig
);
}
}
סינכרון משתמשים עם SDK
כדי להתחיל באופן חוקי את הרשיון, עליך לסינכרן קודם משתמש ב Permit, ואחר כך להעניק לו תפקיד.
בקוד הבא, המקוםUserServiceמעניק שימוש בשירותים להתחברות למשתמש, הרשמה, העניקה של תפקידים, והסימוכה עם הגדרות, עם טיפול בהפרעות שעלולות להתרחש בזמן התקשורת עם ה-API של Permit.
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); // 处理授权期间的异常
}
}
}
UserController מחלקה חושפת נקודות קצה API REST עבור הרשמת משתמש והקצאת תפקיד. היא מתחברת עם מחלקת UserService כדי לטפל בלוגיקה עסקית הקשורה למשתמשים ומספקת תגובות 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") // נתיב URL בסיס לכל הפעולות הקשורות למשתמשים
public class UserController {
private final UserService userService;
// הזרקת UserService, המכילה לוגיקה עסקית עבור פעולות משתמש
public UserController(UserService userService) {
this.userService = userService;
}
/**
* מטפל בבקשות הרשמת משתמש.
* נקודת קצה: 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 בקשת HTTP, משמשת לאחזור המשתמש הנוכחי
* @param role תפקיד להקצאה למשתמש הנוכחי
*/
@PostMapping("/assign-role")
public void assignRole(HttpServletRequest request, @RequestBody String role) {
// מאחזר את המשתמש הנוכחי מתכונות הבקשה
User currentUser = (User) request.getAttribute("user");
// זורק חריגה אם המשתמש לא מחובר
if (currentUser == null) {
throw new UnauthorizedException("Not logged in");
}
// מקצה את התפקיד המצוין למשתמש הנוכחי
userService.assignRole(currentUser, role);
}
}
יצירת RBAC, ABAC, ו-ReBAC Policy Enforcement Point
בקוד מ below, המועדף ProductService ניהל את הפעילויות 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 של Permit לטיפול בהרשאות וניהול משאבים
// בנאי להזרקת תלות
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);
}
// מוסיף מוצר חדש, מאשר את המשתמש ויוצר מופעי משאבים והקצאות תפקידים ב-Permit
public Product addProduct(User user, String content) {
authorize(user, "create", productResourceBuilder.build()); // בדוק אם המשתמש יכול ליצור מוצר
Product product = new Product(productIdCounter.incrementAndGet(), user.getKey(), content); // יצירת מוצר חדש
try {
// יצירת מופע משאב ב-Permit והקצאת תפקיד "ספק" למשתמש עבור מוצר זה
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); // הסרת מופע משאב המוצר מ-Permit
} catch (IOException | PermitApiError | PermitContextError e) {
throw new RuntimeException(e);
}
}
// מוסיף ביקורת למוצר, יוצר מופע משאב ומערכת יחסים ב-Permit
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); // הסרת מופע משאב הביקורת מ-Permit
} catch (IOException | PermitApiError | PermitContextError e) {
throw new RuntimeException(e);
}
}
}
בקוד הבא, המועדף ProductController מתמשך בבקשות 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 // מציין שמחלקה זו היא בקר REST של Spring
@RequestMapping("/api/products") // כתובת הבסיס לכל נקודות הקצה בבקר זה
public class ProductController {
private final ProductService productService; // מופע ProductService לטיפול בפעולות הקשורות למוצרים
@Autowired // מזריק אוטומטית את ה-bean של 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
וכתובת ה-PDP URL
בקובץ application.yaml
.
permit:
pdpUrl: 'http://localhost:7766'
apiKey: "Your Permit environment API Key"
שלב 3: הפצת נקודת החלטה המדינית (PDP)
נקודת החלטה המדינית (PDP) מופצת בתוך ה-VPC שלך ואחראית להערכת הבקשות ההסרטות שלך. הPDP תוודא את האטגנזיה האפסת, הביצועים הגבוהים, ההתפעלות הגבוהה והאבטחה המורגשת.
השתמש בפקודה הבאה כדי למשך את תא הPermit.io PDP מהדוקרבר המרכזי.
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 שלך, תחת מנהל > כל הסוכנויות.
להתחיל, המשתמש אין תפקידים, כך שהוא לא יכול לעשות הרבה. לדוגמה, ניסיון לרשום את המוצרים יוביל לתגובה 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'
מצב מצליחה! השלמת הפרוייקט עבור ההדרכה הזו בסיום.
השלבים הבאים
עכשיו שלמדת איך ליישם אישור מפורט בין היישומים שלך בין היישומים ב-Java ו
כאן חלק ממשקים ערך מאד:
לפני שביציא
אני מקווה שמצאתם את ההדרכה הזו מבטלת תובנות.
הנה חלק מהמאמרים האחרונים שלי האחרונים שאולי תהנו:
עבור הרבה מורשת הוראות על כלים פיתוח מדהימים, בודקו בבלוג שלי DTA.
עיצבו אותי בTwitter כדי לקבל עדכונים חיים על הפרוייקטים האחרים שלי.
שימו לב שממשיכים לקוד.
Source:
https://www.freecodecamp.org/news/fine-grained-authorization-in-java-and-springboot/