Openfga 授权模型搭建

news2025/2/3 17:34:06

1.根据项目去启动 配置一个 openfga 服务器

先创建一个 config.yaml文件

cd /opt/openFGA/conf
touch ./config.yaml

怎么配置?

根据官网来看

openfga/.config-schema.json at main · openfga/openfga · GitHub

这里讲述详细的每一个配置每一个类型

这些配置有三种形式配置方式

命令行

需要用 - 隔开每个 属性下的属性 和 properties 的 . 类似

环境变量

OpenFGA 服务器支持用于配置的环境变量,它们将优先于您的配置文件。每个变量都必须以 OPENFGA_ 为前缀,后跟大写的选项(例如,--grpc-tls-key 变为 OPENFGA_GRPC_TLS_KEY)。

官方也讲了怎么配置

 config.yaml文件

需要映射数据卷

这里我们选择使用yaml配置

我们其他的用命令行配置,然后 把会变的用 yaml配置

1.配置限制单个 BatchCheck 请求中允许的检查数量。

maxChecksPerBatchCheck: 3000

 2.限制可同时解决的 Check 数量

maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100

3.ListObjects 将返回在分配时间内找到的结果 

maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100
listObjectsDeadline: 10s

4.ListObjects 最多为配置的最大结果数

maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100
listObjectsDeadline: 10s
listObjectsMaxResults: 3000

 5.Listuser将返回在分配时间内找到的结果 

maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100
listObjectsDeadline: 10s
listObjectsMaxResults: 3000
listUsersDeadline: 10s

6.ListObjects 最多为配置的最大结果数

maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100
listObjectsDeadline: 10s
listObjectsMaxResults: 3000
listUsersDeadline: 10s
listUsersMaxResults: 3000

 7.最大元组一次写入

maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100
listObjectsDeadline: 10s
listObjectsMaxResults: 3000
listUsersDeadline: 10s
listUsersMaxResults: 3000
maxTuplesPerWrite: 1000

 8.最大每个授权模型定义的类型

maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100
listObjectsDeadline: 10s
listObjectsMaxResults: 3000
listUsersDeadline: 10s
listUsersMaxResults: 3000
maxTuplesPerWrite: 1000
maxTypesPerAuthorizationModel: 3000

9.持久化授权模型所允许的最大字节数(默认值为256KB)。

maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100
listObjectsDeadline: 10s
listObjectsMaxResults: 3000
listUsersDeadline: 10s
listUsersMaxResults: 3000
maxTuplesPerWrite: 1000
maxTypesPerAuthorizationModel: 3000
maxAuthorizationModelSizeInBytes: 262144
requestTimeout: 10s

常用的配置就这么多

安装

docker-compose.yaml

version: '3.8'

networks:
  openfga:

services:
  mysql:
    image: mysql:8
    container_name: mysql
    restart: always
    networks:
      - openfga
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_DATABASE=openfga
    healthcheck:
      test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]
      timeout: 20s
      retries: 5

  migrate:
    depends_on:
      mysql:
        condition: service_healthy
    image: openfga/openfga:latest
    container_name: migrate
    command: migrate
    environment:
      - OPENFGA_DATASTORE_ENGINE=mysql
      - OPENFGA_DATASTORE_URI=root:secret@tcp(mysql:3306)/openfga?parseTime=true
    networks:
      - openfga

  openfga:
    depends_on:
      migrate:
        condition: service_completed_successfully
    image: openfga/openfga:latest
    restart: always
    container_name: openfga
    environment:
      - OPENFGA_DATASTORE_ENGINE=mysql
      - OPENFGA_DATASTORE_URI=root:secret@tcp(mysql:3306)/openfga?parseTime=true
      - OPENFGA_LOG_FORMAT=json
    volumes:
      - /opt/openFGA/conf:/etc/openfga
    command: run
    networks:
      - openfga
    ports:
      # Needed for the http server
      - "8080:8080"
      # Needed for the grpc server (if used)
      - "8081:8081"
      # Needed for the playground (Do not enable in prod!)
      - "3000:3000"

启动

cd /opt/openFGA
docker compose up -d

检测配置文件是否生效

docker compose logs -f

 看日志 是生效了

2.下载fga cli

Releases · openfga/cli · GitHub

vim /etc/profile

修改环境变量文件

# 在环境变量文件中末尾,添加如下内容
export OPEN_FGA_HOME=/opt/openFGA/fgacli
export PATH=$OPEN_FGA_HOME:$PATH
# 重新加载环境变量文件
source /etc/profile

编辑配置文件 

vim /root/.fga.yaml
 .fga.yaml 内容如下 
api-url: http://localhost:8080

 根据官网来

试着执行一下命令

fga store create --name "FGA Demo Store"

表示执行成功,也成功在数据库创建了一个Store

 

3.下载Visual Studio Code

下载插件

下载windows版本的 cli

配置环境变量

然后 将fga目录文件也放入环境变量中,

就可以在任何位置使用 fga命令了

vscode 使用 ctrl shift p 选中 插件中的 transform to json 可以把 .fga 转化为 .json文件

然后创建模型开始 接下来就可以开始测试了

跟着官方文档 一步一步走

GitHub - openfga/cli: A cross-platform CLI to interact with an OpenFGA server

4.测试

这里我们作为测试

为了以后逻辑更加清晰,我们以模块化开发chuangj

创建 fga.mod 模块化开发核心组装文件 

schema: '1.2'
contents:
  - core.fga

 创建 核心 core.fga 其中一个模块

module core

type user

type role
  relations
    define member: [user] 

type application
  relations
    define viewer: [user,role#member] 
    define can_view: viewer


type menu
  relations
    define parent: [application]
    define add: can_view from parent

整合创建模型

fga model write --file=fga.mod 

测试开始 

Microsoft Windows [版本 10.0.19045.5371]
(c) Microsoft Corporation。保留所有权利。

C:\Users\RJ\Documents\fgatestmodel>fga model get
model
  schema 1.2

type application # module: core, file: core.fga
  relations
    define can_view: viewer
    define viewer: [user, role#member]

type role # module: core, file: core.fga       
  relations
    define member: [user]

type user # module: core, file: core.fga       


C:\Users\RJ\Documents\fgatestmodel>fga model validate -file core.fga
Error: unknown shorthand flag: 'f' in -file

C:\Users\RJ\Documents\fgatestmodel>fga model validate --file core.fga 
{
  "is_valid":false,
  "error":"invalid schema version"
}

C:\Users\RJ\Documents\fgatestmodel>fga tuple write user:jmj member role:admin
{
  "successful": [
    {
      "object":"role:admin",
      "relation":"member",
      "user":"user:jmj"
    }
  ]
}

C:\Users\RJ\Documents\fgatestmodel>fga tuple write user:jmj member role:admin
Error: failed to write tuple: Write validation error for POST Write with body {"code":"write_failed_due_to_invalid_input","message":"cannot write a tuple which already exists: user: 'user:jmj', relation: 'member', object: 'role:admin': tuple to be written already existed or the tuple to be deleted did not exist"}
 with error code write_failed_due_to_invalid_input error message: cannot write a tuple which already exists: user: 'user:jmj', relation: 'member', object: 'role:admin': tuple to be written already existed or the tuple to be deleted did not exist

C:\Users\RJ\Documents\fgatestmodel>fga tuple write user:jmj member role:gaojimanager
{
  "successful": [
    {
      "object":"role:gaojimanager",
      "relation":"member",
      "user":"user:jmj"
    }
  ]
}

C:\Users\RJ\Documents\fgatestmodel>fga tuple write user:jmj member role:putongguanliyuan1
{
  "successful": [
    {
      "object":"role:putongguanliyuan1",
      "relation":"member",
      "user":"user:jmj"
    }
  ]
}

C:\Users\RJ\Documents\fgatestmodel>fga tuple read --user user:jmj --relation member --object role:admin
{
  "continuation_token":"",
  "tuples": [
    {
      "key": {
        "object":"role:admin",
        "relation":"member",
        "user":"user:jmj"
      },
      "timestamp":"2025-01-25T03:06:32Z"
    }
  ]
}

C:\Users\RJ\Documents\fgatestmodel>fga tuple changes --type admin
{
  "changes": [],
  "continuation_token":""
}

C:\Users\RJ\Documents\fgatestmodel>fga tuple changes --type role 
{
  "changes": [
    {
      "operation":"TUPLE_OPERATION_WRITE",
      "timestamp":"2025-01-25T03:06:32Z",
      "tuple_key": {
        "object":"role:admin",
        "relation":"member",
        "user":"user:jmj"
      }
    },
    {
      "operation":"TUPLE_OPERATION_WRITE",
      "timestamp":"2025-01-25T03:07:37Z",
      "tuple_key": {
        "object":"role:gaojimanager",
        "relation":"member",
        "user":"user:jmj"
      }
    },
    {
      "operation":"TUPLE_OPERATION_WRITE",
      "timestamp":"2025-01-25T03:07:45Z",
      "tuple_key": {
        "object":"role:putongguanliyuan1",
        "relation":"member",
        "user":"user:jmj"
      }
    }
  ],
  "continuation_token":"eyJ1bGlkIjoiMDFKSkRQVzBDRDJNVzVXNTFHQ1ZRMDQ5QzYiLCJPYmplY3RUeXBlIjoicm9sZSJ9"
}

C:\Users\RJ\Documents\fgatestmodel>fga tuple changes --type role
{
  "changes": [
    {
      "operation":"TUPLE_OPERATION_WRITE",
      "timestamp":"2025-01-25T03:06:32Z",
      "tuple_key": {
        "object":"role:admin",
        "relation":"member",
        "user":"user:jmj"
      }
    },
    {
      "operation":"TUPLE_OPERATION_WRITE",
      "timestamp":"2025-01-25T03:07:37Z",
      "tuple_key": {
        "object":"role:gaojimanager",
        "relation":"member",
        "user":"user:jmj"
      }
    },
    {
      "operation":"TUPLE_OPERATION_WRITE",
      "timestamp":"2025-01-25T03:07:45Z",
      "tuple_key": {
        "object":"role:putongguanliyuan1",
        "relation":"member",
        "user":"user:jmj"
      }
    }
  ],
  "continuation_token":"eyJ1bGlkIjoiMDFKSkRQVzBDRDJNVzVXNTFHQ1ZRMDQ5QzYiLCJPYmplY3RUeXBlIjoicm9sZSJ9"
}

C:\Users\RJ\Documents\fgatestmodel>fga tuple changes --type role  
{
  "changes": [
    {
      "operation":"TUPLE_OPERATION_WRITE",
      "timestamp":"2025-01-25T03:06:32Z",
      "tuple_key": {
        "object":"role:admin",
        "relation":"member",
        "user":"user:jmj"
      }
    },
    {
      "operation":"TUPLE_OPERATION_WRITE",
      "timestamp":"2025-01-25T03:07:37Z",
      "tuple_key": {
        "object":"role:gaojimanager",
        "relation":"member",
        "user":"user:jmj"
      }
    },
    {
      "operation":"TUPLE_OPERATION_WRITE",
      "timestamp":"2025-01-25T03:07:45Z",
      "tuple_key": {
        "object":"role:putongguanliyuan1",
        "relation":"member",
        "user":"user:jmj"
      }
    }
  ],
  "continuation_token":"eyJ1bGlkIjoiMDFKSkRQVzBDRDJNVzVXNTFHQ1ZRMDQ5QzYiLCJPYmplY3RUeXBlIjoicm9sZSJ9"
}

C:\Users\RJ\Documents\fgatestmodel>fga tuple changes --type role --continuation-token=eyJ1bGlkIjoiMDFKSkRQVzBDRDJNVzVXNTFHQ1ZRMDQ5QzYiLCJPYmplY3RUeXBlIjoicm9sZSJ9
{
  "changes": [],
  "continuation_token":"eyJ1bGlkIjoiMDFKSkRQVzBDRDJNVzVXNTFHQ1ZRMDQ5QzYiLCJPYmplY3RUeXBlIjoicm9sZSJ9"
}

C:\Users\RJ\Documents\fgatestmodel>
C:\Users\RJ\Documents\fgatestmodel>fga tuple changes --type role --continuation-token=eyJ1bGlkIjoiMDFKSkRQVzBDRDJNVzVXNTFHQ1ZRMDQ5QzYiLCJPYmplY3RUeXBlIjoicm9sZSJ9
{
  "changes": [],
  "continuation_token":"eyJ1bGlkIjoiMDFKSkRQVzBDRDJNVzVXNTFHQ1ZRMDQ5QzYiLCJPYmplY3RUeXBlIjoicm9sZSJ9"
}

C:\Users\RJ\Documents\fgatestmodel>fga tuple changes --type role
{
  "changes": [
    {
      "operation":"TUPLE_OPERATION_WRITE",
      "timestamp":"2025-01-25T03:06:32Z",
      "tuple_key": {
        "object":"role:admin",
        "relation":"member",
        "user":"user:jmj"
      }
    },
    {
      "operation":"TUPLE_OPERATION_WRITE",
      "timestamp":"2025-01-25T03:07:37Z",
      "tuple_key": {
        "object":"role:gaojimanager",
        "relation":"member",
        "user":"user:jmj"
      }
    },
    {
      "operation":"TUPLE_OPERATION_WRITE",
      "timestamp":"2025-01-25T03:07:45Z",
      "tuple_key": {
        "object":"role:putongguanliyuan1",
        "relation":"member",
        "user":"user:jmj"
      }
    }
  ],
  "continuation_token":"eyJ1bGlkIjoiMDFKSkRQVzBDRDJNVzVXNTFHQ1ZRMDQ5QzYiLCJPYmplY3RUeXBlIjoicm9sZSJ9"
}

C:\Users\RJ\Documents\fgatestmodel>fga query check user:jmj member role:admin 
{
  "allowed":true,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query check user:jmj member role:admin
{
  "allowed":true,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query list-objects user:jmj member role
{
  "objects": [
    "role:admin",
    "role:putongguanliyuan1",
    "role:gaojimanager"
  ]
}

C:\Users\RJ\Documents\fgatestmodel>fga tuple write user:a can_view docu
Error: failed to write tuple: Write validation error for POST Write with body {"code":"validation_error","message":"Invalid tuple 'docu#can_view@user:a'. Reason: invalid 'object' field format"}
 with error code validation_error error message: Invalid tuple 'docu#can_view@user:a'. Reason: invalid 'object' field format

C:\Users\RJ\Documents\fgatestmodel>fga tuple write user:a can_view docu:aa
Error: failed to write tuple: Write validation error for POST Write with body {"code":"validation_error","message":"Invalid tuple 'docu:aa#can_view@user:a'. Reason: type 'docu' not found"}
 with error code validation_error error message: Invalid tuple 'docu:aa#can_view@user:a'. Reason: type 'docu' not found

C:\Users\RJ\Documents\fgatestmodel>fga tuple write user:bzm member  role:admin
{
  "successful": [
    {
      "object":"role:admin",
      "relation":"member",
      "user":"user:bzm"
    }
  ]
}

C:\Users\RJ\Documents\fgatestmodel>fga query list-relations  user:jmj  role:admin --relation member
{
  "relations": [
    "member"
  ]
}

C:\Users\RJ\Documents\fgatestmodel>fga query list-relations  user:jmj  role --relation member       
{
  "relations": []
}

C:\Users\RJ\Documents\fgatestmodel>fga query list-relations  user:jmj   --relation member     
Error: accepts 2 arg(s), received 1

C:\Users\RJ\Documents\fgatestmodel>fga query list-relations  role:admin   --relation member 
Error: accepts 2 arg(s), received 1

C:\Users\RJ\Documents\fgatestmodel>fga query list-relations  role:admin  user --relation member 
{
  "relations": []
}

C:\Users\RJ\Documents\fgatestmodel>fga query list-relations  role:admin  user:jmj --relation member 
{
  "relations": []
}

C:\Users\RJ\Documents\fgatestmodel>fga query list-relations    user:jmj role:admin --relation member 
{
  "relations": [
    "member"
  ]
}

C:\Users\RJ\Documents\fgatestmodel>fga query expand member  role:admin
{
  "tree": {
    "root": {
      "leaf": {
        "users": {
          "users": [
            "user:bzm",
            "user:jmj"
          ]
        }
      },
      "name":"role:admin#member"
    }
  }
}

C:\Users\RJ\Documents\fgatestmodel>fga query list-uers --object role:admin --relation member --user0filter user
Error: unknown flag: --object

C:\Users\RJ\Documents\fgatestmodel>fga query list-uers --object role:admin --relation member --userfilter user  
Error: unknown flag: --object

C:\Users\RJ\Documents\fgatestmodel>fga query list-uers --object role:admin --relation member --user-filter user 
Error: unknown flag: --object

C:\Users\RJ\Documents\fgatestmodel>fga query list-uers --object  role:admin --relation member --user-filter user
Error: unknown flag: --object

C:\Users\RJ\Documents\fgatestmodel>fga query list-users --object  role:admin --relation member --user-filter user 
{
  "users": [
    {
      "object": {
        "id":"jmj",
        "type":"user"
      }
    },
    {
      "object": {
        "id":"bzm",
        "type":"user"
      }
    }
  ]
}

C:\Users\RJ\Documents\fgatestmodel>fga tuple write role:admin#member viewer application:tigeriotapp
{
  "successful": [
    {
      "object":"application:tigeriotapp",
      "relation":"viewer",
      "user":"role:admin#member"
    }
  ]
}

C:\Users\RJ\Documents\fgatestmodel>fga tuple changes --type application
{
  "changes": [
    {
      "operation":"TUPLE_OPERATION_WRITE",
      "timestamp":"2025-01-25T03:22:43Z",
      "tuple_key": {
        "object":"application:tigeriotapp",
        "relation":"viewer",
        "user":"role:admin#member"
      }
    }
  ],
  "continuation_token":"eyJ1bGlkIjoiMDFKSkRRUUQyQkozQ1M2RDE2NzY4MTBDSloiLCJPYmplY3RUeXBlIjoiYXBwbGljYXRpb24ifQ=="
}

C:\Users\RJ\Documents\fgatestmodel>fga tuple write role:admin#1member viewer application:tigeriotapp
Error: failed to write tuple: Write validation error for POST Write with body {"code":"validation_error","message":"Invalid tuple 'application:tigeriotapp#viewer@role:admin#1member'. Reason: relation 'role#1member' not found"} with error code validation_error error message: Invalid tuple 'application:tigeriotapp#viewer@role:admin#1member'. Reason: relation 'role#1member' not found

C:\Users\RJ\Documents\fgatestmodel>fga query check user:jmj can_view application:tigeriotapp
{
  "allowed":true,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query check user:bzm can_view application:tigeriotapp 
{
  "allowed":true,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query check user:cc can_view application:tigeriotapp  
{
  "allowed":false,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query check user:cc can_view application:tigeriotapp --contextual-tuple "user:cc member role:admin" 
{
  "allowed":true,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga model list
{
  "authorization_models": [
    {
      "id":"01JJDP39W07DXX87KS937GKK9S",
      "created_at":"2025-01-25T02:54:15.936Z"
    }
  ]
}

C:\Users\RJ\Documents\fgatestmodel>fga tuple write application:tigeriotapp parent menu:erwangpingheng 
Error: failed to write tuple: Write validation error for POST Write with body {"code":"validation_error","message":"Invalid tuple 'menu:erwangpingheng#parent@application:tigeriotapp'. Reason: type 'menu' not found"}
 with error code validation_error error message: Invalid tuple 'menu:erwangpingheng#parent@application:tigeriotapp'. Reason: type 'menu' not found

C:\Users\RJ\Documents\fgatestmodel>fga model write --file =fga.mod
Error: failed to read file =fga.mod due to open =fga.mod: The system cannot find the file specified.

C:\Users\RJ\Documents\fgatestmodel>fga model write --file=fga.mod  
{
  "authorization_model_id":"01JJDRB8QK912Y0EW06BMT7VG4"
}

C:\Users\RJ\Documents\fgatestmodel>fga tuple write application:tigeriotapp parent menu:erwangpingheng
{
  "successful": [
    {
      "object":"menu:erwangpingheng",
      "relation":"parent",
      "user":"application:tigeriotapp"
    }
  ]
}

C:\Users\RJ\Documents\fgatestmodel>fga query check user:jmj member role:admin
{
  "allowed":true,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query check role:admin#rember viewer application:tigeriotapp
Error: check failed: Check validation error for POST Check with body {"code":"validation_error","message":"relation 'role#rember' not found"}
 with error code validation_error error message: relation 'role#rember' not found

C:\Users\RJ\Documents\fgatestmodel>fga query check role:admin#member viewer application:tigeriotapp 
{
  "allowed":true,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query check role:admin#member can_view  application:tigeriotapp 
{
  "allowed":true,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query check role:admin add  menu:erwangpingheng                 
{
  "allowed":false,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query check application:tigeriotapp add menu:erwangpingheng
{
  "allowed":false,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query check application:tigeriotapp parent  menu:erwangpingheng 
{
  "allowed":true,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query expand add menu:erwangpingheng
{
  "tree": {
    "root": {
      "leaf": {
        "tupleToUserset": {
          "computed": [
            {
              "userset":"application:tigeriotapp#can_view"
            }
          ],
          "tupleset":"menu:erwangpingheng#parent"
        }
      },
      "name":"menu:erwangpingheng#add"
    }
  }
}

C:\Users\RJ\Documents\fgatestmodel>fga query user:jmj  add  menu:erwangpingheng                        
Run queries (Check, Expand, ListObjects, ListRelations, ListUsers) that are evaluated according to a particular model.

Usage:
  fga query [command]

Available Commands:
  check          Check
  expand         Expand
  list-objects   List Objects
  list-relations List Relations
  list-users     List users

Flags:
      --consistency string             Consistency preference for the request. Valid options are HIGHER_CONSISTENCY and MINIMIZE_LATENCY.
      --context string                 Query context (as a JSON string)
      --contextual-tuple stringArray   Contextual Tuple, output: "user relation object"
  -h, --help                           help for query
      --model-id string                Model ID
      --store-id string                Store ID

Global Flags:
      --api-audience string       API Audience. Used when performing the Client Credentials flow
      --api-scopes stringArray    API Scopes (repeat option for multiple values). Used in the Client Credentials flow
      --api-token string          API Token. Will be sent in as a Bearer in the Authorization header
      --api-token-issuer string   API Token Issuer. API responsible for issuing the API Token. Used in the Client Credentials flow
      --api-url string            OpenFGA API URI e.g. https://api.fga.example:8080 (default "http://localhost:8080")
      --client-id string          Client ID. Sent to the Token Issuer during the Client Credentials flow
      --client-secret string      Client Secret. Sent to the Token Issuer during the Client Credentials flow
      --config string             config file (default is $HOME/.fga.yaml)

Use "fga query [command] --help" for more information about a command.

C:\Users\RJ\Documents\fgatestmodel>fga query check  user:jmj  add  menu:erwangpingheng 
{
  "allowed":true,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query check  user:jmj  add  menu:erwangpingheng
{
  "allowed":true,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query check  user:cc  add  menu:erwangpingheng  
{
  "allowed":false,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query check  user:jmj  add  menu:erwangpingheng 
{
  "allowed":true,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query check  role:admin  add  menu:erwangpingheng 
{
  "allowed":false,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query check  role:admin#member  add  menu:erwangpingheng 
{
  "allowed":true,
  "resolution":""
}

C:\Users\RJ\Documents\fgatestmodel>fga query check  user:jmj  add  menu:erwangpingheng          
{
  "allowed":true,
  "resolution":""
}

把我的测试记录保存下来了

测试没问题,下一章节我们将 实战入我们自己的项目

5.整合Java SDK

<dependency>
    <groupId>dev.openfga</groupId>
    <artifactId>openfga-sdk</artifactId>
    <version>0.7.1</version>
</dependency>

6.创建一个新的项目

7.导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.jmj</groupId>
    <artifactId>openFGA-snap</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>openFGA-snap</name>
    <description>openFGA-snap</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>21</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>dev.openfga</groupId>
            <artifactId>openfga-sdk</artifactId>
            <version>0.3.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

8.从头开始 创建Store

模型 store -> authmodel -> tuple

一个store 里的 授权模型 是以版本号迭代的 ,也就是一个 store 就一个 授权模型,你可以使用不同版本

我们以模块化开发授权模型

1.fga.mod 归纳模块

schema: '1.2'
contents:
  - core.fga

2.core.fga 核心模块

module core

#核心模块
#用户类型
type user 

#角色类型
#关系
# 成员 :member :用户类型
type role 
  relations
    define member: [user]  

#应用类型
#关系
# can_access:能否访问: 角色成员用户集
type application 
  relations
    define can_access: [role#member] 

#页面模块分组
#关系
#parent_model父模型
#可见身份: 用户成员组绑定
#访问身份: 用户成员组绑定 且 必须父模型要有访问权限
#能否查看 查看者 与 访问者 都可以访问
#能否访问 只有访问者可以访问
type page_module_group 
  relations
    define parent_model: [application] 
    define viewer: [role#member]
    define accesser: [role#member] and can_access from parent_model
    define can_viewer: viewer or accesser
    define can_access: accesser

#页面模块
#关系
#parent_model父模型
#可见身份: 用户成员组绑定
#访问身份: 用户成员组绑定 且 必须父模型要有访问权限
#能否查看 查看者 与 访问者 都可以访问
#能否访问 只有访问者可以访问
type page_module
  relations
    define parent_model: [page_module_group] 
    define viewer: [role#member]
    define accesser: [role#member] and can_access from parent_model
    define can_viewer: viewer or accesser
    define can_access: accesser

#页面菜单
#关系
#parent_model父模型
#可见身份: 用户成员组绑定
#访问身份: 用户成员组绑定 且 必须父模型要有访问权限
#能否查看 查看者 与 访问者 都可以访问
#能否访问 只有访问者可以访问
type page_menu
  relations
    define parent_model: [page_module] 
    define viewer: [role#member]
    define accesser: [role#member] and can_access from parent_model
    define can_viewer: viewer or accesser
    define can_access: accesser

#页面界面
#关系
#parent_model父模型
#可见身份: 用户成员组绑定
#访问身份: 用户成员组绑定 且 必须父模型要有访问权限
#能否查看 查看者 与 访问者 都可以访问
#能否访问 只有访问者可以访问
type page_view
  relations
    define parent_model: [page_menu] 
    define viewer: [role#member]
    define accesser: [role#member] and can_access from parent_model
    define can_viewer: viewer or accesser
    define can_access: accesser

 配好环境变量

fga model write --file=fga.mod 

一个store 就专注于一个 授权模型,由版本进行迭代维护升级,而不是 多个授权模型

9. 创建一个Store

package com.jmj.openfgatest.openfga.init;

import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.client.model.ClientCreateStoreResponse;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.api.model.CreateStoreRequest;
import dev.openfga.sdk.errors.FgaInvalidParameterException;

import java.util.concurrent.ExecutionException;

public class CreateStore {
    public static void main(String[] args) throws FgaInvalidParameterException, ExecutionException, InterruptedException {

        ClientConfiguration clientConfiguration = new ClientConfiguration()
                .apiUrl("http://192.168.59.100:8080");

        OpenFgaClient openFgaClient = new OpenFgaClient(clientConfiguration);

        CreateStoreRequest body = new CreateStoreRequest()
                .name("authorTreePage");

        ClientCreateStoreResponse store = openFgaClient.createStore(body).get();
        System.out.println(store.getId());

    }
}

10.创建一个授权模型

package com.jmj.openfgatest.openfga.init;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.client.model.ClientWriteAuthorizationModelResponse;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.api.model.WriteAuthorizationModelRequest;
import dev.openfga.sdk.errors.FgaInvalidParameterException;

import java.util.concurrent.ExecutionException;

public class CreateAuthorizeModel {
    public static void main(String[] args) throws FgaInvalidParameterException, JsonProcessingException, ExecutionException, InterruptedException {

        ClientConfiguration clientConfiguration = new ClientConfiguration()
                .apiUrl("http://192.168.59.100:8080")
                .storeId("01JJN8S18TRYPMTXHG0JE50402")
                ;

        OpenFgaClient openFgaClient = new OpenFgaClient(clientConfiguration);

        ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
        ClientWriteAuthorizationModelResponse authorizationModelResponse = openFgaClient.writeAuthorizationModel(mapper
                .readValue("{\n" +
                        "  \"schema_version\": \"1.1\",\n" +
                        "  \"type_definitions\": [\n" +
                        "    {\n" +
                        "      \"type\": \"user\",\n" +
                        "      \"relations\": {},\n" +
                        "      \"metadata\": null\n" +
                        "    },\n" +
                        "    {\n" +
                        "      \"type\": \"role\",\n" +
                        "      \"relations\": {\n" +
                        "        \"member\": {\n" +
                        "          \"this\": {}\n" +
                        "        }\n" +
                        "      },\n" +
                        "      \"metadata\": {\n" +
                        "        \"relations\": {\n" +
                        "          \"member\": {\n" +
                        "            \"directly_related_user_types\": [\n" +
                        "              {\n" +
                        "                \"type\": \"user\"\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          }\n" +
                        "        }\n" +
                        "      }\n" +
                        "    },\n" +
                        "    {\n" +
                        "      \"type\": \"application\",\n" +
                        "      \"relations\": {\n" +
                        "        \"can_access\": {\n" +
                        "          \"this\": {}\n" +
                        "        }\n" +
                        "      },\n" +
                        "      \"metadata\": {\n" +
                        "        \"relations\": {\n" +
                        "          \"can_access\": {\n" +
                        "            \"directly_related_user_types\": [\n" +
                        "              {\n" +
                        "                \"type\": \"role\",\n" +
                        "                \"relation\": \"member\"\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          }\n" +
                        "        }\n" +
                        "      }\n" +
                        "    },\n" +
                        "    {\n" +
                        "      \"type\": \"page_module_group\",\n" +
                        "      \"relations\": {\n" +
                        "        \"parent_model\": {\n" +
                        "          \"this\": {}\n" +
                        "        },\n" +
                        "        \"viewer\": {\n" +
                        "          \"this\": {}\n" +
                        "        },\n" +
                        "        \"accesser\": {\n" +
                        "          \"intersection\": {\n" +
                        "            \"child\": [\n" +
                        "              {\n" +
                        "                \"this\": {}\n" +
                        "              },\n" +
                        "              {\n" +
                        "                \"tupleToUserset\": {\n" +
                        "                  \"computedUserset\": {\n" +
                        "                    \"relation\": \"can_access\"\n" +
                        "                  },\n" +
                        "                  \"tupleset\": {\n" +
                        "                    \"relation\": \"parent_model\"\n" +
                        "                  }\n" +
                        "                }\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          }\n" +
                        "        },\n" +
                        "        \"can_viewer\": {\n" +
                        "          \"union\": {\n" +
                        "            \"child\": [\n" +
                        "              {\n" +
                        "                \"computedUserset\": {\n" +
                        "                  \"relation\": \"viewer\"\n" +
                        "                }\n" +
                        "              },\n" +
                        "              {\n" +
                        "                \"computedUserset\": {\n" +
                        "                  \"relation\": \"accesser\"\n" +
                        "                }\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          }\n" +
                        "        },\n" +
                        "        \"can_access\": {\n" +
                        "          \"computedUserset\": {\n" +
                        "            \"relation\": \"accesser\"\n" +
                        "          }\n" +
                        "        }\n" +
                        "      },\n" +
                        "      \"metadata\": {\n" +
                        "        \"relations\": {\n" +
                        "          \"parent_model\": {\n" +
                        "            \"directly_related_user_types\": [\n" +
                        "              {\n" +
                        "                \"type\": \"application\"\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          },\n" +
                        "          \"viewer\": {\n" +
                        "            \"directly_related_user_types\": [\n" +
                        "              {\n" +
                        "                \"type\": \"role\",\n" +
                        "                \"relation\": \"member\"\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          },\n" +
                        "          \"accesser\": {\n" +
                        "            \"directly_related_user_types\": [\n" +
                        "              {\n" +
                        "                \"type\": \"role\",\n" +
                        "                \"relation\": \"member\"\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          },\n" +
                        "          \"can_viewer\": {\n" +
                        "            \"directly_related_user_types\": []\n" +
                        "          },\n" +
                        "          \"can_access\": {\n" +
                        "            \"directly_related_user_types\": []\n" +
                        "          }\n" +
                        "        }\n" +
                        "      }\n" +
                        "    },\n" +
                        "    {\n" +
                        "      \"type\": \"page_module\",\n" +
                        "      \"relations\": {\n" +
                        "        \"parent_model\": {\n" +
                        "          \"this\": {}\n" +
                        "        },\n" +
                        "        \"viewer\": {\n" +
                        "          \"this\": {}\n" +
                        "        },\n" +
                        "        \"accesser\": {\n" +
                        "          \"intersection\": {\n" +
                        "            \"child\": [\n" +
                        "              {\n" +
                        "                \"this\": {}\n" +
                        "              },\n" +
                        "              {\n" +
                        "                \"tupleToUserset\": {\n" +
                        "                  \"computedUserset\": {\n" +
                        "                    \"relation\": \"can_access\"\n" +
                        "                  },\n" +
                        "                  \"tupleset\": {\n" +
                        "                    \"relation\": \"parent_model\"\n" +
                        "                  }\n" +
                        "                }\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          }\n" +
                        "        },\n" +
                        "        \"can_viewer\": {\n" +
                        "          \"union\": {\n" +
                        "            \"child\": [\n" +
                        "              {\n" +
                        "                \"computedUserset\": {\n" +
                        "                  \"relation\": \"viewer\"\n" +
                        "                }\n" +
                        "              },\n" +
                        "              {\n" +
                        "                \"computedUserset\": {\n" +
                        "                  \"relation\": \"accesser\"\n" +
                        "                }\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          }\n" +
                        "        },\n" +
                        "        \"can_access\": {\n" +
                        "          \"computedUserset\": {\n" +
                        "            \"relation\": \"accesser\"\n" +
                        "          }\n" +
                        "        }\n" +
                        "      },\n" +
                        "      \"metadata\": {\n" +
                        "        \"relations\": {\n" +
                        "          \"parent_model\": {\n" +
                        "            \"directly_related_user_types\": [\n" +
                        "              {\n" +
                        "                \"type\": \"page_module_group\"\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          },\n" +
                        "          \"viewer\": {\n" +
                        "            \"directly_related_user_types\": [\n" +
                        "              {\n" +
                        "                \"type\": \"role\",\n" +
                        "                \"relation\": \"member\"\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          },\n" +
                        "          \"accesser\": {\n" +
                        "            \"directly_related_user_types\": [\n" +
                        "              {\n" +
                        "                \"type\": \"role\",\n" +
                        "                \"relation\": \"member\"\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          },\n" +
                        "          \"can_viewer\": {\n" +
                        "            \"directly_related_user_types\": []\n" +
                        "          },\n" +
                        "          \"can_access\": {\n" +
                        "            \"directly_related_user_types\": []\n" +
                        "          }\n" +
                        "        }\n" +
                        "      }\n" +
                        "    },\n" +
                        "    {\n" +
                        "      \"type\": \"page_menu\",\n" +
                        "      \"relations\": {\n" +
                        "        \"parent_model\": {\n" +
                        "          \"this\": {}\n" +
                        "        },\n" +
                        "        \"viewer\": {\n" +
                        "          \"this\": {}\n" +
                        "        },\n" +
                        "        \"accesser\": {\n" +
                        "          \"intersection\": {\n" +
                        "            \"child\": [\n" +
                        "              {\n" +
                        "                \"this\": {}\n" +
                        "              },\n" +
                        "              {\n" +
                        "                \"tupleToUserset\": {\n" +
                        "                  \"computedUserset\": {\n" +
                        "                    \"relation\": \"can_access\"\n" +
                        "                  },\n" +
                        "                  \"tupleset\": {\n" +
                        "                    \"relation\": \"parent_model\"\n" +
                        "                  }\n" +
                        "                }\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          }\n" +
                        "        },\n" +
                        "        \"can_viewer\": {\n" +
                        "          \"union\": {\n" +
                        "            \"child\": [\n" +
                        "              {\n" +
                        "                \"computedUserset\": {\n" +
                        "                  \"relation\": \"viewer\"\n" +
                        "                }\n" +
                        "              },\n" +
                        "              {\n" +
                        "                \"computedUserset\": {\n" +
                        "                  \"relation\": \"accesser\"\n" +
                        "                }\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          }\n" +
                        "        },\n" +
                        "        \"can_access\": {\n" +
                        "          \"computedUserset\": {\n" +
                        "            \"relation\": \"accesser\"\n" +
                        "          }\n" +
                        "        }\n" +
                        "      },\n" +
                        "      \"metadata\": {\n" +
                        "        \"relations\": {\n" +
                        "          \"parent_model\": {\n" +
                        "            \"directly_related_user_types\": [\n" +
                        "              {\n" +
                        "                \"type\": \"page_module\"\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          },\n" +
                        "          \"viewer\": {\n" +
                        "            \"directly_related_user_types\": [\n" +
                        "              {\n" +
                        "                \"type\": \"role\",\n" +
                        "                \"relation\": \"member\"\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          },\n" +
                        "          \"accesser\": {\n" +
                        "            \"directly_related_user_types\": [\n" +
                        "              {\n" +
                        "                \"type\": \"role\",\n" +
                        "                \"relation\": \"member\"\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          },\n" +
                        "          \"can_viewer\": {\n" +
                        "            \"directly_related_user_types\": []\n" +
                        "          },\n" +
                        "          \"can_access\": {\n" +
                        "            \"directly_related_user_types\": []\n" +
                        "          }\n" +
                        "        }\n" +
                        "      }\n" +
                        "    },\n" +
                        "    {\n" +
                        "      \"type\": \"page_view\",\n" +
                        "      \"relations\": {\n" +
                        "        \"parent_model\": {\n" +
                        "          \"this\": {}\n" +
                        "        },\n" +
                        "        \"viewer\": {\n" +
                        "          \"this\": {}\n" +
                        "        },\n" +
                        "        \"accesser\": {\n" +
                        "          \"intersection\": {\n" +
                        "            \"child\": [\n" +
                        "              {\n" +
                        "                \"this\": {}\n" +
                        "              },\n" +
                        "              {\n" +
                        "                \"tupleToUserset\": {\n" +
                        "                  \"computedUserset\": {\n" +
                        "                    \"relation\": \"can_access\"\n" +
                        "                  },\n" +
                        "                  \"tupleset\": {\n" +
                        "                    \"relation\": \"parent_model\"\n" +
                        "                  }\n" +
                        "                }\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          }\n" +
                        "        },\n" +
                        "        \"can_viewer\": {\n" +
                        "          \"union\": {\n" +
                        "            \"child\": [\n" +
                        "              {\n" +
                        "                \"computedUserset\": {\n" +
                        "                  \"relation\": \"viewer\"\n" +
                        "                }\n" +
                        "              },\n" +
                        "              {\n" +
                        "                \"computedUserset\": {\n" +
                        "                  \"relation\": \"accesser\"\n" +
                        "                }\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          }\n" +
                        "        },\n" +
                        "        \"can_access\": {\n" +
                        "          \"computedUserset\": {\n" +
                        "            \"relation\": \"accesser\"\n" +
                        "          }\n" +
                        "        }\n" +
                        "      },\n" +
                        "      \"metadata\": {\n" +
                        "        \"relations\": {\n" +
                        "          \"parent_model\": {\n" +
                        "            \"directly_related_user_types\": [\n" +
                        "              {\n" +
                        "                \"type\": \"page_menu\"\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          },\n" +
                        "          \"viewer\": {\n" +
                        "            \"directly_related_user_types\": [\n" +
                        "              {\n" +
                        "                \"type\": \"role\",\n" +
                        "                \"relation\": \"member\"\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          },\n" +
                        "          \"accesser\": {\n" +
                        "            \"directly_related_user_types\": [\n" +
                        "              {\n" +
                        "                \"type\": \"role\",\n" +
                        "                \"relation\": \"member\"\n" +
                        "              }\n" +
                        "            ]\n" +
                        "          },\n" +
                        "          \"can_viewer\": {\n" +
                        "            \"directly_related_user_types\": []\n" +
                        "          },\n" +
                        "          \"can_access\": {\n" +
                        "            \"directly_related_user_types\": []\n" +
                        "          }\n" +
                        "        }\n" +
                        "      }\n" +
                        "    }\n" +
                        "  ]\n" +
                        "}", WriteAuthorizationModelRequest.class)
        ).get();

        System.out.println(authorizationModelResponse.getAuthorizationModelId());

    }
}

11.创建一个配置属性类用于放入 openfga的配置

package com.jmj.openfgatest.openfga.configuration;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("openfga")
@Data
public class OpenfgaConfigurationProperties {

    private String storeId;

    private String apiUrl;

    private String authorizeModelId;

}

12.创建一个配置类 

package com.jmj.openfgatest.openfga.configuration;

import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.errors.FgaInvalidParameterException;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(OpenfgaConfigurationProperties.class)
public class OpenfgaAutoConfiguration {


    @Bean
    public OpenFgaClient openFgaClient(OpenfgaConfigurationProperties openfgaConfigurationProperties) throws FgaInvalidParameterException {
        String apiUrl = openfgaConfigurationProperties.getApiUrl();
        String storeId = openfgaConfigurationProperties.getStoreId();
        String authorizeModelId = openfgaConfigurationProperties.getAuthorizeModelId();

        ClientConfiguration clientConfiguration = new ClientConfiguration()
                .apiUrl(apiUrl)
                .storeId(storeId)
                .authorizationModelId(authorizeModelId);

        return new OpenFgaClient(clientConfiguration);
    }




}

13.在yaml里配置

openfga:
  api-url: http://192.168.59.100:8080
  store-id: 01JJN8S18TRYPMTXHG0JE50402
  authorize-model-id: 01JJN9399F4YR3DZ7KJ65MZ3RW

14.openfga controller

package com.jmj.openfgatest.controller;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.jmj.openfgatest.common.TreePageEntityConst;
import com.jmj.openfgatest.common.TreeUtils;
import com.jmj.openfgatest.entity.BatchCheckRequest;
import com.jmj.openfgatest.entity.ListByUserRequest;
import com.jmj.openfgatest.entity.TreePageEntity;
import com.jmj.openfgatest.entity.TreePageNode;
import com.jmj.openfgatest.openfga.configuration.OpenfgaConfigurationProperties;
import com.jmj.openfgatest.repository.TreePageEntityRepository;
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.client.model.*;
import dev.openfga.sdk.errors.FgaInvalidParameterException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/treePage")
public class TreePageController {


    @Autowired
    private TreePageEntityRepository treePageEntityRepository;
    @Autowired
    private OpenFgaClient openFgaClient;


    @GetMapping("/create")
    @Transactional(rollbackFor = Exception.class)
    public String create() throws FgaInvalidParameterException, ExecutionException, InterruptedException {
        try {
            //创建基本数据
            List<TreePageEntity> list = treePageEntityRepository.findByType(TreePageEntityConst.Type.PAGE_MENU.value);
            ArrayList<TreePageEntity> arrayList = new ArrayList<>();

            for (TreePageEntity t : list) {
                for (int i = 0; i < 1; i++) {
                    TreePageEntity treePageEntity = new TreePageEntity();
                    treePageEntity.setName("模型界面" + i);
                    treePageEntity.setType(TreePageEntityConst.Type.PAGE_VIEW.value);
                    treePageEntity.setParentId(t.getId());
                    arrayList.add(treePageEntity);
                }

            }
            treePageEntityRepository.saveAll(arrayList);

            List<ClientTupleKey> parentModel = arrayList.stream()
                    .map(
                            t -> {
                                return new ClientTupleKey()
                                        .user(TreePageEntityConst.Type.PAGE_MENU.model + ":" + t.getParentId())
                                        .relation("parent_model")
                                        ._object(TreePageEntityConst.Type.PAGE_VIEW.model + ":" + t.getId());
                            }
                    ).collect(Collectors.toList());


            System.out.println(parentModel.size());


            ArrayList<ClientTupleKey> clientTupleKeys = new ArrayList<>();

            for (int i = 0; i < parentModel.size(); i++) {
                clientTupleKeys.add(parentModel.get(i));

                if ((i + 1) % 10 == 0) {
                    System.out.println("=============================================");

                    for (ClientTupleKey clientTupleKey : clientTupleKeys) {
                        System.out.println(StrUtil.format("user:{},relation:{},object:{}",clientTupleKey.getUser(),clientTupleKey.getRelation(),clientTupleKey.getObject()));
                    }

                    CompletableFuture<ClientWriteResponse> write = openFgaClient.write(
                            new ClientWriteRequest()
                                    .writes(clientTupleKeys)
                    );

                    ClientWriteResponse clientWriteResponse = write.get();
                    System.out.println(clientWriteResponse.getRawResponse());

                    clientTupleKeys.clear();
                }

            }



        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
        return "成功";
    }

    @GetMapping("/check")
    @Transactional(rollbackFor = Exception.class)
    public String check() throws FgaInvalidParameterException, ExecutionException, InterruptedException {

        ClientCheckRequest parentModel = new ClientCheckRequest()
                .user(TreePageEntityConst.Type.PAGE_MODULE.model + ":" + "2011")
                .relation("parent_model")
                ._object(TreePageEntityConst.Type.PAGE_MENU.model + ":" + "3011");

        ClientCheckResponse clientCheckResponse = openFgaClient.check(parentModel).get();
        System.out.println(clientCheckResponse);
        return clientCheckResponse.getAllowed() + "";
    }

    @GetMapping("/list")
    public List<TreePageNode> list() throws FgaInvalidParameterException, ExecutionException, InterruptedException {

        List<TreePageEntity> all = treePageEntityRepository.findAll();


        List<TreePageNode> collect = all.stream().map(TreePageNode::of).collect(Collectors.toList());

        System.out.println(all.size());

        return TreeUtils.buildTree(collect);


    }

    @GetMapping("/listObjByOpenfga")
    public List<TreePageEntity> listObjByOpenfga() throws FgaInvalidParameterException, ExecutionException, InterruptedException {

        var body = new ClientListObjectsRequest()
                .user(TreePageEntityConst.Type.PAGE_MODULE.model + ":" + "2011")
                .relation("parent_model")
                .type(TreePageEntityConst.Type.PAGE_MENU.model);

        CompletableFuture<ClientListObjectsResponse> clientListObjectsResponseCompletableFuture = openFgaClient.listObjects(body);

        ClientListObjectsResponse clientListObjectsResponse = clientListObjectsResponseCompletableFuture.get();
        System.out.println(clientListObjectsResponse.getObjects());


        List<TreePageEntity> byParentId = treePageEntityRepository.findByParentId(2011L);
        System.out.println(byParentId.stream().map(TreePageEntity::getId).toList());


        return byParentId;
    }

    @GetMapping("/listRelationByOpenfga")
    public List<String> listRelationByOpenfga() throws FgaInvalidParameterException, ExecutionException, InterruptedException {


        ClientListRelationsResponse clientListRelationsResponse = openFgaClient.listRelations(
                new ClientListRelationsRequest()
                        .user(TreePageEntityConst.Type.PAGE_MODULE.model + ":" + "2011")
                        .relations(List.of("parent_model", "viewer", "accesser"))
                        ._object(TreePageEntityConst.Type.PAGE_MENU.model + ":" + 3011)
        ).get();

        System.out.println(clientListRelationsResponse.getRelations());
        return clientListRelationsResponse.getRelations();
    }

    @Autowired
    private OpenfgaConfigurationProperties openfgaConfigurationProperties;

    @GetMapping("/listUserByOpenfgaApi")
    public String listUserByOpenfga() throws FgaInvalidParameterException, ExecutionException, InterruptedException {


        ListByUserRequest listByUserRequest = new ListByUserRequest();
        listByUserRequest.setAuthorization_model_id(openfgaConfigurationProperties.getAuthorizeModelId());
        listByUserRequest.setObject(
                new ListByUserRequest.ObjectRequest("page_module_group", "1891")
        );
        listByUserRequest.setRelation("parent_model");
        listByUserRequest.setUser_filters(List.of(new ListByUserRequest.TypeRequest("application")));


        String url = StrUtil.format(openfgaConfigurationProperties.getApiUrl() + "/stores/{}/list-users", openfgaConfigurationProperties.getStoreId());
        HttpResponse execute = HttpRequest.post(url)
                .body(JSONUtil.toJsonStr(listByUserRequest))
                .execute();
        System.out.println(execute.body());

        return execute.body();
    }
    @GetMapping("/batchCheck")
    public String batchCheck() throws FgaInvalidParameterException, ExecutionException, InterruptedException {


        BatchCheckRequest batchCheckRequest =new BatchCheckRequest();


        batchCheckRequest.setAuthorization_model_id(openfgaConfigurationProperties.getAuthorizeModelId());
        batchCheckRequest.setChecks(
                List.of(
                        new BatchCheckRequest.Check("1",
                                new BatchCheckRequest.TupleKey(
                                        "page_menu:10000",
                                        "parent_model",
                                        "page_module:2709"
                                        )
                                ),
                        new BatchCheckRequest.Check("2",
                                new BatchCheckRequest.TupleKey(
                                        "page_menu:asd",
                                        "parent_model",
                                        "page_view:83011"
                                )
                        )
                )
        );


        String url = StrUtil.format(openfgaConfigurationProperties.getApiUrl() + "/stores/{}/batch-check", openfgaConfigurationProperties.getStoreId());
        HttpResponse execute = HttpRequest.post(url)
                .body(JSONUtil.toJsonStr(batchCheckRequest))
                .execute();
        System.out.println(execute.body());

        return execute.body();
    }
}

15.注意

这里的批量check不好用 SDK  里面会开启线程池 ,而且不关闭 很浪费资源,源码里

我们采用HTTP 调用API

  @GetMapping("/batchCheck")
    public String batchCheck() throws FgaInvalidParameterException, ExecutionException, InterruptedException {


        BatchCheckRequest batchCheckRequest =new BatchCheckRequest();


        batchCheckRequest.setAuthorization_model_id(openfgaConfigurationProperties.getAuthorizeModelId());
        batchCheckRequest.setChecks(
                List.of(
                        new BatchCheckRequest.Check("1",
                                new BatchCheckRequest.TupleKey(
                                        "page_menu:10000",
                                        "parent_model",
                                        "page_module:2709"
                                        )
                                ),
                        new BatchCheckRequest.Check("2",
                                new BatchCheckRequest.TupleKey(
                                        "page_menu:asd",
                                        "parent_model",
                                        "page_view:83011"
                                )
                        )
                )
        );


        String url = StrUtil.format(openfgaConfigurationProperties.getApiUrl() + "/stores/{}/batch-check", openfgaConfigurationProperties.getStoreId());
        HttpResponse execute = HttpRequest.post(url)
                .body(JSONUtil.toJsonStr(batchCheckRequest))
                .execute();
        System.out.println(execute.body());

        return execute.body();
    }

就这个调用api就可以,其他的暂时调用 SDK没有问题

package com.jmj.openfgatest.controller;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.jmj.openfgatest.common.TreePageEntityConst;
import com.jmj.openfgatest.common.TreeUtils;
import com.jmj.openfgatest.entity.BatchCheckRequest;
import com.jmj.openfgatest.entity.ListByUserRequest;
import com.jmj.openfgatest.entity.TreePageEntity;
import com.jmj.openfgatest.entity.TreePageNode;
import com.jmj.openfgatest.openfga.configuration.OpenfgaConfigurationProperties;
import com.jmj.openfgatest.repository.TreePageEntityRepository;
import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.client.model.*;
import dev.openfga.sdk.api.model.FgaObject;
import dev.openfga.sdk.api.model.User;
import dev.openfga.sdk.api.model.UserTypeFilter;
import dev.openfga.sdk.errors.FgaInvalidParameterException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/treePage")
public class TreePageController {


    @Autowired
    private TreePageEntityRepository treePageEntityRepository;
    @Autowired
    private OpenFgaClient openFgaClient;


    @GetMapping("/create")
    @Transactional(rollbackFor = Exception.class)
    public String create() throws FgaInvalidParameterException, ExecutionException, InterruptedException {
        try {
            //创建基本数据
            List<TreePageEntity> list = treePageEntityRepository.findByType(TreePageEntityConst.Type.PAGE_MENU.value);
            ArrayList<TreePageEntity> arrayList = new ArrayList<>();

            for (TreePageEntity t : list) {
                for (int i = 0; i < 1; i++) {
                    TreePageEntity treePageEntity = new TreePageEntity();
                    treePageEntity.setName("模型界面" + i);
                    treePageEntity.setType(TreePageEntityConst.Type.PAGE_VIEW.value);
                    treePageEntity.setParentId(t.getId());
                    arrayList.add(treePageEntity);
                }

            }
            treePageEntityRepository.saveAll(arrayList);

            List<ClientTupleKey> parentModel = arrayList.stream()
                    .map(
                            t -> {
                                return new ClientTupleKey()
                                        .user(TreePageEntityConst.Type.PAGE_MENU.model + ":" + t.getParentId())
                                        .relation("parent_model")
                                        ._object(TreePageEntityConst.Type.PAGE_VIEW.model + ":" + t.getId());
                            }
                    ).collect(Collectors.toList());


            System.out.println(parentModel.size());


            ArrayList<ClientTupleKey> clientTupleKeys = new ArrayList<>();

            for (int i = 0; i < parentModel.size(); i++) {
                clientTupleKeys.add(parentModel.get(i));

                if ((i + 1) % 10 == 0) {
                    System.out.println("=============================================");

                    for (ClientTupleKey clientTupleKey : clientTupleKeys) {
                        System.out.println(StrUtil.format("user:{},relation:{},object:{}",clientTupleKey.getUser(),clientTupleKey.getRelation(),clientTupleKey.getObject()));
                    }

                    CompletableFuture<ClientWriteResponse> write = openFgaClient.write(
                            new ClientWriteRequest()
                                    .writes(clientTupleKeys)
                    );

                    ClientWriteResponse clientWriteResponse = write.get();
                    System.out.println(clientWriteResponse.getRawResponse());

                    clientTupleKeys.clear();
                }

            }



        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
        return "成功";
    }

    @GetMapping("/check")
    @Transactional(rollbackFor = Exception.class)
    public String check() throws FgaInvalidParameterException, ExecutionException, InterruptedException {

        ClientCheckRequest parentModel = new ClientCheckRequest()
                .user(TreePageEntityConst.Type.PAGE_MODULE.model + ":" + "2011")
                .relation("parent_model")
                ._object(TreePageEntityConst.Type.PAGE_MENU.model + ":" + "3011");

        ClientCheckResponse clientCheckResponse = openFgaClient.check(parentModel).get();
        System.out.println(clientCheckResponse);
        return clientCheckResponse.getAllowed() + "";
    }

    @GetMapping("/list")
    public List<TreePageNode> list() throws FgaInvalidParameterException, ExecutionException, InterruptedException {

        List<TreePageEntity> all = treePageEntityRepository.findAll();


        List<TreePageNode> collect = all.stream().map(TreePageNode::of).collect(Collectors.toList());

        System.out.println(all.size());

        return TreeUtils.buildTree(collect);


    }

    @GetMapping("/listObjByOpenfga")
    public List<TreePageEntity> listObjByOpenfga() throws FgaInvalidParameterException, ExecutionException, InterruptedException {

        var body = new ClientListObjectsRequest()
                .user(  "user:" + "jmj")
                .relation("can_access")
                .type(TreePageEntityConst.Type.PAGE_VIEW.model);

        CompletableFuture<ClientListObjectsResponse> clientListObjectsResponseCompletableFuture = openFgaClient.listObjects(body);

        ClientListObjectsResponse clientListObjectsResponse = clientListObjectsResponseCompletableFuture.get();
        System.out.println(clientListObjectsResponse.getObjects());

//
        List<TreePageEntity> byParentId = treePageEntityRepository.findByParentId(2011L);
//        System.out.println(byParentId.stream().map(TreePageEntity::getId).toList());


        return byParentId;
    }

    @GetMapping("/listRelationByOpenfga")
    public List<String> listRelationByOpenfga() throws FgaInvalidParameterException, ExecutionException, InterruptedException {


        ClientListRelationsResponse clientListRelationsResponse = openFgaClient.listRelations(
                new ClientListRelationsRequest()
                        .user(TreePageEntityConst.Type.PAGE_MODULE.model + ":" + "2011")
                        .relations(List.of("parent_model", "viewer", "accesser"))
                        ._object(TreePageEntityConst.Type.PAGE_MENU.model + ":" + 3011)
        ).get();

        System.out.println(clientListRelationsResponse.getRelations());
        return clientListRelationsResponse.getRelations();
    }

    @Autowired
    private OpenfgaConfigurationProperties openfgaConfigurationProperties;

    @GetMapping("/listUserByOpenfgaApi")
    public String listUserByOpenfga() throws FgaInvalidParameterException, ExecutionException, InterruptedException {


        ListByUserRequest listByUserRequest = new ListByUserRequest();
        listByUserRequest.setAuthorization_model_id(openfgaConfigurationProperties.getAuthorizeModelId());
        listByUserRequest.setObject(
                new ListByUserRequest.ObjectRequest("page_module_group", "1011")
        );
        listByUserRequest.setRelation("parent_model");
        listByUserRequest.setUser_filters(List.of(new ListByUserRequest.TypeRequest("application")));


        String url = StrUtil.format(openfgaConfigurationProperties.getApiUrl() + "/stores/{}/list-users", openfgaConfigurationProperties.getStoreId());
        HttpResponse execute = HttpRequest.post(url)
                .body(JSONUtil.toJsonStr(listByUserRequest))
                .execute();
        System.out.println(execute.body());

        return execute.body();
    }

    @GetMapping("/listUserByOpenfgaSDK")
    public List<User> listUserByOpenfgaSDK() throws FgaInvalidParameterException, ExecutionException, InterruptedException {
        var userFilters = new ArrayList<UserTypeFilter>() {
            {
                add(new UserTypeFilter().type("user"));
            }
        };


        CompletableFuture<ClientListUsersResponse> clientListUsersResponseCompletableFuture = openFgaClient.listUsers(
                new ClientListUsersRequest()
                        ._object(new FgaObject().type("page_view").id("93011"))
                        .relation("can_access")
                        .userFilters(userFilters)
        );
        ClientListUsersResponse clientListUsersResponse = clientListUsersResponseCompletableFuture.get();
        List<User> users = clientListUsersResponse.getUsers();
        for (User user : users) {
            System.out.println(user.getUserset());
        }
        System.out.println(users);
        return users;
    }

    @GetMapping("/batchCheck")
    public String batchCheck() throws FgaInvalidParameterException, ExecutionException, InterruptedException {


        BatchCheckRequest batchCheckRequest =new BatchCheckRequest();


        batchCheckRequest.setAuthorization_model_id(openfgaConfigurationProperties.getAuthorizeModelId());
        batchCheckRequest.setChecks(
                List.of(
                        new BatchCheckRequest.Check("1",
                                new BatchCheckRequest.TupleKey(
                                        "page_menu:10000",
                                        "parent_model",
                                        "page_module:2709"
                                        )
                                ),
                        new BatchCheckRequest.Check("2",
                                new BatchCheckRequest.TupleKey(
                                        "page_menu:asd",
                                        "parent_model",
                                        "page_view:83011"
                                )
                        )
                )
        );


        String url = StrUtil.format(openfgaConfigurationProperties.getApiUrl() + "/stores/{}/batch-check", openfgaConfigurationProperties.getStoreId());
        HttpResponse execute = HttpRequest.post(url)
                .body(JSONUtil.toJsonStr(batchCheckRequest))
                .execute();
        System.out.println(execute.body());

        return execute.body();
    }
}

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

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

相关文章

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

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

ArkTS渲染控制

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

UbuntuWindows双系统安装

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

【leetcode详解】T598 区间加法

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

备考蓝桥杯嵌入式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…

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;为构建和训练神经网络提供了高效且灵活…

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 …

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

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

吴恩达深度学习——优化神经网络

本文来自https://www.bilibili.com/video/BV1FT4y1E74V&#xff0c;仅为本人学习所用。 文章目录 优化样本大小mini-batch 优化梯度下降法动量梯度下降法指数加权平均概念偏差纠正 动量梯度下降法 RMSpropAdam优化算法 优化学习率局部最优问题&#xff08;了解&#xff09; 优…

揭秘算法 课程导读

目录 一、老师介绍 二、课程目标 三、课程安排 一、老师介绍 学问小小谢 我是一个热爱分享知识的人&#xff0c;我深信知识的力量能够启迪思考&#xff0c;丰富生活。 欢迎每一位对知识有渴望的朋友&#xff0c;如果你对我的创作感兴趣&#xff0c;或者我们有着共同的兴趣点&…

17.[前端开发]Day17-形变-动画-vertical-align

1 transform CSS属性 - transform transform的用法 表示一个或者多个 不用记住全部的函数&#xff0c;只用掌握这四个常用的函数即可 位移 - translate <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta ht…

Python的那些事第五篇:数据结构的艺术与应用

新月人物传记&#xff1a;人物传记之新月篇-CSDN博客 目录 一、列表&#xff08;List&#xff09;&#xff1a;动态的容器 二、元组&#xff08;Tuple&#xff09;&#xff1a;不可变的序列 三、字典&#xff08;Dict&#xff09;&#xff1a;键值对的集合 四、集合&#xf…