1.根据项目去启动 配置一个 openfga 服务器
先创建一个 config.yaml文件
cd /opt/openFGA/conf
touch ./config.yaml
怎么配置?
根据官网来看
openfga/.config-schema.json at main · openfga/openfga · GitHub
这里讲述详细的每一个配置每一个类型
这些配置有三种形式配置方式
命令行
需要用 - 隔开每个 属性下的属性 和 properties 的 . 类似
环境变量
OpenFGA 服务器支持用于配置的环境变量,它们将优先于您的配置文件。每个变量都必须以 OPENFGA_
为前缀,后跟大写的选项(例如,--grpc-tls-key
变为 OPENFGA_GRPC_TLS_KEY
)。
官方也讲了怎么配置
config.yaml文件
需要映射数据卷
这里我们选择使用yaml配置
我们其他的用命令行配置,然后 把会变的用 yaml配置
1.配置限制单个 BatchCheck 请求中允许的检查数量。
maxChecksPerBatchCheck: 3000
2.限制可同时解决的 Check 数量
maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100
3.ListObjects 将返回在分配时间内找到的结果
maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100
listObjectsDeadline: 10s
4.ListObjects 最多为配置的最大结果数
maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100
listObjectsDeadline: 10s
listObjectsMaxResults: 3000
5.Listuser将返回在分配时间内找到的结果
maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100
listObjectsDeadline: 10s
listObjectsMaxResults: 3000
listUsersDeadline: 10s
6.ListObjects 最多为配置的最大结果数
maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100
listObjectsDeadline: 10s
listObjectsMaxResults: 3000
listUsersDeadline: 10s
listUsersMaxResults: 3000
7.最大元组一次写入
maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100
listObjectsDeadline: 10s
listObjectsMaxResults: 3000
listUsersDeadline: 10s
listUsersMaxResults: 3000
maxTuplesPerWrite: 1000
8.最大每个授权模型定义的类型
maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100
listObjectsDeadline: 10s
listObjectsMaxResults: 3000
listUsersDeadline: 10s
listUsersMaxResults: 3000
maxTuplesPerWrite: 1000
maxTypesPerAuthorizationModel: 3000
9.持久化授权模型所允许的最大字节数(默认值为256KB)。
maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100
listObjectsDeadline: 10s
listObjectsMaxResults: 3000
listUsersDeadline: 10s
listUsersMaxResults: 3000
maxTuplesPerWrite: 1000
maxTypesPerAuthorizationModel: 3000
maxAuthorizationModelSizeInBytes: 262144
requestTimeout: 10s
常用的配置就这么多
安装
docker-compose.yaml
version: '3.8'networks:openfga:services:mysql:image: mysql:8container_name: mysqlrestart: alwaysnetworks:- openfgaports:- "3306:3306"environment:- MYSQL_ROOT_PASSWORD=secret- MYSQL_DATABASE=openfgahealthcheck:test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]timeout: 20sretries: 5migrate:depends_on:mysql:condition: service_healthyimage: openfga/openfga:latestcontainer_name: migratecommand: migrateenvironment:- OPENFGA_DATASTORE_ENGINE=mysql- OPENFGA_DATASTORE_URI=root:secret@tcp(mysql:3306)/openfga?parseTime=truenetworks:- openfgaopenfga:depends_on:migrate:condition: service_completed_successfullyimage: openfga/openfga:latestrestart: alwayscontainer_name: openfgaenvironment:- OPENFGA_DATASTORE_ENGINE=mysql- OPENFGA_DATASTORE_URI=root:secret@tcp(mysql:3306)/openfga?parseTime=true- OPENFGA_LOG_FORMAT=jsonvolumes:- /opt/openFGA/conf:/etc/openfgacommand: runnetworks:- openfgaports:# Needed for the http server- "8080:8080"# Needed for the grpc server (if used)- "8081:8081"# Needed for the playground (Do not enable in prod!)- "3000:3000"
启动
cd /opt/openFGA
docker compose up -d
检测配置文件是否生效
docker compose logs -f
看日志 是生效了
2.下载fga cli
Releases · openfga/cli · GitHub
vim /etc/profile
修改环境变量文件
# 在环境变量文件中末尾,添加如下内容
export OPEN_FGA_HOME=/opt/openFGA/fgacli
export PATH=$OPEN_FGA_HOME:$PATH
# 重新加载环境变量文件
source /etc/profile
编辑配置文件
vim /root/.fga.yaml
.fga.yaml 内容如下
api-url: http://localhost:8080
根据官网来
试着执行一下命令
fga store create --name "FGA Demo Store"
表示执行成功,也成功在数据库创建了一个Store
3.下载Visual Studio Code
下载插件
下载windows版本的 cli
配置环境变量
然后 将fga目录文件也放入环境变量中,
就可以在任何位置使用 fga命令了
vscode 使用 ctrl shift p 选中 插件中的 transform to json 可以把 .fga 转化为 .json文件
然后创建模型开始 接下来就可以开始测试了
跟着官方文档 一步一步走
GitHub - openfga/cli: A cross-platform CLI to interact with an OpenFGA server
4.测试
这里我们作为测试
为了以后逻辑更加清晰,我们以模块化开发chuangj
创建 fga.mod 模块化开发核心组装文件
schema: '1.2'
contents:- core.fga
创建 核心 core.fga 其中一个模块
module coretype usertype rolerelationsdefine member: [user] type applicationrelationsdefine viewer: [user,role#member] define can_view: viewertype menurelationsdefine parent: [application]define add: can_view from parent
整合创建模型
fga model write --file=fga.mod
测试开始
Microsoft Windows [版本 10.0.19045.5371]
(c) Microsoft Corporation。保留所有权利。C:\Users\RJ\Documents\fgatestmodel>fga model get
modelschema 1.2type application # module: core, file: core.fgarelationsdefine can_view: viewerdefine viewer: [user, role#member]type role # module: core, file: core.fga relationsdefine member: [user]type user # module: core, file: core.fga C:\Users\RJ\Documents\fgatestmodel>fga model validate -file core.fga
Error: unknown shorthand flag: 'f' in -fileC:\Users\RJ\Documents\fgatestmodel>fga model validate --file core.fga
{"is_valid":false,"error":"invalid schema version"
}C:\Users\RJ\Documents\fgatestmodel>fga tuple write user:jmj member role:admin
{"successful": [{"object":"role:admin","relation":"member","user":"user:jmj"}]
}C:\Users\RJ\Documents\fgatestmodel>fga tuple write user:jmj member role:admin
Error: failed to write tuple: Write validation error for POST Write with body {"code":"write_failed_due_to_invalid_input","message":"cannot write a tuple which already exists: user: 'user:jmj', relation: 'member', object: 'role:admin': tuple to be written already existed or the tuple to be deleted did not exist"}with error code write_failed_due_to_invalid_input error message: cannot write a tuple which already exists: user: 'user:jmj', relation: 'member', object: 'role:admin': tuple to be written already existed or the tuple to be deleted did not existC:\Users\RJ\Documents\fgatestmodel>fga tuple write user:jmj member role:gaojimanager
{"successful": [{"object":"role:gaojimanager","relation":"member","user":"user:jmj"}]
}C:\Users\RJ\Documents\fgatestmodel>fga tuple write user:jmj member role:putongguanliyuan1
{"successful": [{"object":"role:putongguanliyuan1","relation":"member","user":"user:jmj"}]
}C:\Users\RJ\Documents\fgatestmodel>fga tuple read --user user:jmj --relation member --object role:admin
{"continuation_token":"","tuples": [{"key": {"object":"role:admin","relation":"member","user":"user:jmj"},"timestamp":"2025-01-25T03:06:32Z"}]
}C:\Users\RJ\Documents\fgatestmodel>fga tuple changes --type admin
{"changes": [],"continuation_token":""
}C:\Users\RJ\Documents\fgatestmodel>fga tuple changes --type role
{"changes": [{"operation":"TUPLE_OPERATION_WRITE","timestamp":"2025-01-25T03:06:32Z","tuple_key": {"object":"role:admin","relation":"member","user":"user:jmj"}},{"operation":"TUPLE_OPERATION_WRITE","timestamp":"2025-01-25T03:07:37Z","tuple_key": {"object":"role:gaojimanager","relation":"member","user":"user:jmj"}},{"operation":"TUPLE_OPERATION_WRITE","timestamp":"2025-01-25T03:07:45Z","tuple_key": {"object":"role:putongguanliyuan1","relation":"member","user":"user:jmj"}}],"continuation_token":"eyJ1bGlkIjoiMDFKSkRQVzBDRDJNVzVXNTFHQ1ZRMDQ5QzYiLCJPYmplY3RUeXBlIjoicm9sZSJ9"
}C:\Users\RJ\Documents\fgatestmodel>fga tuple changes --type role
{"changes": [{"operation":"TUPLE_OPERATION_WRITE","timestamp":"2025-01-25T03:06:32Z","tuple_key": {"object":"role:admin","relation":"member","user":"user:jmj"}},{"operation":"TUPLE_OPERATION_WRITE","timestamp":"2025-01-25T03:07:37Z","tuple_key": {"object":"role:gaojimanager","relation":"member","user":"user:jmj"}},{"operation":"TUPLE_OPERATION_WRITE","timestamp":"2025-01-25T03:07:45Z","tuple_key": {"object":"role:putongguanliyuan1","relation":"member","user":"user:jmj"}}],"continuation_token":"eyJ1bGlkIjoiMDFKSkRQVzBDRDJNVzVXNTFHQ1ZRMDQ5QzYiLCJPYmplY3RUeXBlIjoicm9sZSJ9"
}C:\Users\RJ\Documents\fgatestmodel>fga tuple changes --type role
{"changes": [{"operation":"TUPLE_OPERATION_WRITE","timestamp":"2025-01-25T03:06:32Z","tuple_key": {"object":"role:admin","relation":"member","user":"user:jmj"}},{"operation":"TUPLE_OPERATION_WRITE","timestamp":"2025-01-25T03:07:37Z","tuple_key": {"object":"role:gaojimanager","relation":"member","user":"user:jmj"}},{"operation":"TUPLE_OPERATION_WRITE","timestamp":"2025-01-25T03:07:45Z","tuple_key": {"object":"role:putongguanliyuan1","relation":"member","user":"user:jmj"}}],"continuation_token":"eyJ1bGlkIjoiMDFKSkRQVzBDRDJNVzVXNTFHQ1ZRMDQ5QzYiLCJPYmplY3RUeXBlIjoicm9sZSJ9"
}C:\Users\RJ\Documents\fgatestmodel>fga tuple changes --type role --continuation-token=eyJ1bGlkIjoiMDFKSkRQVzBDRDJNVzVXNTFHQ1ZRMDQ5QzYiLCJPYmplY3RUeXBlIjoicm9sZSJ9
{"changes": [],"continuation_token":"eyJ1bGlkIjoiMDFKSkRQVzBDRDJNVzVXNTFHQ1ZRMDQ5QzYiLCJPYmplY3RUeXBlIjoicm9sZSJ9"
}C:\Users\RJ\Documents\fgatestmodel>
C:\Users\RJ\Documents\fgatestmodel>fga tuple changes --type role --continuation-token=eyJ1bGlkIjoiMDFKSkRQVzBDRDJNVzVXNTFHQ1ZRMDQ5QzYiLCJPYmplY3RUeXBlIjoicm9sZSJ9
{"changes": [],"continuation_token":"eyJ1bGlkIjoiMDFKSkRQVzBDRDJNVzVXNTFHQ1ZRMDQ5QzYiLCJPYmplY3RUeXBlIjoicm9sZSJ9"
}C:\Users\RJ\Documents\fgatestmodel>fga tuple changes --type role
{"changes": [{"operation":"TUPLE_OPERATION_WRITE","timestamp":"2025-01-25T03:06:32Z","tuple_key": {"object":"role:admin","relation":"member","user":"user:jmj"}},{"operation":"TUPLE_OPERATION_WRITE","timestamp":"2025-01-25T03:07:37Z","tuple_key": {"object":"role:gaojimanager","relation":"member","user":"user:jmj"}},{"operation":"TUPLE_OPERATION_WRITE","timestamp":"2025-01-25T03:07:45Z","tuple_key": {"object":"role:putongguanliyuan1","relation":"member","user":"user:jmj"}}],"continuation_token":"eyJ1bGlkIjoiMDFKSkRQVzBDRDJNVzVXNTFHQ1ZRMDQ5QzYiLCJPYmplY3RUeXBlIjoicm9sZSJ9"
}C:\Users\RJ\Documents\fgatestmodel>fga query check user:jmj member role:admin
{"allowed":true,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query check user:jmj member role:admin
{"allowed":true,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query list-objects user:jmj member role
{"objects": ["role:admin","role:putongguanliyuan1","role:gaojimanager"]
}C:\Users\RJ\Documents\fgatestmodel>fga tuple write user:a can_view docu
Error: failed to write tuple: Write validation error for POST Write with body {"code":"validation_error","message":"Invalid tuple 'docu#can_view@user:a'. Reason: invalid 'object' field format"}with error code validation_error error message: Invalid tuple 'docu#can_view@user:a'. Reason: invalid 'object' field formatC:\Users\RJ\Documents\fgatestmodel>fga tuple write user:a can_view docu:aa
Error: failed to write tuple: Write validation error for POST Write with body {"code":"validation_error","message":"Invalid tuple 'docu:aa#can_view@user:a'. Reason: type 'docu' not found"}with error code validation_error error message: Invalid tuple 'docu:aa#can_view@user:a'. Reason: type 'docu' not foundC:\Users\RJ\Documents\fgatestmodel>fga tuple write user:bzm member role:admin
{"successful": [{"object":"role:admin","relation":"member","user":"user:bzm"}]
}C:\Users\RJ\Documents\fgatestmodel>fga query list-relations user:jmj role:admin --relation member
{"relations": ["member"]
}C:\Users\RJ\Documents\fgatestmodel>fga query list-relations user:jmj role --relation member
{"relations": []
}C:\Users\RJ\Documents\fgatestmodel>fga query list-relations user:jmj --relation member
Error: accepts 2 arg(s), received 1C:\Users\RJ\Documents\fgatestmodel>fga query list-relations role:admin --relation member
Error: accepts 2 arg(s), received 1C:\Users\RJ\Documents\fgatestmodel>fga query list-relations role:admin user --relation member
{"relations": []
}C:\Users\RJ\Documents\fgatestmodel>fga query list-relations role:admin user:jmj --relation member
{"relations": []
}C:\Users\RJ\Documents\fgatestmodel>fga query list-relations user:jmj role:admin --relation member
{"relations": ["member"]
}C:\Users\RJ\Documents\fgatestmodel>fga query expand member role:admin
{"tree": {"root": {"leaf": {"users": {"users": ["user:bzm","user:jmj"]}},"name":"role:admin#member"}}
}C:\Users\RJ\Documents\fgatestmodel>fga query list-uers --object role:admin --relation member --user0filter user
Error: unknown flag: --objectC:\Users\RJ\Documents\fgatestmodel>fga query list-uers --object role:admin --relation member --userfilter user
Error: unknown flag: --objectC:\Users\RJ\Documents\fgatestmodel>fga query list-uers --object role:admin --relation member --user-filter user
Error: unknown flag: --objectC:\Users\RJ\Documents\fgatestmodel>fga query list-uers --object role:admin --relation member --user-filter user
Error: unknown flag: --objectC:\Users\RJ\Documents\fgatestmodel>fga query list-users --object role:admin --relation member --user-filter user
{"users": [{"object": {"id":"jmj","type":"user"}},{"object": {"id":"bzm","type":"user"}}]
}C:\Users\RJ\Documents\fgatestmodel>fga tuple write role:admin#member viewer application:tigeriotapp
{"successful": [{"object":"application:tigeriotapp","relation":"viewer","user":"role:admin#member"}]
}C:\Users\RJ\Documents\fgatestmodel>fga tuple changes --type application
{"changes": [{"operation":"TUPLE_OPERATION_WRITE","timestamp":"2025-01-25T03:22:43Z","tuple_key": {"object":"application:tigeriotapp","relation":"viewer","user":"role:admin#member"}}],"continuation_token":"eyJ1bGlkIjoiMDFKSkRRUUQyQkozQ1M2RDE2NzY4MTBDSloiLCJPYmplY3RUeXBlIjoiYXBwbGljYXRpb24ifQ=="
}C:\Users\RJ\Documents\fgatestmodel>fga tuple write role:admin#1member viewer application:tigeriotapp
Error: failed to write tuple: Write validation error for POST Write with body {"code":"validation_error","message":"Invalid tuple 'application:tigeriotapp#viewer@role:admin#1member'. Reason: relation 'role#1member' not found"} with error code validation_error error message: Invalid tuple 'application:tigeriotapp#viewer@role:admin#1member'. Reason: relation 'role#1member' not foundC:\Users\RJ\Documents\fgatestmodel>fga query check user:jmj can_view application:tigeriotapp
{"allowed":true,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query check user:bzm can_view application:tigeriotapp
{"allowed":true,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query check user:cc can_view application:tigeriotapp
{"allowed":false,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query check user:cc can_view application:tigeriotapp --contextual-tuple "user:cc member role:admin"
{"allowed":true,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga model list
{"authorization_models": [{"id":"01JJDP39W07DXX87KS937GKK9S","created_at":"2025-01-25T02:54:15.936Z"}]
}C:\Users\RJ\Documents\fgatestmodel>fga tuple write application:tigeriotapp parent menu:erwangpingheng
Error: failed to write tuple: Write validation error for POST Write with body {"code":"validation_error","message":"Invalid tuple 'menu:erwangpingheng#parent@application:tigeriotapp'. Reason: type 'menu' not found"}with error code validation_error error message: Invalid tuple 'menu:erwangpingheng#parent@application:tigeriotapp'. Reason: type 'menu' not foundC:\Users\RJ\Documents\fgatestmodel>fga model write --file =fga.mod
Error: failed to read file =fga.mod due to open =fga.mod: The system cannot find the file specified.C:\Users\RJ\Documents\fgatestmodel>fga model write --file=fga.mod
{"authorization_model_id":"01JJDRB8QK912Y0EW06BMT7VG4"
}C:\Users\RJ\Documents\fgatestmodel>fga tuple write application:tigeriotapp parent menu:erwangpingheng
{"successful": [{"object":"menu:erwangpingheng","relation":"parent","user":"application:tigeriotapp"}]
}C:\Users\RJ\Documents\fgatestmodel>fga query check user:jmj member role:admin
{"allowed":true,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query check role:admin#rember viewer application:tigeriotapp
Error: check failed: Check validation error for POST Check with body {"code":"validation_error","message":"relation 'role#rember' not found"}with error code validation_error error message: relation 'role#rember' not foundC:\Users\RJ\Documents\fgatestmodel>fga query check role:admin#member viewer application:tigeriotapp
{"allowed":true,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query check role:admin#member can_view application:tigeriotapp
{"allowed":true,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query check role:admin add menu:erwangpingheng
{"allowed":false,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query check application:tigeriotapp add menu:erwangpingheng
{"allowed":false,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query check application:tigeriotapp parent menu:erwangpingheng
{"allowed":true,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query expand add menu:erwangpingheng
{"tree": {"root": {"leaf": {"tupleToUserset": {"computed": [{"userset":"application:tigeriotapp#can_view"}],"tupleset":"menu:erwangpingheng#parent"}},"name":"menu:erwangpingheng#add"}}
}C:\Users\RJ\Documents\fgatestmodel>fga query user:jmj add menu:erwangpingheng
Run queries (Check, Expand, ListObjects, ListRelations, ListUsers) that are evaluated according to a particular model.Usage:fga query [command]Available Commands:check Checkexpand Expandlist-objects List Objectslist-relations List Relationslist-users List usersFlags:--consistency string Consistency preference for the request. Valid options are HIGHER_CONSISTENCY and MINIMIZE_LATENCY.--context string Query context (as a JSON string)--contextual-tuple stringArray Contextual Tuple, output: "user relation object"-h, --help help for query--model-id string Model ID--store-id string Store IDGlobal Flags:--api-audience string API Audience. Used when performing the Client Credentials flow--api-scopes stringArray API Scopes (repeat option for multiple values). Used in the Client Credentials flow--api-token string API Token. Will be sent in as a Bearer in the Authorization header--api-token-issuer string API Token Issuer. API responsible for issuing the API Token. Used in the Client Credentials flow--api-url string OpenFGA API URI e.g. https://api.fga.example:8080 (default "http://localhost:8080")--client-id string Client ID. Sent to the Token Issuer during the Client Credentials flow--client-secret string Client Secret. Sent to the Token Issuer during the Client Credentials flow--config string config file (default is $HOME/.fga.yaml)Use "fga query [command] --help" for more information about a command.C:\Users\RJ\Documents\fgatestmodel>fga query check user:jmj add menu:erwangpingheng
{"allowed":true,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query check user:jmj add menu:erwangpingheng
{"allowed":true,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query check user:cc add menu:erwangpingheng
{"allowed":false,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query check user:jmj add menu:erwangpingheng
{"allowed":true,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query check role:admin add menu:erwangpingheng
{"allowed":false,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query check role:admin#member add menu:erwangpingheng
{"allowed":true,"resolution":""
}C:\Users\RJ\Documents\fgatestmodel>fga query check user:jmj add menu:erwangpingheng
{"allowed":true,"resolution":""
}
把我的测试记录保存下来了
测试没问题,下一章节我们将 实战入我们自己的项目
5.整合Java SDK
<dependency><groupId>dev.openfga</groupId><artifactId>openfga-sdk</artifactId><version>0.7.1</version>
</dependency>
6.创建一个新的项目
7.导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.4.2</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.jmj</groupId><artifactId>openFGA-snap</artifactId><version>0.0.1-SNAPSHOT</version><name>openFGA-snap</name><description>openFGA-snap</description><url/><licenses><license/></licenses><developers><developer/></developers><scm><connection/><developerConnection/><tag/><url/></scm><properties><java.version>21</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>dev.openfga</groupId><artifactId>openfga-sdk</artifactId><version>0.3.1</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><annotationProcessorPaths><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></path></annotationProcessorPaths></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>
8.从头开始 创建Store
模型 store -> authmodel -> tuple
一个store 里的 授权模型 是以版本号迭代的 ,也就是一个 store 就一个 授权模型,你可以使用不同版本
我们以模块化开发授权模型
1.fga.mod 归纳模块
schema: '1.2'
contents:- core.fga
2.core.fga 核心模块
module core#核心模块
#用户类型
type user #角色类型
#关系
# 成员 :member :用户类型
type role relationsdefine member: [user] #应用类型
#关系
# can_access:能否访问: 角色成员用户集
type application relationsdefine can_access: [role#member] #页面模块分组
#关系
#parent_model父模型
#可见身份: 用户成员组绑定
#访问身份: 用户成员组绑定 且 必须父模型要有访问权限
#能否查看 查看者 与 访问者 都可以访问
#能否访问 只有访问者可以访问
type page_module_group relationsdefine parent_model: [application] define viewer: [role#member]define accesser: [role#member] and can_access from parent_modeldefine can_viewer: viewer or accesserdefine can_access: accesser#页面模块
#关系
#parent_model父模型
#可见身份: 用户成员组绑定
#访问身份: 用户成员组绑定 且 必须父模型要有访问权限
#能否查看 查看者 与 访问者 都可以访问
#能否访问 只有访问者可以访问
type page_modulerelationsdefine parent_model: [page_module_group] define viewer: [role#member]define accesser: [role#member] and can_access from parent_modeldefine can_viewer: viewer or accesserdefine can_access: accesser#页面菜单
#关系
#parent_model父模型
#可见身份: 用户成员组绑定
#访问身份: 用户成员组绑定 且 必须父模型要有访问权限
#能否查看 查看者 与 访问者 都可以访问
#能否访问 只有访问者可以访问
type page_menurelationsdefine parent_model: [page_module] define viewer: [role#member]define accesser: [role#member] and can_access from parent_modeldefine can_viewer: viewer or accesserdefine can_access: accesser#页面界面
#关系
#parent_model父模型
#可见身份: 用户成员组绑定
#访问身份: 用户成员组绑定 且 必须父模型要有访问权限
#能否查看 查看者 与 访问者 都可以访问
#能否访问 只有访问者可以访问
type page_viewrelationsdefine parent_model: [page_menu] define viewer: [role#member]define accesser: [role#member] and can_access from parent_modeldefine can_viewer: viewer or accesserdefine can_access: accesser
配好环境变量
fga model write --file=fga.mod
一个store 就专注于一个 授权模型,由版本进行迭代维护升级,而不是 多个授权模型
9. 创建一个Store
package com.jmj.openfgatest.openfga.init;import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.client.model.ClientCreateStoreResponse;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.api.model.CreateStoreRequest;
import dev.openfga.sdk.errors.FgaInvalidParameterException;import java.util.concurrent.ExecutionException;public class CreateStore {public static void main(String[] args) throws FgaInvalidParameterException, ExecutionException, InterruptedException {ClientConfiguration clientConfiguration = new ClientConfiguration().apiUrl("http://192.168.59.100:8080");OpenFgaClient openFgaClient = new OpenFgaClient(clientConfiguration);CreateStoreRequest body = new CreateStoreRequest().name("authorTreePage");ClientCreateStoreResponse store = openFgaClient.createStore(body).get();System.out.println(store.getId());}
}
10.创建一个授权模型
package com.jmj.openfgatest.openfga.init;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.client.model.ClientWriteAuthorizationModelResponse;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.api.model.WriteAuthorizationModelRequest;
import dev.openfga.sdk.errors.FgaInvalidParameterException;import java.util.concurrent.ExecutionException;public class CreateAuthorizeModel {public static void main(String[] args) throws FgaInvalidParameterException, JsonProcessingException, ExecutionException, InterruptedException {ClientConfiguration clientConfiguration = new ClientConfiguration().apiUrl("http://192.168.59.100:8080").storeId("01JJN8S18TRYPMTXHG0JE50402");OpenFgaClient openFgaClient = new OpenFgaClient(clientConfiguration);ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();ClientWriteAuthorizationModelResponse authorizationModelResponse = openFgaClient.writeAuthorizationModel(mapper.readValue("{\n" +" \"schema_version\": \"1.1\",\n" +" \"type_definitions\": [\n" +" {\n" +" \"type\": \"user\",\n" +" \"relations\": {},\n" +" \"metadata\": null\n" +" },\n" +" {\n" +" \"type\": \"role\",\n" +" \"relations\": {\n" +" \"member\": {\n" +" \"this\": {}\n" +" }\n" +" },\n" +" \"metadata\": {\n" +" \"relations\": {\n" +" \"member\": {\n" +" \"directly_related_user_types\": [\n" +" {\n" +" \"type\": \"user\"\n" +" }\n" +" ]\n" +" }\n" +" }\n" +" }\n" +" },\n" +" {\n" +" \"type\": \"application\",\n" +" \"relations\": {\n" +" \"can_access\": {\n" +" \"this\": {}\n" +" }\n" +" },\n" +" \"metadata\": {\n" +" \"relations\": {\n" +" \"can_access\": {\n" +" \"directly_related_user_types\": [\n" +" {\n" +" \"type\": \"role\",\n" +" \"relation\": \"member\"\n" +" }\n" +" ]\n" +" }\n" +" }\n" +" }\n" +" },\n" +" {\n" +" \"type\": \"page_module_group\",\n" +" \"relations\": {\n" +" \"parent_model\": {\n" +" \"this\": {}\n" +" },\n" +" \"viewer\": {\n" +" \"this\": {}\n" +" },\n" +" \"accesser\": {\n" +" \"intersection\": {\n" +" \"child\": [\n" +" {\n" +" \"this\": {}\n" +" },\n" +" {\n" +" \"tupleToUserset\": {\n" +" \"computedUserset\": {\n" +" \"relation\": \"can_access\"\n" +" },\n" +" \"tupleset\": {\n" +" \"relation\": \"parent_model\"\n" +" }\n" +" }\n" +" }\n" +" ]\n" +" }\n" +" },\n" +" \"can_viewer\": {\n" +" \"union\": {\n" +" \"child\": [\n" +" {\n" +" \"computedUserset\": {\n" +" \"relation\": \"viewer\"\n" +" }\n" +" },\n" +" {\n" +" \"computedUserset\": {\n" +" \"relation\": \"accesser\"\n" +" }\n" +" }\n" +" ]\n" +" }\n" +" },\n" +" \"can_access\": {\n" +" \"computedUserset\": {\n" +" \"relation\": \"accesser\"\n" +" }\n" +" }\n" +" },\n" +" \"metadata\": {\n" +" \"relations\": {\n" +" \"parent_model\": {\n" +" \"directly_related_user_types\": [\n" +" {\n" +" \"type\": \"application\"\n" +" }\n" +" ]\n" +" },\n" +" \"viewer\": {\n" +" \"directly_related_user_types\": [\n" +" {\n" +" \"type\": \"role\",\n" +" \"relation\": \"member\"\n" +" }\n" +" ]\n" +" },\n" +" \"accesser\": {\n" +" \"directly_related_user_types\": [\n" +" {\n" +" \"type\": \"role\",\n" +" \"relation\": \"member\"\n" +" }\n" +" ]\n" +" },\n" +" \"can_viewer\": {\n" +" \"directly_related_user_types\": []\n" +" },\n" +" \"can_access\": {\n" +" \"directly_related_user_types\": []\n" +" }\n" +" }\n" +" }\n" +" },\n" +" {\n" +" \"type\": \"page_module\",\n" +" \"relations\": {\n" +" \"parent_model\": {\n" +" \"this\": {}\n" +" },\n" +" \"viewer\": {\n" +" \"this\": {}\n" +" },\n" +" \"accesser\": {\n" +" \"intersection\": {\n" +" \"child\": [\n" +" {\n" +" \"this\": {}\n" +" },\n" +" {\n" +" \"tupleToUserset\": {\n" +" \"computedUserset\": {\n" +" \"relation\": \"can_access\"\n" +" },\n" +" \"tupleset\": {\n" +" \"relation\": \"parent_model\"\n" +" }\n" +" }\n" +" }\n" +" ]\n" +" }\n" +" },\n" +" \"can_viewer\": {\n" +" \"union\": {\n" +" \"child\": [\n" +" {\n" +" \"computedUserset\": {\n" +" \"relation\": \"viewer\"\n" +" }\n" +" },\n" +" {\n" +" \"computedUserset\": {\n" +" \"relation\": \"accesser\"\n" +" }\n" +" }\n" +" ]\n" +" }\n" +" },\n" +" \"can_access\": {\n" +" \"computedUserset\": {\n" +" \"relation\": \"accesser\"\n" +" }\n" +" }\n" +" },\n" +" \"metadata\": {\n" +" \"relations\": {\n" +" \"parent_model\": {\n" +" \"directly_related_user_types\": [\n" +" {\n" +" \"type\": \"page_module_group\"\n" +" }\n" +" ]\n" +" },\n" +" \"viewer\": {\n" +" \"directly_related_user_types\": [\n" +" {\n" +" \"type\": \"role\",\n" +" \"relation\": \"member\"\n" +" }\n" +" ]\n" +" },\n" +" \"accesser\": {\n" +" \"directly_related_user_types\": [\n" +" {\n" +" \"type\": \"role\",\n" +" \"relation\": \"member\"\n" +" }\n" +" ]\n" +" },\n" +" \"can_viewer\": {\n" +" \"directly_related_user_types\": []\n" +" },\n" +" \"can_access\": {\n" +" \"directly_related_user_types\": []\n" +" }\n" +" }\n" +" }\n" +" },\n" +" {\n" +" \"type\": \"page_menu\",\n" +" \"relations\": {\n" +" \"parent_model\": {\n" +" \"this\": {}\n" +" },\n" +" \"viewer\": {\n" +" \"this\": {}\n" +" },\n" +" \"accesser\": {\n" +" \"intersection\": {\n" +" \"child\": [\n" +" {\n" +" \"this\": {}\n" +" },\n" +" {\n" +" \"tupleToUserset\": {\n" +" \"computedUserset\": {\n" +" \"relation\": \"can_access\"\n" +" },\n" +" \"tupleset\": {\n" +" \"relation\": \"parent_model\"\n" +" }\n" +" }\n" +" }\n" +" ]\n" +" }\n" +" },\n" +" \"can_viewer\": {\n" +" \"union\": {\n" +" \"child\": [\n" +" {\n" +" \"computedUserset\": {\n" +" \"relation\": \"viewer\"\n" +" }\n" +" },\n" +" {\n" +" \"computedUserset\": {\n" +" \"relation\": \"accesser\"\n" +" }\n" +" }\n" +" ]\n" +" }\n" +" },\n" +" \"can_access\": {\n" +" \"computedUserset\": {\n" +" \"relation\": \"accesser\"\n" +" }\n" +" }\n" +" },\n" +" \"metadata\": {\n" +" \"relations\": {\n" +" \"parent_model\": {\n" +" \"directly_related_user_types\": [\n" +" {\n" +" \"type\": \"page_module\"\n" +" }\n" +" ]\n" +" },\n" +" \"viewer\": {\n" +" \"directly_related_user_types\": [\n" +" {\n" +" \"type\": \"role\",\n" +" \"relation\": \"member\"\n" +" }\n" +" ]\n" +" },\n" +" \"accesser\": {\n" +" \"directly_related_user_types\": [\n" +" {\n" +" \"type\": \"role\",\n" +" \"relation\": \"member\"\n" +" }\n" +" ]\n" +" },\n" +" \"can_viewer\": {\n" +" \"directly_related_user_types\": []\n" +" },\n" +" \"can_access\": {\n" +" \"directly_related_user_types\": []\n" +" }\n" +" }\n" +" }\n" +" },\n" +" {\n" +" \"type\": \"page_view\",\n" +" \"relations\": {\n" +" \"parent_model\": {\n" +" \"this\": {}\n" +" },\n" +" \"viewer\": {\n" +" \"this\": {}\n" +" },\n" +" \"accesser\": {\n" +" \"intersection\": {\n" +" \"child\": [\n" +" {\n" +" \"this\": {}\n" +" },\n" +" {\n" +" \"tupleToUserset\": {\n" +" \"computedUserset\": {\n" +" \"relation\": \"can_access\"\n" +" },\n" +" \"tupleset\": {\n" +" \"relation\": \"parent_model\"\n" +" }\n" +" }\n" +" }\n" +" ]\n" +" }\n" +" },\n" +" \"can_viewer\": {\n" +" \"union\": {\n" +" \"child\": [\n" +" {\n" +" \"computedUserset\": {\n" +" \"relation\": \"viewer\"\n" +" }\n" +" },\n" +" {\n" +" \"computedUserset\": {\n" +" \"relation\": \"accesser\"\n" +" }\n" +" }\n" +" ]\n" +" }\n" +" },\n" +" \"can_access\": {\n" +" \"computedUserset\": {\n" +" \"relation\": \"accesser\"\n" +" }\n" +" }\n" +" },\n" +" \"metadata\": {\n" +" \"relations\": {\n" +" \"parent_model\": {\n" +" \"directly_related_user_types\": [\n" +" {\n" +" \"type\": \"page_menu\"\n" +" }\n" +" ]\n" +" },\n" +" \"viewer\": {\n" +" \"directly_related_user_types\": [\n" +" {\n" +" \"type\": \"role\",\n" +" \"relation\": \"member\"\n" +" }\n" +" ]\n" +" },\n" +" \"accesser\": {\n" +" \"directly_related_user_types\": [\n" +" {\n" +" \"type\": \"role\",\n" +" \"relation\": \"member\"\n" +" }\n" +" ]\n" +" },\n" +" \"can_viewer\": {\n" +" \"directly_related_user_types\": []\n" +" },\n" +" \"can_access\": {\n" +" \"directly_related_user_types\": []\n" +" }\n" +" }\n" +" }\n" +" }\n" +" ]\n" +"}", WriteAuthorizationModelRequest.class)).get();System.out.println(authorizationModelResponse.getAuthorizationModelId());}
}
11.创建一个配置属性类用于放入 openfga的配置
package com.jmj.openfgatest.openfga.configuration;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties("openfga")
@Data
public class OpenfgaConfigurationProperties {private String storeId;private String apiUrl;private String authorizeModelId;}
12.创建一个配置类
package com.jmj.openfgatest.openfga.configuration;import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.errors.FgaInvalidParameterException;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@EnableConfigurationProperties(OpenfgaConfigurationProperties.class)
public class OpenfgaAutoConfiguration {@Beanpublic OpenFgaClient openFgaClient(OpenfgaConfigurationProperties openfgaConfigurationProperties) throws FgaInvalidParameterException {String apiUrl = openfgaConfigurationProperties.getApiUrl();String storeId = openfgaConfigurationProperties.getStoreId();String authorizeModelId = openfgaConfigurationProperties.getAuthorizeModelId();ClientConfiguration clientConfiguration = new ClientConfiguration().apiUrl(apiUrl).storeId(storeId).authorizationModelId(authorizeModelId);return new OpenFgaClient(clientConfiguration);}}
13.在yaml里配置
openfga:api-url: http://192.168.59.100:8080store-id: 01JJN8S18TRYPMTXHG0JE50402authorize-model-id: 01JJN9399F4YR3DZ7KJ65MZ3RW
14.openfga controller
package com.jmj.openfgatest.controller;import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.jmj.openfgatest.common.TreePageEntityConst;
import com.jmj.openfgatest.common.TreeUtils;
import com.jmj.openfgatest.entity.BatchCheckRequest;
import com.jmj.openfgatest.entity.ListByUserRequest;
import com.jmj.openfgatest.entity.TreePageEntity;
import com.jmj.openfgatest.entity.TreePageNode;
import com.jmj.openfgatest.openfga.configuration.OpenfgaConfigurationProperties;
import com.jmj.openfgatest.repository.TreePageEntityRepository;
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.client.model.*;
import dev.openfga.sdk.errors.FgaInvalidParameterException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;@RestController
@RequestMapping("/treePage")
public class TreePageController {@Autowiredprivate TreePageEntityRepository treePageEntityRepository;@Autowiredprivate OpenFgaClient openFgaClient;@GetMapping("/create")@Transactional(rollbackFor = Exception.class)public String create() throws FgaInvalidParameterException, ExecutionException, InterruptedException {try {//创建基本数据List<TreePageEntity> list = treePageEntityRepository.findByType(TreePageEntityConst.Type.PAGE_MENU.value);ArrayList<TreePageEntity> arrayList = new ArrayList<>();for (TreePageEntity t : list) {for (int i = 0; i < 1; i++) {TreePageEntity treePageEntity = new TreePageEntity();treePageEntity.setName("模型界面" + i);treePageEntity.setType(TreePageEntityConst.Type.PAGE_VIEW.value);treePageEntity.setParentId(t.getId());arrayList.add(treePageEntity);}}treePageEntityRepository.saveAll(arrayList);List<ClientTupleKey> parentModel = arrayList.stream().map(t -> {return new ClientTupleKey().user(TreePageEntityConst.Type.PAGE_MENU.model + ":" + t.getParentId()).relation("parent_model")._object(TreePageEntityConst.Type.PAGE_VIEW.model + ":" + t.getId());}).collect(Collectors.toList());System.out.println(parentModel.size());ArrayList<ClientTupleKey> clientTupleKeys = new ArrayList<>();for (int i = 0; i < parentModel.size(); i++) {clientTupleKeys.add(parentModel.get(i));if ((i + 1) % 10 == 0) {System.out.println("=============================================");for (ClientTupleKey clientTupleKey : clientTupleKeys) {System.out.println(StrUtil.format("user:{},relation:{},object:{}",clientTupleKey.getUser(),clientTupleKey.getRelation(),clientTupleKey.getObject()));}CompletableFuture<ClientWriteResponse> write = openFgaClient.write(new ClientWriteRequest().writes(clientTupleKeys));ClientWriteResponse clientWriteResponse = write.get();System.out.println(clientWriteResponse.getRawResponse());clientTupleKeys.clear();}}} catch (Exception e) {e.printStackTrace();throw e;}return "成功";}@GetMapping("/check")@Transactional(rollbackFor = Exception.class)public String check() throws FgaInvalidParameterException, ExecutionException, InterruptedException {ClientCheckRequest parentModel = new ClientCheckRequest().user(TreePageEntityConst.Type.PAGE_MODULE.model + ":" + "2011").relation("parent_model")._object(TreePageEntityConst.Type.PAGE_MENU.model + ":" + "3011");ClientCheckResponse clientCheckResponse = openFgaClient.check(parentModel).get();System.out.println(clientCheckResponse);return clientCheckResponse.getAllowed() + "";}@GetMapping("/list")public List<TreePageNode> list() throws FgaInvalidParameterException, ExecutionException, InterruptedException {List<TreePageEntity> all = treePageEntityRepository.findAll();List<TreePageNode> collect = all.stream().map(TreePageNode::of).collect(Collectors.toList());System.out.println(all.size());return TreeUtils.buildTree(collect);}@GetMapping("/listObjByOpenfga")public List<TreePageEntity> listObjByOpenfga() throws FgaInvalidParameterException, ExecutionException, InterruptedException {var body = new ClientListObjectsRequest().user(TreePageEntityConst.Type.PAGE_MODULE.model + ":" + "2011").relation("parent_model").type(TreePageEntityConst.Type.PAGE_MENU.model);CompletableFuture<ClientListObjectsResponse> clientListObjectsResponseCompletableFuture = openFgaClient.listObjects(body);ClientListObjectsResponse clientListObjectsResponse = clientListObjectsResponseCompletableFuture.get();System.out.println(clientListObjectsResponse.getObjects());List<TreePageEntity> byParentId = treePageEntityRepository.findByParentId(2011L);System.out.println(byParentId.stream().map(TreePageEntity::getId).toList());return byParentId;}@GetMapping("/listRelationByOpenfga")public List<String> listRelationByOpenfga() throws FgaInvalidParameterException, ExecutionException, InterruptedException {ClientListRelationsResponse clientListRelationsResponse = openFgaClient.listRelations(new ClientListRelationsRequest().user(TreePageEntityConst.Type.PAGE_MODULE.model + ":" + "2011").relations(List.of("parent_model", "viewer", "accesser"))._object(TreePageEntityConst.Type.PAGE_MENU.model + ":" + 3011)).get();System.out.println(clientListRelationsResponse.getRelations());return clientListRelationsResponse.getRelations();}@Autowiredprivate OpenfgaConfigurationProperties openfgaConfigurationProperties;@GetMapping("/listUserByOpenfgaApi")public String listUserByOpenfga() throws FgaInvalidParameterException, ExecutionException, InterruptedException {ListByUserRequest listByUserRequest = new ListByUserRequest();listByUserRequest.setAuthorization_model_id(openfgaConfigurationProperties.getAuthorizeModelId());listByUserRequest.setObject(new ListByUserRequest.ObjectRequest("page_module_group", "1891"));listByUserRequest.setRelation("parent_model");listByUserRequest.setUser_filters(List.of(new ListByUserRequest.TypeRequest("application")));String url = StrUtil.format(openfgaConfigurationProperties.getApiUrl() + "/stores/{}/list-users", openfgaConfigurationProperties.getStoreId());HttpResponse execute = HttpRequest.post(url).body(JSONUtil.toJsonStr(listByUserRequest)).execute();System.out.println(execute.body());return execute.body();}@GetMapping("/batchCheck")public String batchCheck() throws FgaInvalidParameterException, ExecutionException, InterruptedException {BatchCheckRequest batchCheckRequest =new BatchCheckRequest();batchCheckRequest.setAuthorization_model_id(openfgaConfigurationProperties.getAuthorizeModelId());batchCheckRequest.setChecks(List.of(new BatchCheckRequest.Check("1",new BatchCheckRequest.TupleKey("page_menu:10000","parent_model","page_module:2709")),new BatchCheckRequest.Check("2",new BatchCheckRequest.TupleKey("page_menu:asd","parent_model","page_view:83011"))));String url = StrUtil.format(openfgaConfigurationProperties.getApiUrl() + "/stores/{}/batch-check", openfgaConfigurationProperties.getStoreId());HttpResponse execute = HttpRequest.post(url).body(JSONUtil.toJsonStr(batchCheckRequest)).execute();System.out.println(execute.body());return execute.body();}
}
15.注意
这里的批量check不好用 SDK 里面会开启线程池 ,而且不关闭 很浪费资源,源码里
我们采用HTTP 调用API
@GetMapping("/batchCheck")public String batchCheck() throws FgaInvalidParameterException, ExecutionException, InterruptedException {BatchCheckRequest batchCheckRequest =new BatchCheckRequest();batchCheckRequest.setAuthorization_model_id(openfgaConfigurationProperties.getAuthorizeModelId());batchCheckRequest.setChecks(List.of(new BatchCheckRequest.Check("1",new BatchCheckRequest.TupleKey("page_menu:10000","parent_model","page_module:2709")),new BatchCheckRequest.Check("2",new BatchCheckRequest.TupleKey("page_menu:asd","parent_model","page_view:83011"))));String url = StrUtil.format(openfgaConfigurationProperties.getApiUrl() + "/stores/{}/batch-check", openfgaConfigurationProperties.getStoreId());HttpResponse execute = HttpRequest.post(url).body(JSONUtil.toJsonStr(batchCheckRequest)).execute();System.out.println(execute.body());return execute.body();}
就这个调用api就可以,其他的暂时调用 SDK没有问题
package com.jmj.openfgatest.controller;import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.jmj.openfgatest.common.TreePageEntityConst;
import com.jmj.openfgatest.common.TreeUtils;
import com.jmj.openfgatest.entity.BatchCheckRequest;
import com.jmj.openfgatest.entity.ListByUserRequest;
import com.jmj.openfgatest.entity.TreePageEntity;
import com.jmj.openfgatest.entity.TreePageNode;
import com.jmj.openfgatest.openfga.configuration.OpenfgaConfigurationProperties;
import com.jmj.openfgatest.repository.TreePageEntityRepository;
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.client.model.*;
import dev.openfga.sdk.api.model.FgaObject;
import dev.openfga.sdk.api.model.User;
import dev.openfga.sdk.api.model.UserTypeFilter;
import dev.openfga.sdk.errors.FgaInvalidParameterException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;@RestController
@RequestMapping("/treePage")
public class TreePageController {@Autowiredprivate TreePageEntityRepository treePageEntityRepository;@Autowiredprivate OpenFgaClient openFgaClient;@GetMapping("/create")@Transactional(rollbackFor = Exception.class)public String create() throws FgaInvalidParameterException, ExecutionException, InterruptedException {try {//创建基本数据List<TreePageEntity> list = treePageEntityRepository.findByType(TreePageEntityConst.Type.PAGE_MENU.value);ArrayList<TreePageEntity> arrayList = new ArrayList<>();for (TreePageEntity t : list) {for (int i = 0; i < 1; i++) {TreePageEntity treePageEntity = new TreePageEntity();treePageEntity.setName("模型界面" + i);treePageEntity.setType(TreePageEntityConst.Type.PAGE_VIEW.value);treePageEntity.setParentId(t.getId());arrayList.add(treePageEntity);}}treePageEntityRepository.saveAll(arrayList);List<ClientTupleKey> parentModel = arrayList.stream().map(t -> {return new ClientTupleKey().user(TreePageEntityConst.Type.PAGE_MENU.model + ":" + t.getParentId()).relation("parent_model")._object(TreePageEntityConst.Type.PAGE_VIEW.model + ":" + t.getId());}).collect(Collectors.toList());System.out.println(parentModel.size());ArrayList<ClientTupleKey> clientTupleKeys = new ArrayList<>();for (int i = 0; i < parentModel.size(); i++) {clientTupleKeys.add(parentModel.get(i));if ((i + 1) % 10 == 0) {System.out.println("=============================================");for (ClientTupleKey clientTupleKey : clientTupleKeys) {System.out.println(StrUtil.format("user:{},relation:{},object:{}",clientTupleKey.getUser(),clientTupleKey.getRelation(),clientTupleKey.getObject()));}CompletableFuture<ClientWriteResponse> write = openFgaClient.write(new ClientWriteRequest().writes(clientTupleKeys));ClientWriteResponse clientWriteResponse = write.get();System.out.println(clientWriteResponse.getRawResponse());clientTupleKeys.clear();}}} catch (Exception e) {e.printStackTrace();throw e;}return "成功";}@GetMapping("/check")@Transactional(rollbackFor = Exception.class)public String check() throws FgaInvalidParameterException, ExecutionException, InterruptedException {ClientCheckRequest parentModel = new ClientCheckRequest().user(TreePageEntityConst.Type.PAGE_MODULE.model + ":" + "2011").relation("parent_model")._object(TreePageEntityConst.Type.PAGE_MENU.model + ":" + "3011");ClientCheckResponse clientCheckResponse = openFgaClient.check(parentModel).get();System.out.println(clientCheckResponse);return clientCheckResponse.getAllowed() + "";}@GetMapping("/list")public List<TreePageNode> list() throws FgaInvalidParameterException, ExecutionException, InterruptedException {List<TreePageEntity> all = treePageEntityRepository.findAll();List<TreePageNode> collect = all.stream().map(TreePageNode::of).collect(Collectors.toList());System.out.println(all.size());return TreeUtils.buildTree(collect);}@GetMapping("/listObjByOpenfga")public List<TreePageEntity> listObjByOpenfga() throws FgaInvalidParameterException, ExecutionException, InterruptedException {var body = new ClientListObjectsRequest().user( "user:" + "jmj").relation("can_access").type(TreePageEntityConst.Type.PAGE_VIEW.model);CompletableFuture<ClientListObjectsResponse> clientListObjectsResponseCompletableFuture = openFgaClient.listObjects(body);ClientListObjectsResponse clientListObjectsResponse = clientListObjectsResponseCompletableFuture.get();System.out.println(clientListObjectsResponse.getObjects());//List<TreePageEntity> byParentId = treePageEntityRepository.findByParentId(2011L);
// System.out.println(byParentId.stream().map(TreePageEntity::getId).toList());return byParentId;}@GetMapping("/listRelationByOpenfga")public List<String> listRelationByOpenfga() throws FgaInvalidParameterException, ExecutionException, InterruptedException {ClientListRelationsResponse clientListRelationsResponse = openFgaClient.listRelations(new ClientListRelationsRequest().user(TreePageEntityConst.Type.PAGE_MODULE.model + ":" + "2011").relations(List.of("parent_model", "viewer", "accesser"))._object(TreePageEntityConst.Type.PAGE_MENU.model + ":" + 3011)).get();System.out.println(clientListRelationsResponse.getRelations());return clientListRelationsResponse.getRelations();}@Autowiredprivate OpenfgaConfigurationProperties openfgaConfigurationProperties;@GetMapping("/listUserByOpenfgaApi")public String listUserByOpenfga() throws FgaInvalidParameterException, ExecutionException, InterruptedException {ListByUserRequest listByUserRequest = new ListByUserRequest();listByUserRequest.setAuthorization_model_id(openfgaConfigurationProperties.getAuthorizeModelId());listByUserRequest.setObject(new ListByUserRequest.ObjectRequest("page_module_group", "1011"));listByUserRequest.setRelation("parent_model");listByUserRequest.setUser_filters(List.of(new ListByUserRequest.TypeRequest("application")));String url = StrUtil.format(openfgaConfigurationProperties.getApiUrl() + "/stores/{}/list-users", openfgaConfigurationProperties.getStoreId());HttpResponse execute = HttpRequest.post(url).body(JSONUtil.toJsonStr(listByUserRequest)).execute();System.out.println(execute.body());return execute.body();}@GetMapping("/listUserByOpenfgaSDK")public List<User> listUserByOpenfgaSDK() throws FgaInvalidParameterException, ExecutionException, InterruptedException {var userFilters = new ArrayList<UserTypeFilter>() {{add(new UserTypeFilter().type("user"));}};CompletableFuture<ClientListUsersResponse> clientListUsersResponseCompletableFuture = openFgaClient.listUsers(new ClientListUsersRequest()._object(new FgaObject().type("page_view").id("93011")).relation("can_access").userFilters(userFilters));ClientListUsersResponse clientListUsersResponse = clientListUsersResponseCompletableFuture.get();List<User> users = clientListUsersResponse.getUsers();for (User user : users) {System.out.println(user.getUserset());}System.out.println(users);return users;}@GetMapping("/batchCheck")public String batchCheck() throws FgaInvalidParameterException, ExecutionException, InterruptedException {BatchCheckRequest batchCheckRequest =new BatchCheckRequest();batchCheckRequest.setAuthorization_model_id(openfgaConfigurationProperties.getAuthorizeModelId());batchCheckRequest.setChecks(List.of(new BatchCheckRequest.Check("1",new BatchCheckRequest.TupleKey("page_menu:10000","parent_model","page_module:2709")),new BatchCheckRequest.Check("2",new BatchCheckRequest.TupleKey("page_menu:asd","parent_model","page_view:83011"))));String url = StrUtil.format(openfgaConfigurationProperties.getApiUrl() + "/stores/{}/batch-check", openfgaConfigurationProperties.getStoreId());HttpResponse execute = HttpRequest.post(url).body(JSONUtil.toJsonStr(batchCheckRequest)).execute();System.out.println(execute.body());return execute.body();}
}