当前位置: 首页 > article >正文

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

相关文章:

Openfga 授权模型搭建

1.根据项目去启动 配置一个 openfga 服务器 先创建一个 config.yaml文件 cd /opt/openFGA/conf touch ./config.yaml 怎么配置&#xff1f; 根据官网来看 openfga/.config-schema.json at main openfga/openfga GitHub 这里讲述详细的每一个配置每一个类型 这些配置有…...

C++模板编程——可变参函数模板之折叠表达式

目录 1. 什么是折叠表达式 2. 一元左折 3. 一元右折 4. 二元左折 5. 二元右折 6. 后记 上一节主要讲解了可变参函数模板和参数包展开&#xff0c;这一节主要讲一下折叠表达式。 1. 什么是折叠表达式 折叠表达式是C17中引入的概念&#xff0c;引入折叠表达式的目的是为了…...

ArkTS渲染控制

文章目录 if/else:条件渲染ArkUI通过自定义组件的build()函数和@Builder装饰器中的声明式UI描述语句构建相应的UI。在声明式描述语句中开发者除了使用系统组件外,还可以使用渲染控制语句来辅助UI的构建,这些渲染控制语句包括控制组件是否显示的条件渲染语句,基于数组数据快…...

在Scene里面绘制编辑工具

功能要求 策划要在scene模式下编辑棋子摆放。用handle.GUI绘制来解决了。 问题 在scene模式下编辑产生的数据&#xff0c;进入游戏模式后就全不见了。改为executeAlways也没用。我的解决办法是把编辑数据序列化保存到本地。在OnEnable的时候再读取。但是我忽然想到&#xff…...

UbuntuWindows双系统安装

做系统盘&#xff1a; Ubuntu20.04双系统安装详解&#xff08;内容详细&#xff0c;一文通关&#xff01;&#xff09;_ubuntu 20.04-CSDN博客 ubuntu系统调整大小&#xff1a; 调整指南&#xff1a; 虚拟机中的Ubuntu扩容及重新分区方法_ubuntu重新分配磁盘空间-CSDN博客 …...

[Linux]如何將腳本(shell script)轉換到系統管理服務器(systemd service)來運行?

[InfluxDB]Monitor Tem. and Volt of RaspberryPi and Send Message by Line Notify 在Linux中&#xff0c;shell腳本(shell script)常用於運行各種自動化的流程&#xff0c;包含API串接&#xff0c;設置和啟動應用服務等等&#xff0c;腳本語法也相對易學易讀&#xff0c;因此…...

【leetcode详解】T598 区间加法

598. 区间加法 II - 力扣&#xff08;LeetCode&#xff09; 思路分析 核心在于将问题转化&#xff0c; 题目不是要求最大整数本身&#xff0c;而是要求解最大整数的个数 结合矩阵元素的增加原理&#xff0c;我们将抽象问题转为可操作的方法&#xff0c;其实就是再找每组ops中…...

分层多维度应急管理系统的设计

一、系统总体架构设计 1. 六层体系架构 #mermaid-svg-QOXtM1MnbrwUopPb {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-QOXtM1MnbrwUopPb .error-icon{fill:#552222;}#mermaid-svg-QOXtM1MnbrwUopPb .error-text{f…...

稀疏进化训练:机器学习优化算法中的高效解决方案

稀疏进化训练&#xff1a;机器学习优化算法中的高效解决方案 稀疏进化训练&#xff1a;机器学习优化算法中的高效解决方案引言第一部分&#xff1a;背景与动机1.1 传统优化算法的局限性1.2 进化策略的优势1.3 稀疏性的重要性 第二部分&#xff1a;稀疏进化训练的核心思想2.1 稀…...

实战:如何利用网站日志诊断并解决收录问题?

本文转自&#xff1a;百万收录网 原文链接&#xff1a;https://www.baiwanshoulu.com/50.html 利用网站日志诊断并解决收录问题是一种非常有效的方法。以下是一个实战指南&#xff0c;帮助你如何利用网站日志来诊断并解决网站的收录问题&#xff1a; 一、获取并分析网站日志 …...

群晖搭建Gitea教程(使用系统自带的postgresql)

基于群晖7.2.2&#xff0c;使用套件中心的gitea&#xff0c;和系统自带的postgresql postgresql: 切换到postgres用户 sudo -I -u postgres 在想要保存数据库的磁盘路径下创建PostgreSql文件夹 初始化数据库文件夹配置 initdb -D ./PostgreSql 备份./PostgreSql路径下的post…...

备考蓝桥杯嵌入式2:使用LCD完成显示

LCD LCD&#xff08;液晶显示器&#xff0c;Liquid Crystal Display&#xff09;是一种常见的平面显示技术&#xff0c;广泛应用于电视、电脑显示器、手机屏幕等设备。蓝桥杯中&#xff0c;也有涉及到使用LCD来完成字符串显示的要求和操作。 考场上会给予LCD的驱动包&#xf…...

网络爬虫学习:应用selenium获取Edge浏览器版本号,自动下载对应版本msedgedriver,确保Edge浏览器顺利打开。

一、前言 我从24年11月份开始学习网络爬虫应用开发&#xff0c;经过2个来月的努力&#xff0c;于1月下旬完成了开发一款网络爬虫软件的学习目标。这里对本次学习及应用开发进行一下回顾总结。 前几天我已经发了一篇日志&#xff08;网络爬虫学习&#xff1a;应用selenium从搜…...

Elasticsearch的索引生命周期管理

目录 说明零、参考一、ILM的基本概念二、ILM的实践步骤Elasticsearch ILM策略中的“最小年龄”是如何计算的&#xff1f;如何监控和调整Elasticsearch ILM策略的性能&#xff1f; 1. **监控性能**使用/_cat/thread_pool API基本请求格式请求特定线程池的信息响应内容 2. **调整…...

Observability:实现 OpenTelemetry 原生可观察性的商业价值

作者&#xff1a;来自 Elastic David Hope 利用开放标准和简化的数据收集转变组织的可观察性策略。 现代组织面临着前所未有的可观察性挑战。随着系统变得越来越复杂和分散&#xff0c;传统的监控方法难以跟上步伐。由于数据量每两年翻一番&#xff0c;系统跨越多个云和技术&am…...

C语言中的线程本地变量

这处线程本地变量可不是简单的函数中的本地变量。线程除了可以共享存在于进程内的全局变量外&#xff0c;还可以有属于自己的线程本地变量。线程本地变量的值只能够在某个具体线程的生存期内可用。变量的实际存储空间会在线程开始时分配&#xff0c;线程结束时回收。线程不会对…...

Zabbix 推送告警 消息模板 美化(钉钉Webhook机器人、邮件)

目前网络上已经有很多关于Zabbix如何推送告警信息到钉钉机器人、到邮件等文章。 但是在搜索下来&#xff0c;发现缺少了对告警信息的美化的文章。 本文不赘述如何对Zabbix对接钉钉、对接邮件&#xff0c;仅介绍我采用的美化消息模板的内容。 活用AI工具可以减轻很多学习、脑力负…...

罗格斯大学:通过输入嵌入对齐选择agent

&#x1f4d6;标题&#xff1a;AgentRec: Agent Recommendation Using Sentence Embeddings Aligned to Human Feedback &#x1f310;来源&#xff1a;arXiv, 2501.13333 &#x1f31f;摘要 &#x1f538;多代理系统必须决定哪个代理最适合给定的任务。我们提出了一种新的架…...

机器学习7-全连接神经网络3-过拟合与超参数

机器学习6-全连接神经网络3-过拟合欠拟合 过拟合应对过拟合-最优方案&#xff1a;获取更多的训练数据应对过拟合-次优方案&#xff1a;正则化应对过拟合-次优方案2&#xff1a;随机失活综合考量 超参数超参数优化方法 过拟合 机器学习的根本问题是优化和泛化的问题。优化——是…...

【PyTorch】7.自动微分模块:开启神经网络 “进化之门” 的魔法钥匙

目录 1. 梯度基本计算 2. 控制梯度计算 3. 梯度计算注意 4. 小节 个人主页&#xff1a;Icomi 专栏地址&#xff1a;PyTorch入门 在深度学习蓬勃发展的当下&#xff0c;PyTorch 是不可或缺的工具。它作为强大的深度学习框架&#xff0c;为构建和训练神经网络提供了高效且灵活…...

11 3D变换模块(transform3d.rs)

transform3d.rs代码定义了一个名为 Transform3D 的 Rust 结构体&#xff0c;它用于表示一个3D变换矩阵。这个结构体是泛型的&#xff0c;包含三个类型参数&#xff1a;T、Src 和 Dst。其中&#xff0c;T 用于矩阵元素的数据类型&#xff0c;Src 和 Dst 用于表示变换的源和目标类…...

MATLAB基础应用精讲-【数模应用】梯度直方图(HOG)(附C++和python代码实现)(二)

目录 前言 几个高频面试题目 HOG与SIFT区别 边缘特征与梯度方向直方图的关系 算法原理 什么是HOG 图像中像素点的梯度计算 为每个cell构造梯度方向直方图HOG 数学模型 方向梯度直方图计算步骤 第一步:预处理 第二步:计算梯度图像 第三步:在8*8的网格中计算梯度…...

pytorch生成对抗网络

人工智能例子汇总&#xff1a;AI常见的算法和例子-CSDN博客 生成对抗网络&#xff08;GAN&#xff0c;Generative Adversarial Network&#xff09;是一种深度学习模型&#xff0c;由两个神经网络组成&#xff1a;生成器&#xff08;Generator&#xff09;和判别器&#xff0…...

Baklib在企业知识管理领域的领先地位与三款竞品的深度剖析

内容概要 在现代企业中&#xff0c;知识管理已成为提高工作效率和推动创新的重要手段。Baklib作为一款领先的知识中台&#xff0c;以其集成化和智能化的特性&#xff0c;帮助企业在这一领域取得了显著成就。该平台具备强大的知识收集、整理、存储和共享功能&#xff0c;通过构…...

2 MapReduce

2 MapReduce 1. MapReduce 介绍1.1 MapReduce 设计构思 2. MapReduce 编程规范3. Mapper以及Reducer抽象类介绍1.Mapper抽象类的基本介绍2.Reducer抽象类基本介绍 4. WordCount示例编写5. MapReduce程序运行模式6. MapReduce的运行机制详解6.1 MapTask 工作机制6.2 ReduceTask …...

人工智能学习(四)之机器学习基本概念

机器学习基本概念详细解析&#xff1a;从生活实例轻松入门 在当今数字化时代&#xff0c;机器学习作为人工智能领域的核心技术之一&#xff0c;正深刻地改变着我们的生活和工作方式。从智能语音助手到图像识别系统&#xff0c;从个性化推荐引擎到自动驾驶汽车&#xff0c;机器…...

大模型openai范式接口调用方法

本文将介绍如下内容&#xff1a; 一、为什么选择 OpenAI 范式接口&#xff1f;二、调用 Openai 接口官方调用 Demo 示例三、自定义调用 Openai 接口 一、为什么选择 OpenAI 范式接口&#xff1f; OpenAI 范式接口因其简洁、统一和高效的设计&#xff0c;成为了与大型语言模型…...

DeepSeek API接口中的openAI是什么意思?

老六哥的小提示&#xff1a;我们可能不会被AI轻易淘汰&#xff0c;但是会被“会使用AI的人”淘汰。 DeepSeek是一款基于先进推理技术的大型语言模型&#xff0c;能够根据用户提供的简洁提示词生成高质 曾经有外媒评价说&#xff1a;DeepSeek盗用了openAI的技术&#xff0c;或者…...

重构字符串(767)

767. 重构字符串 - 力扣&#xff08;LeetCode&#xff09; 解法&#xff1a; class Solution { public:string reorganizeString(string s){string res;//因为1 < s.length < 500 &#xff0c; uint64_t 类型足够uint16_t n s.size();if (n 0) {return res;}unordere…...

测压表压力表计量表针头针尾检测数据集VOC+YOLO格式4862张4类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4862 标注数量(xml文件个数)&#xff1a;4862 标注数量(txt文件个数)&#xff1a;4862 …...