你的应用程序的安全性不仅仅是在表面层次上授予或拒绝访问。作为开发者,你需要实现细粒度授权
(FGA)来在更详细、更细粒度的层面上管理权限。
FGA允许你设置详细的访问控制,指定谁可以在什么条件下做什么。
在本教程中,你将学习如何在Java和Spring Boot中使用Permit.io实现细粒度授权
。
这里是源代码(别忘了给它点个星⭐)。
我希望你喜欢我之前的博客,那篇博客是关于使用Stream和Next.js构建自定义视频会议应用程序的。这些博客反映了我创建DevTools Academy的过程,这是一个旨在帮助开发者发现惊人开发工具的平台。
本教程是我向您介绍另一个我非常喜欢的开发者工具的最新努力。
目录:
Permit이란?
Permit.io는 フルス택, プラグアンドプレイ 응용层 인가 솔루션입니다. yours 를 몇 분 안에
安全
,靈活
,인가
レイヤー을 实施할 수 있습니다. 그렇게 yours 를 什么が最も重要である 것을 专注于할 수 있습니다.
前提条件
教程을 完全に 理解하려면, Java
와 Spring Boot
의 基本的理解が必要です. 以下が必要です:
-
Permit.io: FGAの実装を簡単化하는 開発ツールです。
-
Spring Boot Starter Web: RESTful API를 포함한 웹 应用程序 빌드에 필수적인 组建을 제공합니다.
-
Gradle: 依存성 관리를 위한 빌드 도구입니다.
-
JDK 11 이상: Spring Boot 应用程序을 컴파일하고 실행하기 위해 필요한 Java 개발 Kit 버전입니다.
-
Postman 또는 cURL:
API
端点을 테스트하는 도구입니다.
Fine-Grained Authorization이란?
Fine-grained authorization는 누구가 리소스에 어떤 정도의 アクセス이 가능하며, 어떤 조건에 따라 어떻게 접근할 수 있는지 결정하는 접근 제어를 제공합니다.
결정 Granular 권한 관리(Coarse-grained Authorization)에 비해, 细分 권한 관리는 자신의 응용 프로그램에서 자신의 역할에 따라 자신의 인스턴스를 특정 리소스나 행동, 심지어 속성에 대한 접근을 정의할 수 있는 flexibility을 제공합니다.
在 Fine Grained Authorization
에서, 인스턴스 관리를 위한 3가지 정책 모델이 존재하며, 役割 기반 접근 제어(RBAC), 속성 기반 접근 제어(ABAC), 그리고 관계 기반 접근 제어(ReBAC) 입니다.
이러한 접근 제어 방안의 각각에 대해 보며, 어떻게 자신의 응용 프로그램에 이를 적용하는지 살펴봅니다.
役割 기반 접근 제어(RBAC)
RBAC은 기업 내에서 사용자의 役割에 따라 자원 접근을 제어하는 보안 접근 제어 방법입니다. 이 모델은 사용자를 役割에 따라 그룹화하고 이러한 정의된 役割에 따라 접근 제어를 관리합니다.
RBAC의 주요 개념
사용자: 시스템을 사용하는 사람들 如火여자 또는 고객.
役割: 사용자 그룹에 대한 특정 권한 또는 접근 사용 이용 권한을 부여하는 것을 repsonsibilities 또는 tasks에 따라 부여하는 것이며, 관리자, 매니저, 또는 고객 등이 있습니다.
권한: 사용자가 리소스와 상호 작용하는 것에 대한 권한을 가지는 것 如火 읽기, 쓰기, 지우기 등입니다.
속성 기반 접근 제어(ABAC)
ABAC 는 사용자 속성과 같은 속성에 따라 자원에 대한 액세스 권한을 결정하는 다维多 drones 모델입니다. ABAC 모델은 사용자 속성에 따라 세세한 권한 정의를 지원합니다.
ABAC에서 중요한 키 кон읍트스
속성 액세스 控制系统 결정에 사용되는 특성 또는 속성입니다. 속성은 일반적으로 다음과 같이 분류되며:
-
사용자 속성 사용자 정보(예를 들어, 역할, 부서, 직위, 나이 등).
-
자원 속성 자원의 특성(예를 들어, 파일 유형, 데이터 분류 수준, 생성 날짜, 소유자).
-
행위 속성 사용자가 수행하려고 하는 행위(예를 들어, 읽기, 쓰기, 지우기, 승인).
-
환경 속성 액세스 요청에 관한 시각적 정보(예를 들어, 시간, 위치, 기기 유형, IP 주소).
関連性基づくアクセスコントロール(ReBAC)
ReBACは、システム内の实体同士の関連性に基づいてリソースにアクセス権を与えるアクセスコントロールシステムである。このアプローチは、ユーザーがリソースや組織やグループなどの他の实体とどのように関連しているかをマッピングすることで、アクセスコントロールを定義し管理することを強調する。
ReBACの主要な概念:
实体: ユーザー、リソース(例:ファイルやドキュメント)、そして他の实体、如く組織やグループなど。
関連性: 2つの实体間の関連性を指定する接続。例えば、ユーザーがドキュメントの「所有者」やチームの「メンバー」であることなど。
ポリシー: 関連性を使用してアクセス権を決定するルール。特定の関連性を持っている場合、ユーザーはリソースにアクセスしたり、それに対してアクションを実行することができる。
细粒度の認証を実装する方法
基本的な理解ができたら、RBAC
、ABAC
、ReBAC
をe-commerce appにどのようにこれらのモデルを実装するかを見てみましょう。
角色基づくアクセスコントロールの実装
ステップ1: Permit.ioに行って、アカウントとワークスペースを作成します。
기본적으로, 두 개의 환경을 포함하는 프로젝트를 보실 수 있습니다: 개발
과 생산
environment.
Step 2: 이름이 Products인 자원을 생성합니다. 자원을 생성하려면 왼쪽 사이드 바에 있는 Policies 탭을 눌러 상단의 Resources 탭을 여는 后来, Create a Resource button을 눌러 Products 이라는 이름의 자원을 생성하면서 읽기, 생성, 수정, 삭제 옵션을 추가합니다.
Step 3: 읽기, 생성, 수정, 삭제 옵션을 갖추고 Reviews 이라는 다른 자원을 생성합니다.
Step 4: Policy Editor 탭을 여섯. 그러다보면 admin
, editor
, 그리고 viewer
라는 이름의 3개의 rolls가 생성되었음을 볼 수 있습니다.
-
admin Role은 제품 또는 리뷰를 생성하거나 지우거나 읽거나 수정할 수 있는 권한이 있습니다.
-
editor Role는
product
또는review
를create
,read
, 또는update
하는 권한이 있으나delete
하는 권한이 없습니다. -
역할
viewer
는 제품이나리뷰
를 생성하고 읽는 권한이 있지만삭제
하거나업데이트
할 수는 없습니다.
속성 기반 접근 제어 구현
단계 1: 리소스 탭을 열고, 속성 추가 버튼을 클릭하세요.
-
vendor라는 속성을 추가하세요.
-
customer라는 속성을 추가하세요.
단계 2: ABAC 규칙 탭을 열고, Products 리소스에 의존하는 Own Products라는 새로운 ABAC 리소스 세트를 생성하세요. 그 후, vendor 속성을 기반으로 제품을 생성한 사용자에게만 권한을 부여하는 조건을 추가하세요.
단계 3: Reviews 리소스에 의존하는 Own Reviews라는 다른 ABAC 리소스 세트를 생성하세요.
관계 기반 접근 제어 구현
단계 1: 리소스 탭을 열고, Products 리소스를 편집하세요. ReBAC
옵션 섹션에 vendor
역할을 추가하세요. 그리고 관계 섹션에서 제품을 리뷰의 상위로 설정하세요.
的第2步: 像下面这样,在ReBAC
选项部分添加角色customer来编辑评论资源:
第3步: 转到策略
编辑器
标签页并添加:
-
角色
vendor
对自己产品的更新和删除权限。 -
角色
customer
对自己在产品上的评论的更新和删除权限。
如何在Java和SpringBoot中实现FGA
现在我们已经在我们在Permit.io网页界面上定义了RBAC
、ABAC
和ReBAC
策略,接下来我们来学习如何使用Permit.io API在电子商务管理系统应用程序中实施它们。
接下来的代码很多,所以请确保你阅读了我在每个代码块中留下的详尽注释。这些注释将帮助你更全面地理解这段代码。
第1步:设置电子商务应用程序
要设置电子商务应用程序,请克隆源代码。
git clone https://github.com/tyaga001/java-spring-fine-grained-auth.git
安装Permit包SDK
要安装Permit包SDK,你需要在build.gradle
文件的依赖项块中添加SDK。
## 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 초기화
다음 코드를 사용하여 Permit 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 // Spring IoC 用的 구성 클래스로 마크
public class PermitClientConfig {
@Value("${permit.api-key}") // 应用程序属性中注入 Permit API 密钥
private String apiKey;
@Value("${permit.pdp-url}") // 应用程序属性中注入 Permit PDP (策略决策点) URL
private String pdpUrl;
/**
* 사용자 정의 구성을 갖추고 Permit クライアント 빈 생성
* @return Permit クライアント 인스턴스
*/
@Bean
public Permit permit() {
return new Permit(
new PermitConfig.Builder(apiKey) // API 密钥를 이용하여 PermitConfig 초기화
.withPdpAddress(pdpUrl) // PDP 주소를 설정
.withDebugMode(true) // 디버그 모드를 사용하여 자세한 로깅 추가
.build() // PermitConfig 오브젝트 생성
);
}
}
SDK と 사용자 동기화
特许证을 적용하기 전に 사용자를 Permit에 동기화하고 그 后再び 역할을 부여합니다.
아래 코드에서, UserService 클래스는 사용자 로그인, 가입, 역할 부여, 인가를 제공하는 메서드를 갖추고 Permit API와 interact时的可能 에러에 대한 예외 처리를 제공합니다.
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;
}
/**
* 사용자 로그인을 simulation하기 위해 Permit User 객체를 생성하고 리턴합니다.
*
* @param key 사용자의 유니크 키
* @return 사용자 객체
*/
public Object login(String key) {
return new User.Builder(key).build();
}
/**
* 새로운 Permit User를 생성하고 동기화하여 사용자 가입을 처리합니다.
*
* @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;
}
/**
* "default" 환경 내에서 사용자에게 역할을 할당합니다.
*
* @param user 역할을 할당하는 사용자 객체
* @param role 할당할 역할
*/
public void assignRole(User user, String role) {
try {
permit.api.users.assignRole(user.getKey(), role, "default"); // "default" 환경에서 역할을 할당합니다.
} catch (PermitApiError | PermitContextError | IOException e) {
throw new RuntimeException("Failed to assign role to user", e); // 역할 할당 도중 예외를 처리합니다.
}
}
/**
* 사용자가 리소스에 대한 특정 동작을 执行业务 authorization를 요청하는지 여부를 확인합니다.
*
* @param user 인증요청하는 사용자 객체
* @param action 인증하는 동작
* @param resource 동작을 실행할 리소스
* @throws UnauthorizedException 사용자가 로그인되지 않은 경우
* @throws ForbiddenAccessException 사용자가 ACCESS를 거부받은 경우
*/
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 클래스는 사용자 가입과 역할 지정에 대한 REST API 엔드포인트를 노출합니다. 이 클래스는 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의 signup 方法을 호출하여 새로운 사용자를 생성합니다
}
/**
* 로그인 된 사용자에게 역할을 지정합니다.
* 엔드포인트: POST /api/users/assign-role
*
* @param request HTTP 요청, 현재 사용자를 가져옵니다
* @param role 현재 사용자에게 지정할 역할
*/
@PostMapping("/assign-role")
public void assignRole(HttpServletRequest request, @RequestBody String role) {
// HTTP 요청 속성에서 현재 사용자를 가져옵니다
User currentUser = (User) request.getAttribute("user");
// 사용자가 로그인되지 않았다면 예외를 던집니다
if (currentUser == null) {
throw new UnauthorizedException("Not logged in");
}
// 지정한 역할을 현재 사용자에게 지정합니다
userService.assignRole(currentUser, role);
}
}
RBAC, ABAC, ReBAC 정책 적용 지점 생성하는 중…
以下のコードでは、ProductServiceクラスが商品とレビューのCRUD操作を管理し、Permit APIを通じて権限と役割を処理します。
各操作には、ユーザーの認可
チェックが含まれ、Permit 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 // 이 클래스를 스프링 서비스로 지정합니다
public class ProductService {
private final List<Product> products = new ArrayList<>(); // 제품을 저장하기 위한 인메모리 리스트
private final AtomicInteger productIdCounter = new AtomicInteger(); // 고유한 제품 ID를 생성하기 위한 카운터
private final AtomicInteger reviewIdCounter = new AtomicInteger(); // 고유한 리뷰 ID를 생성하기 위한 카운터
// Permit 리소스 인스턴스(제품 및 리뷰)를 위한 빌더
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; // 인가 및 리소스 관리를 처리하기 위한 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());
}
// ID로 제품을 검색하고, 찾지 못하면 예외를 발생시킵니다
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); // 제품 목록의 복사본을 반환합니다
}
// ID로 제품을 검색하고, 사용자가 제품을 "읽기" 권한이 있는지 확인합니다
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); // 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); // ID로 제품을 가져옵니다
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); // ID로 제품을 가져옵니다
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); // ID로 제품을 가져옵니다
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 请求。它暴露了用于管理产品(如创建
、更新
、删除
和检索
)以及用于管理产品评论的端点。
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를 호출합니다
}
// ID로 상품을 검색하기 위한 GET 요청입니다
@GetMapping("/{id}")
public Product getProductById(HttpServletRequest request, @PathVariable("id") int id) {
User currentUser = (User) request.getAttribute("user"); // 요청에서 인증된 사용자를 가져옵니다
return productService.getProduct(currentUser, id); // 사용자에 대한 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를 호출합니다
}
// ID로 기존 상품을 업데이트하기 위한 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); // ID로 상품을 업데이트하기 위해 ProductService를 호출합니다
}
// ID로 상품을 삭제하기 위한 DELETE 요청입니다
@DeleteMapping("/{id}")
public String deleteProduct(HttpServletRequest request, @PathVariable("id") int id) {
User currentUser = (User) request.getAttribute("user"); // 요청에서 인증된 사용자를 가져옵니다
productService.deleteProduct(currentUser, id); // ID로 상품을 삭제하기 위해 ProductService를 호출합니다
return "Deleted product with id " + 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를 호출합니다
}
// 상품 및 리뷰 ID별로 기존 리뷰를 업데이트하기 위한 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를 호출합니다
}
// 상품 및 리뷰 ID별로 리뷰를 삭제하기 위한 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密钥
在UI仪表板上,复制活动环境的API密钥
。
然后,在application.yaml
文件中添加API密钥
和PDP URL
。
permit:
pdpUrl: 'http://localhost:7766'
apiKey: "Your Permit environment API Key"
第3步: 部署策略决策点(PDP)
策略决策点(PDP)部署在您的VPC中,负责评估您的授权请求。PDP将确保零延迟、高性能、高可用性和改进的安全性。
使用以下命令从Docker
Hub拉取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 에러 코드는 사용자가 요청한 리소스(이 경우에는 제품)에 대한 Permission을 가지고 있지 않음을 의미합니다. 자세한 정보를 여기서 401과 403 에러 코드의 차이를 배울 수 있습니다.
사용자가 제품 목록을 보기 위해서는 아래 명령어를 사용하여 viewer 역할을 부여하십시오.
curl -X POST "http://localhost:8080/api/users/assign-role" \
-H "Authorization: Bearer johndoe" \
-H "Content-Type: application/json" \
-d 'viewer'
사용자 johndoe
가 viewer 역할을 받은 것을 아래와 같이 확인할 수 있습니다.
viewer로 해당하므로 제품을 생성할 수 있습니다. 사용자 johndoe
에 대한 제품 생성 명령어를 사용하십시오.
curl -X POST "http://localhost:8080/api/products" -H "Authorization: Bearer johndoe" -H "Content-Type: application/json" -d 'MacBook'
ID 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'
하祝贺! 이 튜토리얼의 프로젝트를 완료했습니다.
다음 단계
Permit.io를 사용하여 Java와 Spring Boot 응용 프로그램에 精細 권한 구현을 배웠으면, 더 deeply 탐구하고 싶을 수 있습니다.
여러분의 가치 있는 자원을 여기에 荟萃시킨 것입니다.:
我們が 끝나기 전에
이 튜토리얼이 你们에게 启发的가 되었으면 좋겠습니다.
저의 다른 最近 部落格 글들 중 일부를 여기에 荟萃시킨 것입니다. 你们이 기대되는 것 같습니다:
신기한 開発자 도구에 대한 자습서를 더 많이 보고 싶으면, 저의 블로그 DTA를 확인해 주세요.
다른 프로젝트에 대한 실시간 정보를 받으시려면, 저를 Twitter에 ollow해 주세요.
programming. 행복하게 编码하세요.
Source:
https://www.freecodecamp.org/news/fine-grained-authorization-in-java-and-springboot/