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: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();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/9072.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【物联网】ARM核常用指令(详解):数据传送、计算、位运算、比较、跳转、内存访问、CPSR/SPSR、流水线及伪指令

文章目录 指令格式&#xff08;重点&#xff09;1. 立即数2. 寄存器位移 一、数据传送指令1. MOV指令2. MVN指令3. LDR指令 二、数据计算指令1. ADD指令1. SUB指令1. MUL指令 三、位运算指令1. AND指令2. ORR指令3. EOR指令4. BIC指令 四、比较指令五、跳转指令1. B/BL指令2. l…

星火大模型接入及文本生成HTTP流式、非流式接口(JAVA)

文章目录 一、接入星火大模型二、基于JAVA实现HTTP非流式接口1.配置2.接口实现&#xff08;1&#xff09;分析接口请求&#xff08;2&#xff09;代码实现 3.功能测试&#xff08;1&#xff09;测试对话功能&#xff08;2&#xff09;测试记住上下文功能 三、基于JAVA实现HTTP流…

lightweight-charts-python 包 更新 lightweight-charts.js 的方法

lightweight-charts-python 是 lightweight-charts.js 的 python 包装&#xff0c;非常好用 lightweight-charts 更新比较频繁&#xff0c;导致 lightweight-charts-python 内置的 lightweight-charts 经常不是最新的。 新的 lightweight-charts 通常可以获得性能改进和bug修复…

记录 | Docker的windows版安装

目录 前言一、1.1 打开“启用或关闭Windows功能”1.2 安装“WSL”方式1&#xff1a;命令行下载方式2&#xff1a;离线包下载 二、Docker Desktop更新时间 前言 参考文章&#xff1a;Windows Subsystem for Linux——解决WSL更新速度慢的方案 参考视频&#xff1a;一个视频解决D…

2025年01月27日Github流行趋势

项目名称&#xff1a;onlook项目地址url&#xff1a;https://github.com/onlook-dev/onlook项目语言&#xff1a;TypeScript历史star数&#xff1a;5340今日star数&#xff1a;211项目维护者&#xff1a;Kitenite, drfarrell, iNerdStack, abhiroopc84, apps/dependabot项目简介…

【Linux探索学习】第二十七弹——信号(一):Linux 信号基础详解

Linux学习笔记&#xff1a; https://blog.csdn.net/2301_80220607/category_12805278.html?spm1001.2014.3001.5482 前言&#xff1a; 前面我们已经将进程通信部分讲完了&#xff0c;现在我们来讲一个进程部分也非常重要的知识点——信号&#xff0c;信号也是进程间通信的一…

海外问卷调查渠道查如何设置:最佳实践+示例

随着经济全球化和一体化进程的加速&#xff0c;企业间的竞争日益加剧&#xff0c;为了获得更大的市场份额&#xff0c;对企业和品牌而言&#xff0c;了解受众群体的的需求、偏好和痛点才是走向成功的关键。而海外问卷调查才是获得受众群体痛点的关键&#xff0c;制作海外问卷调…

《STL基础之vector、list、deque》

【vector、list、deque导读】vector、list、deque这三种序列式的容器&#xff0c;算是比较的基础容器&#xff0c;也是大家在日常开发中常用到的容器&#xff0c;因为底层用到的数据结构比较简单&#xff0c;笔者就将他们三者放到一起做下对比分析&#xff0c;介绍下基本用法&a…

一组开源、免费、Metro风格的 WPF UI 控件库

前言 今天大姚给大家分享一个开源、免费、Metro风格的 WPF UI 控件库&#xff1a;MahApps.Metro。 项目介绍 MahApps.Metro 是一个开源、免费、Metro风格的 WPF UI 控件库&#xff0c;提供了现代化、平滑和美观的控件和样式&#xff0c;帮助开发人员轻松创建具有现代感的 Win…

网易云音乐歌名可视化:词云生成与GitHub-Pages部署实践

引言 本文将基于前一篇爬取的网易云音乐数据, 利用Python的wordcloud、matplotlib等库, 对歌名数据进行深入的词云可视化分析. 我们将探索不同random_state对词云布局的影响, 并详细介绍如何将生成的词云图部署到GitHub Pages, 实现数据可视化的在线展示. 介绍了如何从原始数据…

通义灵码插件保姆级教学-IDEA(安装及使用)

一、JetBrains IDEA 中安装指南 官方下载指南&#xff1a;通义灵码安装教程-阿里云 步骤 1&#xff1a;准备工作 操作系统&#xff1a;Windows 7 及以上、macOS、Linux&#xff1b; 下载并安装兼容的 JetBrains IDEs 2020.3 及以上版本&#xff0c;通义灵码与以下 IDE 兼容&…

工业级 RAG 实现 - QAnything

文章目录 1. QAnything简介2. QAnything 安装教程2. 1 安装软件包2.2 运行QAnything框架2.3 访问前端页面 3. QAnything 简单使用3.1 创建知识库3.2 创建聊天机器人3.3 关联知识库3.4 测试 4. QAnything 的分析&#xff1a;4. 1 QAnything 架构4. 2 两阶段检索4. 2.1 一阶段检索…

Cross-Resolution知识蒸馏论文学习

TPAMI 2024&#xff1a;Pixel Distillation: Cost-Flexible Distillation Across Image Sizes and Heterogeneous Networks 教师模型使用高分辨率输入进行学习&#xff0c;学生模型使用低分辨率输入进行学习 学生蒸馏损失&#xff1a;Lpkd和Lisrd Lpkd&#xff1a;任务损失lo…

Versal - 基础3(AXI NoC 专题+仿真+QoS)

目录 1. 简介 2. 示例 2.1 示例说明 2.2 创建项目 2.2.1 平台信息 2.2.2 AXI NoC Automation 2.2.3 创建时钟和复位 2.3 配置 NoC 2.4 配置 AXI Traffic 2.5 配置 Memory Size 2.6 Validate BD 2.7 添加观察信号 2.8 运行仿真 2.9 查看结果 2.9.1 整体波形 2.9…

【PostgreSQL内核学习 —— (WindowAgg(一))】

WindowAgg 窗口函数介绍WindowAgg理论层面源码层面WindowObjectData 结构体WindowStatePerFuncData 结构体WindowStatePerAggData 结构体eval_windowaggregates 函数update_frameheadpos 函数 声明&#xff1a;本文的部分内容参考了他人的文章。在编写过程中&#xff0c;我们尊…

RubyFPV开源代码之系统简介

RubyFPV开源代码之系统简介 1. 源由2. 工程架构3. 特性介绍&#xff08;软件&#xff09;3.1 特性亮点3.2 数字优势3.3 使用功能 4. DEMO推荐&#xff08;硬件&#xff09;4.1 天空端4.2 地面端4.3 按键硬件Raspberry PiRadxa 3W/E/C 5. 软件设计6. 参考资料 1. 源由 RubyFPV以…

MySQL(单表访问)

今天是新年&#xff0c;祝大家新年快乐&#xff0c;但是生活还是得继续。 后面也会持续更新&#xff0c;学到新东西会在其中补充。 建议按顺序食用&#xff0c;欢迎批评或者交流&#xff01; 缺什么东西欢迎评论&#xff01;我都会及时修改的&#xff01; 大部分截图和文章采…

汇编的使用总结

一、汇编的组成 1、汇编指令&#xff08;指令集&#xff09; 数据处理指令: 数据搬移指令 数据移位指令 位运算指令 算术运算指令 比较指令 跳转指令 内存读写指令 状态寄存器传送指令 异常产生指令等 2、伪指令 不是汇编指令&#xff0c;但是可以起到指令的作用&#xff0c;伪…

汇编基础语法及其示例

1.汇编指令 1.1汇编指令的基本格式 <opcode>{<cond>}{s} <Rd> , <Rn> , <shifter_operand> <功能码>{<条件码>}{cpsr影响位} <目标寄存器> , <第一操作寄存器> , <第二操作数> 注&#xff1a;第一操作寄存器…

FLTK - FLTK1.4.1 - 搭建模板,将FLTK自带的实现搬过来做实验

文章目录 FLTK - FLTK1.4.1 - 搭建模板&#xff0c;将FLTK自带的实现搬过来做实验概述笔记my_fltk_test.cppfltk_test.hfltk_test.cxx用adjuster工程试了一下&#xff0c;好使。END FLTK - FLTK1.4.1 - 搭建模板&#xff0c;将FLTK自带的实现搬过来做实验 概述 用fluid搭建UI…