# 访问控制

**URL:** https://heroiclabs.com/docs/zh/nakama/concepts/storage/permissions/
**Summary:** 存储引擎有两个控制对象访问的功能。对象所有权和访问权限。

---


# 访问控制

存储引擎有两个控制对象访问的功能。对象所有权和访问权限。

## 对象所有权

每个存储对象创建时都有一个所有者。所有者可以是创建对象的用户、系统所有者，也可以是使用[代码运行库](../../../server-framework/typescript-runtime/function-reference/#Storage)创建对象时指定的所有者。

从代码运行库写入对象时，除非明确设置，否则将系统用户视为所有者。默认将从客户端写入存储对象的用户设置为所有者。

系统所拥有的对象由系统用户创建，在服务器中以 nil UUID (`00000000-0000-0000-0000-000000000000`) 表示。系统所拥有的对象必须具有公开读取[访问权限](#object-permissions)，客户端才能获取它。

这些代码示例显示如何检索系统所拥有的对象（标记为公开读取）。

{{< code type="client" >}}
```bash
curl -X POST "http://127.0.0.1:7350/v2/storage" \
  -H 'Authorization: Bearer <session token>' \
  -d '{
    "object_ids": [
      {
        "collection": "configuration",
        "key": "config",
      }
    ]
  }'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
const objects = await client.readStorageObjects(session, {
  "object_ids": [{
    "collection": "configurations",
    "key": "config"
  }]
});
console.info("Read objects: %o", objects);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
var result = await client.ReadStorageObjectsAsync(session, new StorageObjectId {
    Collection = "configuration",
    Key = "config"
});

Console.WriteLine("Read objects: [{0}]", string.Join(",\n  ", result.Objects));
```
{{< / code >}}

{{< code type="client" >}}
```cpp
auto successCallback = [](const NStorageObjects& objects)
{
  for (auto& object : objects)
    {
        std::cout << "Object key: " << object.key << ", value: " << object.value << std::endl;
    }
};

std::vector<NReadStorageObjectId> objectIds;
NReadStorageObjectId objectId;
objectId.collection = "configurations";
objectId.key = "config";
objectIds.push_back(objectId);
client->readStorageObjects(session, objectIds, successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```java
StorageObjectId objectId = new StorageObjectId("configuration");
objectId.setKey("config");
StorageObjects objects = client.readStorageObjects(session, objectId).get();
System.out.format("Read objects %s", objects.getObjectsList().toString());
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var result : NakamaAPI.ApiStorageObjects = yield(client.read_storage_objects_async(session, [
    NakamaStorageObjectId.new("configuration", "config")
]), "completed")

if result.is_exception():
    print("An error occurred: %s" % result)
    return

print("Read objects:")

for o in result.objects:
    print("%s" % o)
```
{{< / code >}}

{{< code type="client" >}}
```shell
POST /v2/storage
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "object_ids": [
    {
      "collection": "configuration",
      "key": "config"
    }
  ]
}
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local objects_ids = {
  {
				collection = "configuration",
				key = "config"
  }
}

local result = client.read_storage_objects(objects_ids)

if result.error then
  print(result.message)
  return
end

for _,object in ipairs(result.objects) do
  pprint(object)
end
```
{{< / code >}}

您也可以使用代码运行库获取对象。代码运行库不受访问权限的相关标准规则的限制，因为服务器将其作为权威代码运行。

{{< code type="server" >}}
```lua
local object_ids = {
  { collection = "configuration", key = "config", user_id = nil },
}

local objects = nk.storage_read(object_ids)

for _, o in ipairs(objects) do
  local message = ("value: %q"):format(o.value)
  nk.logger_info(message)
end
```
{{< / code >}}

{{< code type="server" >}}
```go
objectIds := []*runtime.StorageRead{
    &runtime.StorageRead{
        Collection: "configuration",
        Key: "config",
    },
}

objects, err := nk.StorageRead(ctx, objectIds)

if err != nil {
    // Handle error.
} else {
    for _, object := range objects {
        logger.Info("value: %s", object.Value)
    }
}
```
{{< / code >}}

{{< code type="server" >}}
```typescript
let objectIds: nkruntime.StorageReadRequest[] = [
  { collection: 'configuration', key: 'config', userId: '<uuid>' },
]

let objects: nkruntime.StorageObject[] = [];

try {
  let objects = nk.storageRead(objectIds);
} catch (error) {
  // Handle error
}

objects.forEach(function (o) {
  logger.info('value: %q', o.value);
});
```
{{< / code >}}

## 对象权限

对象有权限，在写入或更新对象时，为此对象的所有者执行这些权限。

- **ReadPermission**   可以为 `Public Read` (`2`)、`Owner Read` (`1`) 或 `No Read` (`0`)。
- **WritePermission**   可以为 `Owner Write` (`1`) 或 `No Write` (`0`)。

当通过代码运行库与存储引擎交互时，这些权限将被忽略，因为服务器是权威的，始终可以读取/写入对象。因此，`No Read` / `No Write` 权限意味着没有客户端可以读取/写入此对象。

只有作为所有者的用户才能访问或修改具有 `Owner Read` 和 `Owner Write` 权限的对象。其他客户端不能访问此对象。

`Public Read` 意味着任何用户均可读取该对象。这对于用户需要与其他用户分享其游戏状态或组成部分的游戏玩法很实用。例如，有些具有自己的 `"Army"` 对象的用户想要互相战斗。每个用户都可以写入自己的公开读取对象，其他用户也可以读取该对象，以便在其他用户的设备上呈现该对象。

在从客户端修改对象时，对象的默认权限设置为 `Owner Read` 和 `Owner Write`。
在从运行库修改对象时，对象的默认权限设置为 `No Read` 和 `No Write`。
在列出对象时，将仅返回具有合适权限的对象。

{{< code type="client" >}}
```bash
# "2" refers to Public Read permission
# "1" refers to Owner Write permission
curl -X PUT "http://127.0.0.1:7350/v2/storage" \
  -H 'Authorization: Bearer <session token>' \
  -d '{
    "objects": [
      {
        "collection": "battle",
        "key": "army",
        "value": "{"soldiers": 50}",
        "permission_read": 2,
        "permission_write": 1
      }
    ]
  }'
```
{{< / code >}}

{{< code type="client" >}}
```javascript
var army_setup = { "soldiers": 50 };
// "2" refers to Public Read permission
// "1" refers to Owner Write permission
const object_ids = await client.writeStorageObjects(session, [
  {
    "collection": "saves",
    "key": "savegame",
    "value": army_setup,
    "permission_read": 2,
    "permission_write": 1
  }
]);

console.info("Stored objects: %o", object_ids);
```
{{< / code >}}

{{< code type="client" >}}
```csharp
var armySetup = "{ "soldiers": 50 }";
// "2" refers to Public Read permission
// "1" refers to Owner Write permission
var result = await client.WriteStorageObjectsAsync(session, new WriteStorageObject
{
    Collection = "saves",
    Key = "savegame",
    Value = armySetup,
    PermissionRead = 2,
    PermissionWrite = 1
});

Console.WriteLine("Stored objects: [{0}]", string.Join(",\n  ", result.Objects));
```
{{< / code >}}

{{< code type="client" >}}
```cpp
auto successCallback = [](const NStorageObjectAcks& acks)
{
};

std::vector<NStorageObjectWrite> objects;
NStorageObjectWrite object;
object.collection = "saves";
object.key = "savegame";
object.value = "{ "soldiers": 50 }";
object.permissionRead = NStoragePermissionRead::PUBLIC_READ;   // Public Read permission
object.permissionWrite = NStoragePermissionWrite::OWNER_WRITE; // Owner Write permission
objects.push_back(object);
client->writeStorageObjects(session, objects, successCallback);
```
{{< / code >}}

{{< code type="client" >}}
```java
String armySetup = "{ "soldiers": 50 }";
StorageObjectWrite object = new StorageObjectWrite("saves", "savegame", armySetup, PermissionRead.PUBLIC_READ, PermissionWrite.OWNER_WRITE);
StorageObjectAcks acks = client.writeStorageObjects(session, object).get();
System.out.format("Stored objects %s", acks.getAcksList());
```
{{< / code >}}

{{< code type="client" framework="godot3" >}}
```gdscript
var army_setup = "{ "soldiers": 50 }";
# "2" refers to Public Read permission
# "1" refers to Owner Write permission
var acks : NakamaAPI.ApiStorageObjectAcks = yield(client.write_storage_objects_async(session, [
    NakamaWriteStorageObject.new("saves", "savegame", 2, 1, army_setup, "")
]), "completed")

if acks.is_exception():
    print("An error occurred: %s" % acks)
    return

print("Stored objects: %s" % [acks.acks])
```
{{< / code >}}

{{< code type="client" >}}
```shell
PUT /v2/storage
Host: 127.0.0.1:7350
Accept: application/json
Content-Type: application/json
Authorization: Bearer <session token>
{
  "objects": [
    {
      "collection": "battle",
      "key": "army",
      "value": "{ "soldiers": 50 }",
      "permission_read": 2,
      "permission_write": 1
    }
  ]
}
```
{{< / code >}}

{{< code type="client" framework="defold" >}}
```lua
local army_setup = json.encode({ soldiers = 50 })
local can_read = 2
local can_write = 1

local objects = {
  {
    collection = "battle",
    key = "army",
    permissionRead = can_read,
    permissionWrite = can_write,
    value = army_setup,
    version = version
  }
}

local result = client.write_storage_objects(objects)

if result.error then
  print(result.message)
  return
end

for _,ack in ipairs(result.acks) do
  pprint(ack)
end
```
{{< / code >}}

您可以从代码运行库存储具有自定义权限的对象。

{{< missing type="server" lang="typescript" />}}

{{< code type="server" >}}
```lua
local user_id = "4ec4f126-3f9d-11e7-84ef-b7c182b36521" -- Some user ID.

local new_objects = {
  { collection = "battle", key = "army", user_id = user_id, value = {}, permission_read = 2, permission_write = 1 }
}

nk.storage_write(new_objects)
```
{{< / code >}}

{{< code type="server" >}}
```go
userID := "4ec4f126-3f9d-11e7-84ef-b7c182b36521" // Some user ID.
objects := []*runtime.StorageWrite{
    &runtime.StorageWrite{
        Collection:      "battle",
        Key:             "army",
        UserID:          userID,
        Value:           "{}",
        PermissionRead:  2,
        PermissionWrite: 1,
    },
}

if _, err := nk.StorageWrite(ctx, objects); err != nil {
    // Handle error.
}
```
{{< / code >}}
