@ExtensionPointMethod
    @Override
    public Mono<Void> shareMessageAttachmentWithUser(
            Long requesterId,
            @Nullable Long messageAttachmentIdNum,
            @Nullable String messageAttachmentIdStr,
            Long userId) {
        if (messageAttachmentIdNum == null) {
            return Mono.error(ResponseException.get(ResponseStatusCode.ILLEGAL_ARGUMENT,
                    "The message ID must not be null"));
        }
        return messageAttachmentRepository.isUploader(messageAttachmentIdNum, requesterId)
                .flatMap(isUploader -> {
                    if (!isUploader) {
                        return Mono.error(ResponseException
                                .get(ResponseStatusCode.NOT_UPLOADER_TO_SHARE_MESSAGE_ATTACHMENT));
                    }
                    return messageAttachmentRepository
                            .addSharedWithUser(messageAttachmentIdNum, userId)
                            .then();
                });
    }
@implNote 1. We use HMAC(key, message) instead of a HASH(key + message) to avoid the length
          extension attack. To put simply, if a hacker knows the signature of the resource
          "1", and he can also know the signature of resource "12", "13", "123", and so on
          without knowledge of the key.
          <p>
          2. Use MD5 because its output size (128 bits) is small, and it is a 22-character
          Base62-encoded string.
turms-plugins/turms-plugin-minio/src/main/java/im/turms/plugin/minio/MinioStorageServiceProvider.java