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

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 这里讲述详细的每一个配置每一个类型 这些配置有…...

【Proteus】NE555纯硬件实现LED呼吸灯效果,附源文件,效果展示

本文通过NE555定时器芯片和简单的电容充放电电路,设计了一种纯硬件实现的呼吸灯方案,并借助Proteus仿真软件验证其功能。方案无需编程,成本低且易于实现,适合电子爱好者学习PWM(脉宽调制)和定时器电路原理。 一、呼吸灯原理与NE555功能分析 1. 呼吸灯核心原理 呼吸灯的…...

DRM系列三:drm core模块入口

本系列文章基于linux 5.15 一、drm_core_init 执行一些drm core的初始化工作 static int __init drm_core_init(void) {int ret;drm_connector_ida_init();idr_init(&drm_minors_idr);drm_memcpy_init_early();ret drm_sysfs_init();if (ret < 0) {DRM_ERROR("…...

Clock Controller of RH850/F1KH-D8, RH850/F1KM-S4, RH850/F1KM-S2

&esmp; 时钟控制器由时钟振荡电路、时钟选择电路、和时钟输出电路组成。   RH850/F1KH、RH850/F1KM单片机的时钟控制器具有以下特点: 六个片上时钟振荡器: 主振荡器(MainOSC),振荡频率分别为8、16、20和24 MHz子振荡器(SubOSC),振荡频率为32.768 kHz*1 100针的产品…...

kamailio-auth模块详解【以下内容来源于官网,本文只做翻译】

以下是《Auth 模块》文档的中文翻译&#xff1a; Auth 模块 作者&#xff1a;Jan Janak FhG Fokus janiptel.org Juha Heinanen TutPro Inc jhsong.fi Daniel-Constantin Mierla asipto.com micondagmail.com 版权所有 © 2002, 2003 FhG FOKUS 官网链接: https://kamaili…...

从TypeScript到ArkTS的适配指导

文章目录 一、ArkTS语法适配背景程序稳定性程序性能.ets代码兼容性支持与TS/JS的交互方舟运行时兼容TS/JS二、从TypeScript到ArkTS的适配规则概述强制使用静态类型禁止在运行时变更对象布局限制运算符的语义不支持 structural typing约束说明限制使用标准库一、ArkTS语法适配背…...

Git 版本控制:基础介绍与常用操作

目录 Git 的基本概念 Git 安装与配置 Git 常用命令与操作 1. 初始化本地仓库 2. 版本控制工作流程 3. 分支管理 4. 解决冲突 5. 回退和撤销 6. 查看提交日志 前言 在软件开发过程中&#xff0c;开发者常常需要在现有程序的基础上进行修改和扩展。但如果不加以管理&am…...

【Python】理解Python中的协程和生成器:从yield到async

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在现代编程中,异步编程成为提升程序性能和响应速度的重要手段。Python作为一门功能强大的编程语言,提供了丰富的工具来实现异步操作,其中…...

Unity开发游戏使用XLua的基础

Unity使用Xlua的常用编码方式&#xff0c;做一下记录 1、C#调用lua 1、Lua解析器 private LuaEnv env new LuaEnv();//保持它的唯一性void Start(){env.DoString("print(你好lua)");//env.DoString("require(Main)"); 默认在resources文件夹下面//帮助…...

什么是区块链

区块链是一种去中心化的分布式账本技术&#xff0c;它通过一系列复杂而精密的设计原则和机制来确保数据的安全性、透明性和不可篡改性。在最基础的层面上&#xff0c;区块链是由一系列按照时间顺序链接起来的数据块组成的链式结构。每个数据块中包含了一定数量的交易记录或状态…...

C++中的析构器(Destructor)(也称为析构函数)

在C中&#xff0c;析构器&#xff08;Destructor&#xff09;也称为析构函数&#xff0c;它是一种特殊的成员函数&#xff0c;用于在对象销毁时进行资源清理工作。以下是关于C析构器的详细介绍&#xff1a; 析构函数的特点 名称与类名相同&#xff0c;但前面有一个波浪号 ~&a…...

【ts + java】古玩系统开发总结

src别名的配置 开发中文件和文件的关系会比较复杂&#xff0c;我们需要给src文件夹一个别名吧 vite.config.js import { defineConfig } from vite import vue from vitejs/plugin-vue import path from path// https://vitejs.dev/config/ export default defineConfig({pl…...

NLP深度学习 DAY5:Sequence-to-sequence 模型详解

Seq2Seq&#xff08;Sequence-to-Sequence&#xff09;模型是一种用于处理输入和输出均为序列任务的深度学习模型。它最初被设计用于机器翻译&#xff0c;但后来广泛应用于其他任务&#xff0c;如文本摘要、对话系统、语音识别、问答系统等。 核心思想 Seq2Seq 模型的目标是将…...

音视频多媒体编解码器基础-codec

如果要从事编解码多媒体的工作&#xff0c;需要准备哪些更为基础的内容&#xff0c;这里帮你总结完。 因为数据类型不同所以编解码算法不同&#xff0c;分为图像、视频和音频三大类&#xff1b;因为流程不同&#xff0c;可以分为编码和解码两部分&#xff1b;因为编码器实现不…...

数据分析系列--⑦RapidMiner模型评价(基于泰坦尼克号案例含数据集)

一、前提 二、模型评估 1.改造⑥ 2.Cross Validation算子说明 2.1Cross Validation 的作用 2.1.1 模型评估 2.1.2 减少过拟合 2.1.3 数据利用 2.2 Cross Validation 的工作原理 2.2.1 数据分割 2.2.2 迭代训练与测试 ​​​​​​​ 2.2.3 结果汇总 ​​​​​​​ …...

【react+redux】 react使用redux相关内容

首先说一下&#xff0c;文章中所提及的内容都是我自己的个人理解&#xff0c;是我理逻辑的时候&#xff0c;自我说服的方式&#xff0c;如果有问题有补充欢迎在评论区指出。 一、场景描述 为什么在react里面要使用redux&#xff0c;我的理解是因为想要使组件之间的通信更便捷…...

nacos 配置管理、 配置热更新、 动态路由

文章目录 配置管理引入jar包添加 bootstrap.yaml 文件配置在application.yaml 中添加自定义信息nacos 配置信息 配置热更新采用第一种配置根据服务名确定配置文件根据后缀确定配置文件 动态路由DynamicRouteLoaderNacosConfigManagerRouteDefinitionWriter 路由配置 配置管理 …...

前端知识速记:节流与防抖

前端知识速记&#xff1a;节流与防抖 什么是防抖&#xff1f; 防抖是一种控制事件触发频率的方法&#xff0c;通常用于处理用户频繁触发事件的场景。防抖的核心思想是将多个连续触发事件合并为一个事件&#xff0c;以减少执行次数。它在以下场景中特别有效&#xff1a; 输入…...

2.攻防世界PHP2及知识点

进入题目页面如下 意思是你能访问这个网站吗&#xff1f; ctrlu、F12查看源码&#xff0c;什么都没有发现 用kali中的dirsearch扫描根目录 命令如下&#xff0c;根据题目提示以及需要查看源码&#xff0c;扫描以php、phps、html为后缀的文件 dirsearch -u http://61.147.17…...

【ubuntu】双系统ubuntu下一键切换到Windows

ubuntu下一键切换到Windows 1.4.1 重启脚本1.4.2 快捷方式1.4.3 移动快捷方式到系统目录 按前文所述文档&#xff0c;开机默认启动ubuntu。Windows切换到Ubuntu直接重启就行了&#xff0c;而Ubuntu切换到Windows稍微有点麻烦。可编辑切换重启到Windows的快捷方式。 1.4.1 重启…...

C#属性和字段(访问修饰符)

不同点逻辑性/灵活性存储性访问性使用范围安全性属性(Property)源于字段,对字段的扩展,逻辑字段并不占用实际的内存可以被其他类访问对接收的数据范围做限定,外部使用增加了数据的安全性字段(Field)不经过逻辑处理占用内存的空间及位置大部分字段不能直接被访问内存使用不安全 …...

Androidstdio-真机调试

显示隐藏设备 手机通过数据线插入电脑 Androidstdio设置中下载USB驱动 选择下载的驱动 更新完成后&#xff0c;在编译器查看&#xff0c;此时真机已经显示出来了 调试app可以在日志中查看日志&#xff0c;详细日志查看方法看前面的帖子 如果有这种日志输出&#xff0c;运行到此…...

2025年美赛B题-结合Logistic阻滞增长模型和SIR传染病模型研究旅游可持续性-成品论文

模型设计思路与创新点&#xff1a; 建模的时候应该先确定我们需要建立什么类的模型&#xff1f;优化类还是统计类&#xff1f;这个题需要大量的数据分析&#xff0c;因此我们可以建立一个统计学模型。 统计学建模思路&#xff1a;观察规律&#xff0c;建立模型&#xff0c;参…...

数据结构【链栈】

基于 C 实现链表栈&#xff1a;原理、代码与应用 一、引言 栈就是一个容器&#xff0c;可以当场一个盒子&#xff0c;只能一个一个拿&#xff0c;一个一个放&#xff0c;而且是从上面放入。 有序顺序栈操作比较容易【会了链栈之后顺序栈自然明白】&#xff0c;所以我们这里只…...

MediaPipe与YOLO已训练模型实现可视化人脸和手势关键点检测

项目首页 - ZiTai_YOLOV11:基于前沿的 MediaPipe 技术与先进的 YOLOv11 预测试模型&#xff0c;精心打造一款强大的实时检测应用。该应用无缝连接摄像头&#xff0c;精准捕捉画面&#xff0c;能即时实现人脸检测、手势识别以及骨骼关键点检测&#xff0c;将检测结果实时、直观地…...

使用 SpringBoot+Thymeleaf 模板引擎进行 Web 开发

目录 一、什么是 Thymeleaf 模板引擎 二、Thymeleaf 模板引擎的 Maven 坐标 三、配置 Thymeleaf 四、访问页面 五、访问静态资源 六、Thymeleaf 使用示例 七、Thymeleaf 常用属性 前言 在现代 Web 开发中&#xff0c;模板引擎被广泛用于将动态内容渲染到静态页面中。Thy…...

pytorch深度Q网络

人工智能例子汇总&#xff1a;AI常见的算法和例子-CSDN博客 DQN 引入了深度神经网络来近似Q函数&#xff0c;解决了传统Q-learning在处理高维状态空间时的瓶颈&#xff0c;尤其是在像 Atari 游戏这样的复杂环境中。DQN的核心思想是使用神经网络 Q(s,a;θ)Q(s, a; \theta)Q(s,…...

list的使用,及部分功能的模拟实现(C++)

目录&#xff08;文章中"节点"和"结点"是同一个意思&#xff09; 1. list的介绍及使用 1.1 list的介绍 1.2 list的使用 1.2.1 list的构造 1.2.2 list iterator的使用 1.2.3 list capacity 1.2.4 list element access 1.2.5 list modifiers 1.2.6 list…...

makailio-alias_db模块详解

ALIAS_DB 模块 作者 Daniel-Constantin Mierla micondagmail.com Elena-Ramona Modroiu ramonaasipto.com 编辑 Daniel-Constantin Mierla micondagmail.com 版权 © 2005 Voice Sistem SRL © 2008 asipto.com 目录 管理员指南 概述依赖 2.1 Kamailio 模块 2.2 外…...

【AI】DeepSeek 概念/影响/使用/部署

在大年三十那天&#xff0c;不知道你是否留意到&#xff0c;“deepseek”这个词出现在了各大热搜榜单上。这引起了我的关注&#xff0c;出于学习的兴趣&#xff0c;我深入研究了一番&#xff0c;才有了这篇文章的诞生。 概念 那么&#xff0c;什么是DeepSeek&#xff1f;首先百…...

算法随笔_35: 每日温度

上一篇:算法随笔_34: 最后一个单词的长度-CSDN博客 题目描述如下: 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后。如果气温在这之后都不会升…...

人工智能入门课【手写自注意力机制】

原理 自注意力&#xff08;Self-Attention&#xff09;是一种强大的机制&#xff0c;广泛应用于自然语言处理、计算机视觉等领域&#xff0c;尤其是在Transformer架构中发挥了关键作用。它的核心思想是让模型能够动态地关注输入序列中不同位置之间的关系&#xff0c;从而更好地…...

记7(激活函数+多层神经网络+梯度下降法及其优化

目录 1、激活函数1.1、sigmoid函数&#xff1a;2端饱和&#xff0c;下面2个函数都要幂运算&#xff0c;运算速度会比较慢1.2、ReLU函数&#xff08;Rectified Linear Unit&#xff0c;修正线性单元&#xff09;1.3、PReLU函数&#xff08;Parameteric Rectified Linear Unit&am…...

Qt u盘自动升级软件

Qt u盘自动升级软件 Chapter1 Qt u盘自动升级软件u盘自动升级软件思路&#xff1a;step1. 获取U盘 判断U盘名字是否正确&#xff0c; 升级文件是否存在。step2. 升级step3. 升级界面 Chapter2 Qt 嵌入式设备应用程序&#xff0c;通过U盘升级的一种思路Chapter3 在开发板上运行的…...

关于低代码技术架构的思考

我们经常会看到很多低代码系统的技术架构图&#xff0c;而且经常看不懂。是因为技术架构图没有画好&#xff0c;还是因为技术不够先进&#xff0c;有时候往往都不是。 比如下图&#xff1a; 一个开发者&#xff0c;看到的视角往往都是技术层面&#xff0c;你给用户讲React18、M…...

如何使用 ChatBox AI 简化本地模型对话操作

部署模型请看上一篇帖子&#xff1a;本地部署DeepSeek教程&#xff08;Mac版本&#xff09;-CSDN博客 使用 ChatBox AI 简化本地模型对话操作&#xff1a; 打开 ChatBox AI 官网&#xff1a;Chatbox AI官网&#xff1a;办公学习的AI好助手&#xff0c;全平台AI客户端&#xf…...

缩位求和——蓝桥杯

1.题目描述 在电子计算机普及以前&#xff0c;人们经常用一个粗略的方法来验算四则运算是否正确。 比如&#xff1a;248153720248153720 把乘数和被乘数分别逐位求和&#xff0c;如果是多位数再逐位求和&#xff0c;直到是 1 位数&#xff0c;得 24814>145 156 56 而…...

hexo部署到github page时,hexo d后page里面绑定的个人域名消失的问题

Hexo 部署博客到 GitHub page 后&#xff0c;可以在 setting 中的 page 中绑定自己的域名&#xff0c;但是我发现更新博客后绑定的域名消失&#xff0c;恢复原始的 githubio 的域名。 后面搜索发现需要在 repo 里面添加 CNAME 文件&#xff0c;内容为 page 里面绑定的域名&…...

neo4j入门

文章目录 neo4j版本说明部署安装Mac部署docker部署 neo4j web工具使用数据结构图数据库VS关系数据库 neo4j neo4j官网Neo4j是用ava实现的开源NoSQL图数据库。Neo4作为图数据库中的代表产品&#xff0c;已经在众多的行业项目中进行了应用&#xff0c;如&#xff1a;网络管理&am…...

代码随想录——回溯

文章目录 组合组合总数电话号码的字母组合组合总数组合总数Ⅱ分割回文串复原IP地址子集子集Ⅱ非递减子序列去重的实现方法方法 1&#xff1a;**排序 跳过重复元素**方法 2&#xff1a;**使用哈希表或数组记录已使用的数字** 去重的完整示例总结本题代码 全排列全排列Ⅱ重新安排…...

独立游戏RPG回顾:高成本

刚看了某纪录片&#xff0c; 内容是rpg项目的回顾。也是这个以钱为核心话题的系列的最后一集。 对这期特别有代入感&#xff0c;因为主角是曾经的同事&#xff0c;曾经在某天晚上听过其项目组的争论。 对其这些年的起伏特别的能体会。 主角是制作人&#xff0c;在访谈中透露这…...

SQLModel入门

目录 概述快速开始官方教程简单使用样例 概述 SQLModel 是一个 ORM 框架&#xff0c;其基于 SQLAlchemy 和 Pydantic&#xff0c;其中 SQLALchemy 提供底层 ORM 能力&#xff0c;Pydantic 提供类型校验能力&#xff0c;SQLModel 中&#xff0c;一个 SQLModel model 既是一个 S…...

关于MySQL InnoDB存储引擎的一些认识

文章目录 一、存储引擎1.MySQL中执行一条SQL语句的过程是怎样的&#xff1f;1.1 MySQL的存储引擎有哪些&#xff1f;1.2 MyIsam和InnoDB有什么区别&#xff1f; 2.MySQL表的结构是什么&#xff1f;2.1 行结构是什么样呢&#xff1f;2.1.1 NULL列表&#xff1f;2.1.2 char和varc…...

【学习笔记】深度学习网络-正则化方法

作者选择了由 Ian Goodfellow、Yoshua Bengio 和 Aaron Courville 三位大佬撰写的《Deep Learning》(人工智能领域的经典教程&#xff0c;深度学习领域研究生必读教材),开始深度学习领域学习&#xff0c;深入全面的理解深度学习的理论知识。 在之前的文章中介绍了深度学习中用…...

NVIDIA (英伟达)的 GPU 产品应用领域

游戏娱乐领域 PC 游戏&#xff1a;NVIDIA 的 GeForce 系列 GPU 是 PC 游戏玩家的首选之一。能实现实时光线追踪、高分辨率渲染等&#xff0c;使游戏画面更加逼真&#xff0c;如《赛博朋克 2077》等支持光线追踪的游戏&#xff0c;在 NVIDIA GPU 的加持下&#xff0c;可呈现出真…...

Docker快速部署高效照片管理系统LibrePhotos搭建私有云相册

文章目录 前言1.关于LibrePhotos2.本地部署LibrePhotos3.LibrePhotos简单使用4. 安装内网穿透5.配置LibrePhotos公网地址6. 配置固定公网地址 前言 想象一下这样的场景&#xff1a;你有一大堆珍贵的回忆照片&#xff0c;但又不想使用各种网盘来管理。怎么办&#xff1f;别担心…...

goframe 多语言国际化解决方案

项目背景 本项目采用基于JSON配置的多语言国际化&#xff08;i18n&#xff09;解决方案&#xff0c;支持多种语言的无缝切换和本地化。 目录结构 manifest/ └── i18n/├── zh.json # 简体中文├── zh-tw.json # 繁体中文├── en.json # 英语├…...

mysql如何修改密码

在MySQL中修改密码可以通过多种方式完成&#xff0c;具体取决于你的MySQL版本和你是否有足够的权限。以下是一些常用的方法来修改MySQL用户的密码&#xff1a; 方法1: 使用ALTER USER命令 这是最常用的方法&#xff0c;适用于MySQL 5.7及以上版本。 ALTER USER usernameloca…...

17.2 图形绘制8

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 17.2.10 重绘 先看以下例子&#xff1a; 【例 17.28】【项目&#xff1a;code17-028】绘制填充矩形。 private void button1_Clic…...

Java基础知识总结(三十八)--读取数据

使用Reader体系&#xff0c;读取一个文本文件中的数据。返回 -1 &#xff0c;标志读到结尾。 import java.io.*; class { public static void main(String[] args) throws IOException { /* 创建可以读取文本文件的流对象&#xff0c;让创建好的流对象和指定的文件相关联。…...