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

Openfga 授权模型搭建

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

先创建一个 config.yaml文件

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

怎么配置?

根据官网来看

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

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

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

命令行

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

环境变量

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

官方也讲了怎么配置

 config.yaml文件

需要映射数据卷

这里我们选择使用yaml配置

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

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

maxChecksPerBatchCheck: 3000

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

maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100

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

maxChecksPerBatchCheck: 3000
maxConcurrentChecksPerBatchCheck: 100
listObjectsDeadline: 10s

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

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

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

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

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

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

 7.最大元组一次写入

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

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

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

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

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

常用的配置就这么多

安装

docker-compose.yaml

version: '3.8'

networks:
  openfga:

services:
  mysql:
    image: mysql: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.kler.cn/a/523626.html

相关文章:

  • WordPress event-monster插件存在信息泄露漏洞(CVE-2024-11396)
  • Excel - Binary和Text两种Compare方法
  • mamba论文学习
  • 【C++】特殊类设计、单例模式与类型转换
  • 十年筑梦,再创鲸彩!庆祝和鲸科技十周年
  • JavaScript系列(47)--音频处理系统详解
  • StarRocks BE源码编译、CLion高亮跳转方法
  • http3网站的设置(AI不会配,得人工配)
  • DeepSeek大模型技术解析:从架构到应用的全面探索
  • CNC研究笔记:
  • 【javaweb项目idea版】蛋糕商城(可复用成其他商城项目)
  • 深入理解 Python 中的 `__all__`:控制模块的公共接口
  • Linux 时间同步
  • 北京大学与智元机器人联合实验室发布OmniManip:显著提升机器人3D操作能力
  • 在msf上使用Mimikatz
  • 新型智慧城市解决方案-3
  • [Linux]el8安全配置faillock:登录失败达阈值自动锁定账户配置
  • 网易云音乐歌名可视化:词云生成与GitHub-Pages部署实践
  • typescript 简介
  • 知识库建设对提升团队协作与创新能力的影响分析
  • P1030 [NOIP2001 普及组] 求先序排列(c++)详解
  • [Spring] Gateway详解
  • 【TCP 协议】确认应答机制 超时重传 三次握手 四次挥手
  • 【方法论】ChatGPT与DeepSeek的联合应用,提升工作效率的新解决方案
  • (15)基于状态方程的单相自耦变压器建模仿真
  • 新站如何快速获得搜索引擎收录?