Openfga 授权模型搭建
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:8
container_name: mysql
restart: always
networks:
- openfga
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=openfga
healthcheck:
test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]
timeout: 20s
retries: 5
migrate:
depends_on:
mysql:
condition: service_healthy
image: openfga/openfga:latest
container_name: migrate
command: migrate
environment:
- OPENFGA_DATASTORE_ENGINE=mysql
- OPENFGA_DATASTORE_URI=root:secret@tcp(mysql:3306)/openfga?parseTime=true
networks:
- openfga
openfga:
depends_on:
migrate:
condition: service_completed_successfully
image: openfga/openfga:latest
restart: always
container_name: openfga
environment:
- OPENFGA_DATASTORE_ENGINE=mysql
- OPENFGA_DATASTORE_URI=root:secret@tcp(mysql:3306)/openfga?parseTime=true
- OPENFGA_LOG_FORMAT=json
volumes:
- /opt/openFGA/conf:/etc/openfga
command: run
networks:
- openfga
ports:
# 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 core
type user
type role
relations
define member: [user]
type application
relations
define viewer: [user,role#member]
define can_view: viewer
type menu
relations
define 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
model
schema 1.2
type application # module: core, file: core.fga
relations
define can_view: viewer
define viewer: [user, role#member]
type role # module: core, file: core.fga
relations
define 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 -file
C:\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 exist
C:\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 format
C:\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 found
C:\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 1
C:\Users\RJ\Documents\fgatestmodel>fga query list-relations role:admin --relation member
Error: accepts 2 arg(s), received 1
C:\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: --object
C:\Users\RJ\Documents\fgatestmodel>fga query list-uers --object role:admin --relation member --userfilter user
Error: unknown flag: --object
C:\Users\RJ\Documents\fgatestmodel>fga query list-uers --object role:admin --relation member --user-filter user
Error: unknown flag: --object
C:\Users\RJ\Documents\fgatestmodel>fga query list-uers --object role:admin --relation member --user-filter user
Error: unknown flag: --object
C:\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 found
C:\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 found
C:\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 found
C:\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 Check
expand Expand
list-objects List Objects
list-relations List Relations
list-users List users
Flags:
--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 ID
Global 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
relations
define member: [user]
#应用类型
#关系
# can_access:能否访问: 角色成员用户集
type application
relations
define can_access: [role#member]
#页面模块分组
#关系
#parent_model父模型
#可见身份: 用户成员组绑定
#访问身份: 用户成员组绑定 且 必须父模型要有访问权限
#能否查看 查看者 与 访问者 都可以访问
#能否访问 只有访问者可以访问
type page_module_group
relations
define parent_model: [application]
define viewer: [role#member]
define accesser: [role#member] and can_access from parent_model
define can_viewer: viewer or accesser
define can_access: accesser
#页面模块
#关系
#parent_model父模型
#可见身份: 用户成员组绑定
#访问身份: 用户成员组绑定 且 必须父模型要有访问权限
#能否查看 查看者 与 访问者 都可以访问
#能否访问 只有访问者可以访问
type page_module
relations
define parent_model: [page_module_group]
define viewer: [role#member]
define accesser: [role#member] and can_access from parent_model
define can_viewer: viewer or accesser
define can_access: accesser
#页面菜单
#关系
#parent_model父模型
#可见身份: 用户成员组绑定
#访问身份: 用户成员组绑定 且 必须父模型要有访问权限
#能否查看 查看者 与 访问者 都可以访问
#能否访问 只有访问者可以访问
type page_menu
relations
define parent_model: [page_module]
define viewer: [role#member]
define accesser: [role#member] and can_access from parent_model
define can_viewer: viewer or accesser
define can_access: accesser
#页面界面
#关系
#parent_model父模型
#可见身份: 用户成员组绑定
#访问身份: 用户成员组绑定 且 必须父模型要有访问权限
#能否查看 查看者 与 访问者 都可以访问
#能否访问 只有访问者可以访问
type page_view
relations
define parent_model: [page_menu]
define viewer: [role#member]
define accesser: [role#member] and can_access from parent_model
define can_viewer: viewer or accesser
define 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 {
@Bean
public 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:8080
store-id: 01JJN8S18TRYPMTXHG0JE50402
authorize-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 {
@Autowired
private TreePageEntityRepository treePageEntityRepository;
@Autowired
private 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();
}
@Autowired
private 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 {
@Autowired
private TreePageEntityRepository treePageEntityRepository;
@Autowired
private 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();
}
@Autowired
private 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();
}
}