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.mzph.cn/diannao/68664.shtml

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

相关文章

赛博算卦之周易六十四卦JAVA实现:六幺算尽天下事,梅花化解天下苦。

佬们过年好呀~新年第一篇博客让我们来场赛博算命吧&#xff01; 更多文章&#xff1a;个人主页 系列文章&#xff1a;JAVA专栏 欢迎各位大佬来访哦~互三必回&#xff01;&#xff01;&#xff01; 文章目录 #一、文化背景概述1.文化起源2.起卦步骤 #二、卦象解读#三、just do i…

力扣116. 填充每个节点的下一个右侧节点指针

Problem: 116. 填充每个节点的下一个右侧节点指针 文章目录 题目描述思路复杂度Code 题目描述 思路 遍历思想(利用二叉树的先序遍历) 本题目的难点在于对于不同父节点的邻接问题因此我们可以抽象将两两节点为一组&#xff08;不同父节点的两个孩子节点也抽象为一组&#xff09…

python学opencv|读取图像(四十九)原理探究:使用cv2.bitwise()系列函数实现图像按位运算

【0】基础定义 按位与运算&#xff1a;两个等长度二进制数上下对齐&#xff0c;全1取1&#xff0c;其余取0。 按位或运算&#xff1a;两个等长度二进制数上下对齐&#xff0c;有1取1&#xff0c;其余取0。 按位异或运算&#xff1a; 两个等长度二进制数上下对齐&#xff0c;相…

【架构面试】一、架构设计认知

涉及分布式锁、中间件、数据库、分布式缓存、系统高可用等多个技术领域&#xff0c;旨在考查候选人的技术深度、架构设计能力与解决实际问题的能力。 1. 以 Redis 是否可以作为分布式锁为例&#xff1a; 用 Redis 实现分布式锁会存在哪些问题&#xff1f; 死锁&#xff1a;如果…

MySQL基本架构SQL语句在数据库框架中的执行流程数据库的三范式

MySQL基本架构图&#xff1a; MySQL主要分为Server层和存储引擎层 Server层&#xff1a; 连接器&#xff1a;连接客户端&#xff0c;获取权限&#xff0c;管理连接 查询缓存&#xff08;可选&#xff09;&#xff1a;在执行查询语句之前会先到查询缓存中查看是否执行过这条语…

跟李沐学AI:视频生成类论文精读(Movie Gen、HunyuanVideo)

Movie Gen&#xff1a;A Cast of Media Foundation Models 简介 Movie Gen是Meta公司提出的一系列内容生成模型&#xff0c;包含了 3.2.1 预训练数据 Movie Gen采用大约 100M 的视频-文本对和 1B 的图片-文本对进行预训练。 图片-文本对的预训练流程与Meta提出的 Emu: Enh…

Baklib深入解析企业内容管理与内容中台的本质差异

内容概要 在当前信息化快速发展的时代&#xff0c;企业迫切需要高效的内容管理解决方案。企业内容管理&#xff08;ECM&#xff09;和内容中台是满足这一需求的两种不同系统。企业内容管理的主要作用是对企业内部的各种文档、数据进行整理和存储&#xff0c;确保信息资源的整合…

Ansible介绍与入门学习

一、Ansible的介绍&#xff08;Ansible is a radically simple IT automation engine 极简的IT自动化工具&#xff09; 1.Ansible是一款 运维的自动化工具&#xff0c;常用于软件部署自动化、配置自动化、管理自动化等&#xff0c;其内置大量的功能模块来满足日常的工作需要…

1.27补题 回训练营

E 智乃的小球 题目描述 在一条无限长的水平直线上&#xff0c;有 n 个小球&#xff0c;每个小球的质量相同&#xff0c;体积可以忽略不计。这些小球初始时位于直线上的不同位置&#xff0c;并且每个小球有一个初始速度&#xff0c;速度为 -1 m/s 或 1 m/s。速度为 -1 m/s 表示…

[JavaWeb]搜索表单区域

一.注意事项 设置外边距:margin:(参数可省去部分)上 下 左 右 二.源代码 <!DOCTYPE html> <html lang"zh-CN"> <head> <meta charset"UTF-8"> <title>Tlias智能学习辅助系统</title> <style> /* 导航栏样…

前端进阶:深度剖析预解析机制

一、预解析是什么&#xff1f; 在前端开发中&#xff0c;我们常常会遇到一些看似不符合常规逻辑的代码执行现象&#xff0c;比如为什么在变量声明之前访问它&#xff0c;得到的结果是undefined&#xff0c;而不是报错&#xff1f;为什么函数在声明之前就可以被调用&#xff1f…

基于聚类与相关性分析对马来西亚房价数据进行分析

碎碎念&#xff1a;由于最近太忙了&#xff0c;更新的比较慢&#xff0c;提前祝大家新春快乐&#xff0c;万事如意&#xff01;本数据集的下载地址&#xff0c;读者可以自行下载。 1.项目背景 本项目旨在对马来西亚房地产市场进行初步的数据分析&#xff0c;探索各州的房产市…

范冰冰担任第75届柏林电影节主竞赛单元评委 共鉴电影佳作

近日&#xff0c;备受瞩目的柏林电影节迎来了新一届盛事&#xff0c;而华人演员范冰冰将以主竞赛单元评委身份亮相&#xff0c;引发了广泛关注。此前她已担任过戛纳国际电影节、东京国际电影节、圣塞巴斯蒂安国际电影节等众多电影节主竞赛单元评委。作为国际影坛的知名人物&…

Ollama 运行从 ModelScope 下载的 GGUF 格式的模型

本文系统环境 Windows 10 Ollama 0.5.7 Ollama 是什么&#xff1f; Ollama 可以让你快速集成和部署本地 AI 模型。它支持各种不同的 AI 模型&#xff0c;并允许用户通过简单的 API 进行调用 Ollama 的安装 Ollama 官网 有其下载及安装方法&#xff0c;非常简便 但如果希…

“腾讯、钉钉、飞书” 会议开源平替,免费功能强大

在数字化时代&#xff0c;远程办公和线上协作越来越火。然而&#xff0c;市面上的视频会议工具要么贵得离谱&#xff0c;要么功能受限&#xff0c;甚至还有些在数据安全和隐私保护上让人不放心。 今天开源君给大家安利一个超棒的开源项目 - Jitsi Meet&#xff0c;这可是我在网…

【教学类-89-01】20250127新年篇01—— 蛇年红包(WORD模版)

祈愿在2025蛇年里&#xff0c; 伟大的祖国风调雨顺、国泰民安、每个人齐心协力&#xff0c;共同经历这百年未有之大变局时代&#xff08;国际政治、AI技术……&#xff09; 祝福亲友同事孩子们平安健康&#xff08;安全、安全、安全&#xff09;、巳巳如意&#xff01; 背景需…

2025年1月30日(任意截面、自定义截面梁的设置)

Ansys 在ANSYS中&#xff0c;以下是这些术语的详细解释&#xff1a; Nodal Solution (节点解): Nodal Solution指的是在有限元分析中计算出的节点处的物理量解。通常包括节点的位移、反应力等信息。节点解是分析结果的基础&#xff0c;因为它们可以用来计算其他重要的物理量&a…

unity使用内置videoplayer打包到安卓手机进行视频播放

1.新建UI&#xff0c;新建RawImage在画布当作视频播放的显示载体 2.新建VideoPlayer 3.新建Render Texture作为连接播放器视频显示和幕布的渲染纹理 将Render Texture同时挂载在VideoPlayer播放器和RawImage上。这样就可以将显示的视频内容在RawImage上显示出来了。 问题在于&a…

【Envi遥感图像处理】008:波段(批量)分离与波段合成

文章目录 一、波段分离提取1. 提取单个波段2. 批量提取单个波段二、波段合成相关阅读:【ArcGIS微课1000例】0058:波段合成(CompositeBands)工具的使用 一、波段分离提取 1. 提取单个波段

MongoDB平替数据库对比

背景 项目一直是与实时在线监测相关&#xff0c;特点数据量大&#xff0c;读写操作大&#xff0c;所以选用的是MongoDB。但按趋势来讲&#xff0c;需要有一款国产数据库可替代&#xff0c;实现信创要求。选型对比如下 1. IoTDB 这款是由清华大学主导的开源时序数据库&#x…