Buat token

Panduan ini menjelaskan cara membuat token, serta kolom wajib diisi dan opsional untuk token.

Untuk membuat token, tulis string yang akan ditandatangani, yang kami sebut sebagai nilai bertanda tangan dalam panduan ini. Nilai yang ditandatangani mencakup parameter yang mendeskripsikan konten yang Anda lindungi, waktu habis masa berlaku nilai yang ditandatangani, dan seterusnya.

Anda menggunakan nilai yang ditandatangani saat membuat string token. Anda membuat string token dengan menyusun parameter untuk token, seperti kode autentikasi pesan berbasis hash (HMAC) simetris dari nilai yang ditandatangani.

Media CDN menggunakan token akhir yang telah disusun untuk membantu melindungi konten Anda.

Buat token

  1. Buat nilai yang ditandatangani dengan menggabungkan string yang berisi kolom token wajib dan kolom token opsional yang diinginkan. Pisahkan setiap kolom dan parameter apa pun dengan karakter ~ tanda gelombang.

  2. Tanda tangani nilai yang ditandatangani dengan tanda tangan Ed25519 atau HMAC kunci simetris.

  3. Tulis token dengan menggabungkan string yang berisi kolom token yang diperlukan dan kolom token opsional. Pisahkan setiap kolom dan parameter apa pun dengan karakter ~ tanda gelombang.

    Saat menulis token, nilai untuk setiap parameter adalah sama antara nilai yang ditandatangani dan string token, dengan pengecualian berikut:

    • FullPath
    • Headers

Contoh kode berikut menunjukkan cara membuat token secara terprogram:

Python

Untuk mengautentikasi ke Media CDN, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

import base64
import datetime
import hashlib
import hmac

import cryptography.hazmat.primitives.asymmetric.ed25519 as ed25519


def base64_encoder(value: bytes) -> str:
    """
    Returns a base64-encoded string compatible with Media CDN.

    Media CDN uses URL-safe base64 encoding and strips off the padding at the
    end.
    """
    encoded_bytes = base64.urlsafe_b64encode(value)
    encoded_str = encoded_bytes.decode("utf-8")
    return encoded_str.rstrip("=")


def sign_token(
    base64_key: bytes,
    signature_algorithm: str,
    start_time: datetime.datetime = None,
    expiration_time: datetime.datetime = None,
    url_prefix: str = None,
    full_path: str = None,
    path_globs: str = None,
    session_id: str = None,
    data: str = None,
    headers: str = None,
    ip_ranges: str = None,
) -> str:
    """Gets the Signed URL Suffix string for the Media CDN' Short token URL requests.
    One of (`url_prefix`, `full_path`, `path_globs`) must be included in each input.
    Args:
        base64_key: Secret key as a base64 encoded string.
        signature_algorithm: Algorithm can be either `SHA1` or `SHA256` or `Ed25519`.
        start_time: Start time as a UTC datetime object.
        expiration_time: Expiration time as a UTC datetime object. If None, an expiration time 1 hour from now will be used.
        url_prefix: the URL prefix to sign, including protocol.
                    For example: http://example.com/path/ for URLs under /path or http://example.com/path?param=1
        full_path:  A full path to sign, starting with the first '/'.
                    For example: /path/to/content.mp4
        path_globs: a set of ','- or '!'-delimited path glob strings.
                    For example: /tv/*!/film/* to sign paths starting with /tv/ or /film/ in any URL.
        session_id: a unique identifier for the session
        data: data payload to include in the token
        headers: header name and value to include in the signed token in name=value format.  May be specified more than once.
                    For example: [{'name': 'foo', 'value': 'bar'}, {'name': 'baz', 'value': 'qux'}]
        ip_ranges: A list of comma separated ip ranges. Both IPv4 and IPv6 ranges are acceptable.
                    For example: "203.0.113.0/24,2001:db8:4a7f:a732/64"

    Returns:
        The Signed URL appended with the query parameters based on the
        specified URL prefix and configuration.
    """

    decoded_key = base64.urlsafe_b64decode(base64_key)
    algo = signature_algorithm.lower()

    # For most fields, the value we put in the token and the value we must sign
    # are the same.  The FullPath and Headers use a different string for the
    # value to be signed compared to the token.  To illustrate this difference,
    # we'll keep the token and the value to be signed separate.
    tokens = []
    to_sign = []

    # check for `full_path` or `path_globs` or `url_prefix`
    if full_path:
        tokens.append("FullPath")
        to_sign.append(f"FullPath={full_path}")
    elif path_globs:
        path_globs = path_globs.strip()
        field = f"PathGlobs={path_globs}"
        tokens.append(field)
        to_sign.append(field)
    elif url_prefix:
        field = "URLPrefix=" + base64_encoder(url_prefix.encode("utf-8"))
        tokens.append(field)
        to_sign.append(field)
    else:
        raise ValueError(
            "User Input Missing: One of `url_prefix`, `full_path` or `path_globs` must be specified"
        )

    # check & parse optional params
    if start_time:
        epoch_duration = start_time.astimezone(
            tz=datetime.timezone.utc
        ) - datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc)
        field = f"Starts={int(epoch_duration.total_seconds())}"
        tokens.append(field)
        to_sign.append(field)

    if not expiration_time:
        expiration_time = datetime.datetime.now() + datetime.timedelta(hours=1)
        epoch_duration = expiration_time.astimezone(
            tz=datetime.timezone.utc
        ) - datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc)
    else:
        epoch_duration = expiration_time.astimezone(
            tz=datetime.timezone.utc
        ) - datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc)
    field = f"Expires={int(epoch_duration.total_seconds())}"
    tokens.append(field)
    to_sign.append(field)

    if session_id:
        field = f"SessionID={session_id}"
        tokens.append(field)
        to_sign.append(field)

    if data:
        field = f"Data={data}"
        tokens.append(field)
        to_sign.append(field)

    if headers:
        header_names = []
        header_pairs = []
        for each in headers:
            header_names.append(each["name"])
            header_pairs.append("%s=%s" % (each["name"], each["value"]))
        tokens.append(f"Headers={','.join(header_names)}")
        to_sign.append(f"Headers={','.join(header_pairs)}")

    if ip_ranges:
        field = f"IPRanges={base64_encoder(ip_ranges.encode('ascii'))}"
        tokens.append(field)
        to_sign.append(field)

    # generating token
    to_sign = "~".join(to_sign)
    to_sign_bytes = to_sign.encode("utf-8")
    if algo == "ed25519":
        digest = ed25519.Ed25519PrivateKey.from_private_bytes(decoded_key).sign(
            to_sign_bytes
        )
        tokens.append("Signature=" + base64_encoder(digest))
    elif algo == "sha256":
        signature = hmac.new(
            decoded_key, to_sign_bytes, digestmod=hashlib.sha256
        ).hexdigest()
        tokens.append("hmac=" + signature)
    elif algo == "sha1":
        signature = hmac.new(
            decoded_key, to_sign_bytes, digestmod=hashlib.sha1
        ).hexdigest()
        tokens.append("hmac=" + signature)
    else:
        raise ValueError(
            "Input Missing Error: `signature_algorithm` can only be one of `sha1`, `sha256` or `ed25519`"
        )
    return "~".join(tokens)

Java

Untuk mengautentikasi ke Media CDN, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, lihat Menyiapkan autentikasi untuk lingkungan pengembangan lokal.


import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
import org.bouncycastle.crypto.signers.Ed25519Signer;
import org.bouncycastle.util.encoders.Hex;

public class DualToken {

  public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
    // TODO(developer): Replace these variables before running the sample.
    // Secret key as a base64 encoded string.
    byte[] base64Key = new byte[]{};
    // Algorithm can be one of these: SHA1, SHA256, or Ed25519.
    String signatureAlgorithm = "ed25519";
    // (Optional) Start time as a UTC datetime object.
    DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
    Optional<Instant> startTime = Optional.empty();
    // Expiration time as a UTC datetime object.
    // If None, an expiration time that's an hour after the current time is used.
    Instant expiresTime = Instant.from(formatter.parse("2022-09-13T12:00:00Z"));

    // ONE OF (`urlPrefix`, `fullPath`, `pathGlobs`) must be included in each input.
    // The URL prefix and protocol to sign.
    // For example: http://example.com/path/ for URLs under /path or http://example.com/path?param=1
    Optional<String> urlPrefix = Optional.empty();
    // A full path to sign, starting with the first '/'.
    // For example: /path/to/content.mp4
    Optional<String> fullPath = Optional.of("http://10.20.30.40/");
    // A set of path glob strings delimited by ',' or '!'.
    // For example: /tv/*!/film/* to sign paths starting with /tv/ or /film/ in any URL.
    Optional<String> pathGlobs = Optional.empty();

    // (Optional) A unique identifier for the session.
    Optional<String> sessionId = Optional.empty();
    // (Optional) Data payload to include in the token.
    Optional<String> data = Optional.empty();
    // (Optional) Header name and value to include in the signed token in name=value format.
    // May be specified more than once.
    // For example: [{'name': 'foo', 'value': 'bar'}, {'name': 'baz', 'value': 'qux'}]
    Optional<List<Header>> headers = Optional.empty();
    // (Optional) A list of comma-separated IP ranges. Both IPv4 and IPv6 ranges are acceptable.
    // For example: "203.0.113.0/24,2001:db8:4a7f:a732/64"
    Optional<String> ipRanges = Optional.empty();

    DualToken.signToken(
        base64Key,
        signatureAlgorithm,
        startTime,
        expiresTime,
        urlPrefix,
        fullPath,
        pathGlobs,
        sessionId,
        data,
        headers,
        ipRanges);
  }

  // Gets the signed URL suffix string for the Media CDN short token URL requests.
  // Result:
  //     The signed URL appended with the query parameters based on the
  // specified URL prefix and configuration.
  public static void signToken(
      byte[] base64Key, String signatureAlgorithm, Optional<Instant> startTime,
      Instant expirationTime, Optional<String> urlPrefix, Optional<String> fullPath,
      Optional<String> pathGlobs, Optional<String> sessionId, Optional<String> data,
      Optional<List<Header>> headers, Optional<String> ipRanges)
      throws NoSuchAlgorithmException, InvalidKeyException {

    String field = "";
    byte[] decodedKey = Base64.getUrlDecoder().decode(base64Key);

    // For most fields, the value in the token and the value to sign
    // are the same. Compared to the token, the FullPath and Headers
    // use a different string for the value to sign. To illustrate this difference,
    // we'll keep the token and the value to be signed separate.
    List<String> tokens = new ArrayList<>();
    List<String> toSign = new ArrayList<>();

    // Check for `fullPath` or `pathGlobs` or `urlPrefix`.
    if (fullPath.isPresent()) {
      tokens.add("FullPath");
      toSign.add(String.format("FullPath=%s", fullPath.get()));
    } else if (pathGlobs.isPresent()) {
      field = String.format("PathGlobs=%s", pathGlobs.get().trim());
      tokens.add(field);
      toSign.add(field);
    } else if (urlPrefix.isPresent()) {
      field = String.format("URLPrefix=%s",
          base64Encoder(urlPrefix.get().getBytes(StandardCharsets.UTF_8)));
      tokens.add(field);
      toSign.add(field);
    } else {
      throw new IllegalArgumentException(
          "User Input Missing: One of `urlPrefix`, `fullPath` or `pathGlobs` must be specified");
    }

    // Check & parse optional params.
    long epochDuration;
    if (startTime.isPresent()) {
      epochDuration = ChronoUnit.SECONDS.between(Instant.EPOCH, startTime.get());
      field = String.format("Starts=%s", epochDuration);
      tokens.add(field);
      toSign.add(field);
    }

    if (expirationTime == null) {
      expirationTime = Instant.now().plus(1, ChronoUnit.HOURS);
    }
    epochDuration = ChronoUnit.SECONDS.between(Instant.EPOCH, expirationTime);
    field = String.format("Expires=%s", epochDuration);
    tokens.add(field);
    toSign.add(field);

    if (sessionId.isPresent()) {
      field = String.format("SessionID=%s", sessionId.get());
      tokens.add(field);
      toSign.add(field);
    }

    if (data.isPresent()) {
      field = String.format("Data=%s", data.get());
      tokens.add(field);
      toSign.add(field);
    }

    if (headers.isPresent()) {
      List<String> headerNames = new ArrayList<>();
      List<String> headerPairs = new ArrayList<>();

      for (Header entry : headers.get()) {
        headerNames.add(entry.getName());
        headerPairs.add(String.format("%s=%s", entry.getName(), entry.getValue()));
      }
      tokens.add(String.format("Headers=%s", String.join(",", headerNames)));
      toSign.add(String.format("Headers=%s", String.join(",", headerPairs)));
    }

    if (ipRanges.isPresent()) {
      field = String.format("IPRanges=%s",
          base64Encoder(ipRanges.get().getBytes(StandardCharsets.US_ASCII)));
      tokens.add(field);
      toSign.add(field);
    }

    // Generate token.
    String toSignJoined = String.join("~", toSign);
    byte[] toSignBytes = toSignJoined.getBytes(StandardCharsets.UTF_8);
    String algorithm = signatureAlgorithm.toLowerCase();

    if (algorithm.equalsIgnoreCase("ed25519")) {
      Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(decodedKey, 0);
      Ed25519Signer signer = new Ed25519Signer();
      signer.init(true, privateKey);
      signer.update(toSignBytes, 0, toSignBytes.length);
      byte[] signature = signer.generateSignature();
      tokens.add(String.format("Signature=%s", base64Encoder(signature)));
    } else if (algorithm.equalsIgnoreCase("sha256")) {
      String sha256 = "HmacSHA256";
      Mac mac = Mac.getInstance(sha256);
      SecretKeySpec secretKeySpec = new SecretKeySpec(decodedKey, sha256);
      mac.init(secretKeySpec);
      byte[] signature = mac.doFinal(toSignBytes);
      tokens.add(String.format("hmac=%s", Hex.toHexString(signature)));
    } else if (algorithm.equalsIgnoreCase("sha1")) {
      String sha1 = "HmacSHA1";
      Mac mac = Mac.getInstance(sha1);
      SecretKeySpec secretKeySpec = new SecretKeySpec(decodedKey, sha1);
      mac.init(secretKeySpec);
      byte[] signature = mac.doFinal(toSignBytes);
      tokens.add(String.format("hmac=%s", Hex.toHexString(signature)));
    } else {
      throw new Error(
          "Input Missing Error: `signatureAlgorithm` can only be one of `sha1`, `sha256` or "
              + "`ed25519`");
    }
    // The signed URL appended with the query parameters based on the
    // specified URL prefix and configuration.
    System.out.println(String.join("~", tokens));
  }

  // Returns a base64-encoded string compatible with Media CDN.
  // Media CDN uses URL-safe base64 encoding and strips off the padding at the
  // end.
  public static String base64Encoder(byte[] value) {
    byte[] encodedBytes = Base64.getUrlEncoder().withoutPadding().encode(value);
    return new String(encodedBytes, StandardCharsets.UTF_8);
  }

  public static class Header {

    private String name;
    private String value;

    public Header(String name, String value) {
      this.name = name;
      this.value = value;
    }

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }

    public String getValue() {
      return value;
    }

    public void setValue(String value) {
      this.value = value;
    }

    @Override
    public String toString() {
      return "Header{"
          + "name='" + name + '\''
          + ", value='" + value + '\''
          + '}';
    }
  }

}

Bagian berikut menjelaskan kolom yang digunakan oleh token.

Kolom token yang wajib diisi

Kolom berikut wajib diisi untuk setiap token:

  • Expires
  • Salah satu dari opsi berikut:
    • PathGlobs
    • URLPrefix
    • FullPath
  • Salah satu dari hal berikut:
    • Signature
    • hmac

Kecuali jika ditentukan lain, nama parameter dan nilainya peka huruf besar/kecil.

Tabel berikut menjelaskan setiap parameter:

Nama kolom / alias Parameter token Nilai yang ditandatangani

Expires

exp

Bilangan bulat detik yang telah berlalu sejak epoch Unix (1970-01-01T00:00:00Z) Expires=EXPIRATION_TIME, setelah itu token tidak lagi valid.

PathGlobs

paths, acl

Daftar hingga lima segmen jalur yang dapat diakses. Segmen dapat dipisahkan dengan koma (,) atau tanda seru (!), tetapi tidak keduanya.

PathGlobs mendukung penggunaan karakter pengganti di jalur menggunakan tanda bintang (*) dan tanda tanya (?). Karakter tanda bintang tunggal (*) menjangkau sejumlah segmen jalur, tidak seperti sintaksis pencocokan pola untuk pathMatchTemplate.

Parameter jalur, yang ditandai menggunakan titik koma (;), tidak diizinkan karena akan menimbulkan ambiguitas saat pencocokan.

Karena alasan ini, pastikan URL Anda tidak berisi karakter khusus berikut: ,!*?;

PathGlobs=PATHS
URLPrefix

URL berenkode base64 yang aman bagi web termasuk protokol http:// atau https:// hingga titik pilihan Anda.

Misalnya, beberapa nilai URLPrefix yang valid untuk `https://example.com/foo/bar.ts` adalah `https://example.com`, `https://example.com/foo`, dan `https://example.com/foo/bar`.

URLPrefix=BASE_64_URL_PREFIX
FullPath Tidak ada. Saat menentukan FullPath dalam token, jangan menduplikasi jalur yang Anda tentukan dalam nilai yang ditandatangani. Dalam token, sertakan nama kolom tanpa =. FullPath=FULL_PATH_TO_OBJECT
Signature Versi tanda tangan yang dienkode dengan base64 yang aman bagi web. Tidak berlaku
hmac Versi nilai HMAC berenkode base64 yang aman bagi web. Tidak berlaku

Sintaksis karakter pengganti PathGlobs

Tabel berikut menjelaskan sintaksis karakter pengganti PathGlobs.

Operator Mencocokkan dengan Contoh
* (tanda bintang) Mencocokkan nol atau beberapa karakter di jalur URL, termasuk karakter garis miring (/).
  • /videos/* cocok dengan jalur apa pun yang diawali dengan /videos/.
  • /videos/s*/4k/* cocok dengan /videos/s/4k/ dan /videos/s01/4k/main.m3u8.
  • /manifests/*/4k/* cocok dengan /manifests/s01/4k/main.m3u8 dan /manifests/s01/e01/4k/main.m3u8. Nama ini tidak cocok dengan /manifests/4k/main.m3u8.
? (tanda tanya) Mencocokkan satu karakter di jalur URL, tidak termasuk karakter garis miring (/). /videos/s?main.m3u8 cocok dengan /videos/s1main.m3u8. Parameter ini tidak cocok dengan /videos/s01main.m3u8 atau /videos/s/main.m3u8.

Glob harus diawali dengan tanda bintang (*) atau garis miring (/) untuk jalur URL.

Karena * dan /* cocok dengan semua jalur URL, sebaiknya jangan gunakan keduanya dalam token yang ditandatangani. Untuk perlindungan maksimal, pastikan bahwa glob Anda cocok dengan konten yang ingin Anda berikan aksesnya.

Kolom token opsional

Kecuali jika ditentukan lain, nama parameter dan nilainya peka huruf besar/kecil.

Tabel berikut menjelaskan nama parameter, alias, dan detail untuk parameter opsional:

Nama kolom / alias Parameter Nilai yang ditandatangani

Starts

st

Bilangan bulat detik sejak Unix epoch (1970-01-01T00:00:00Z) Starts=START_TIME
IPRanges

Daftar hingga lima alamat IPv4 dan IPv6 dalam format CIDR yang URL ini valid dalam format base64 yang aman bagi web. Misalnya, untuk menentukan rentang IP "192.6.13.13/32,193.5.64.135/32", Anda menentukan IPRanges=MTkyLjYuMTMuMTMvMzIsMTkzLjUuNjQuMTM1LzMy.

IPRange mungkin tidak berguna untuk disertakan dalam token ketika klien berisiko mengalami migrasi WAN, atau saat jalur jaringan ke frontend aplikasi Anda berbeda dengan jalur pengiriman. Media CDN menolak klien yang memiliki kode HTTP 403 ketika terhubung dengan alamat IP yang bukan bagian dari permintaan yang ditandatangani.

Berikut adalah kasus yang dapat menyebabkan Media CDN menolak klien dengan kode HTTP 403:

  • Lingkungan dual-stack (IPv4, IPv6)
  • Migrasi koneksi (Wi-Fi ke seluler, dan seluler ke Wi-Fi)
  • Jaringan seluler yang menggunakan Carrier Gateway NAT (CGNAT atau CGN)
  • TCP Multijalur (MPTCP)

Semua faktor ini dapat berkontribusi agar klien tertentu memiliki alamat IP nondeterministik selama sesi pemutaran video. Jika alamat IP klien berubah setelah Anda memberikan akses, dan klien mencoba mendownload segmen video ke buffer pemutaran, mereka akan menerima HTTP 403 dari Media CDN.

IPRanges=BASE_64_IP_RANGES

SessionID

id

String arbitrer, berguna untuk analisis log atau pelacakan pemutaran.

Untuk menghindari pembuatan token yang tidak valid, gunakan string berenkode base64 yang dienkode dengan % atau aman bagi web. Karakter berikut tidak boleh digunakan untuk SessionID, karena menyebabkan token menjadi tidak valid: "~", "&", atau " " (spasi).

SessionID=SESSION_ID_VALUE

Data

data, payload

String arbitrer, berguna untuk analisis log.

Untuk menghindari pembuatan token yang tidak valid, gunakan string berenkode base64 yang dienkode dengan % atau aman bagi web. Karakter berikut tidak boleh digunakan untuk Data, karena menyebabkan token menjadi tidak valid: "~", "&", atau " " (spasi).

data=DATA_VALUE
Headers Daftar nama kolom header yang dipisahkan koma. Nama header tidak peka huruf besar/kecil untuk pencarian dalam permintaan. Nama header dalam nilai yang ditandatangani peka huruf besar/kecil. Jika header tidak ada, nilainya adalah string kosong. Jika ada beberapa salinan header, header tersebut akan digabungkan dengan koma. Headers=HEADER_1_NAME=HEADER_1_EXPECTED_VALUE, HEADER_2_NAME=HEADER_2_EXPECTED_VALUE

Contoh

Bagian berikut menunjukkan contoh untuk membuat token.

Contoh menggunakan FullPath

Perhatikan contoh berikut menggunakan kolom FullPath:

  • Item yang diminta: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Waktu kedaluwarsa: 160000000

Nilai yang ditandatangani adalah:

Expires=160000000~FullPath=/tv/my-show/s01/e01/playlist.m3u8

Untuk membuat token, tanda tangani nilai yang ditandatangani dengan tanda tangan Ed25519 atau HMAC kunci simetris.

Berikut adalah contoh token yang dibuat dari nilai yang ditandatangani:

Tanda tangan Ed25519

Expires=160000000~FullPath~Signature=SIGNATURE_OF_SIGNED_VALUE

Dengan SIGNATURE_OF_SIGNED_VALUE adalah tanda tangan ED25519 dari nilai bertanda tangan yang dibuat sebelumnya.

HMAC kunci simetris

Expires=160000000~FullPath~hmac=HMAC_OF_SIGNED_VALUE

Dengan HMAC_OF_SIGNED_VALUE adalah HMAC kunci simetris dari nilai bertanda yang dibuat sebelumnya.

Pada contoh sebelumnya, FullPath disediakan dalam token, tetapi nilainya tidak diulang dari jalur yang ditentukan dalam nilai yang ditandatangani. Dengan begitu, Anda dapat menandatangani jalur lengkap permintaan tanpa menduplikasi permintaan dalam token.

Contoh menggunakan URLPrefix

Perhatikan contoh berikut menggunakan kolom URLPrefix:

  • Item yang diminta: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Waktu kedaluwarsa: 160000000

Nilai yang ditandatangani adalah:

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4

Dalam contoh sebelumnya, kita mengganti jalur ke item yang diminta, http://example.com/tv/my-show/s01/e01/playlist.m3u8 dengan jalur ke item tersebut dalam format Base64 yang aman bagi web, aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4.

Untuk membuat token, tanda tangani nilai yang ditandatangani dengan tanda tangan Ed25519 atau HMAC kunci simetris.

Berikut adalah contoh token yang dibuat dari nilai yang ditandatangani:

Tanda tangan Ed25519

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4~Signature=SIGNATURE_OF_SIGNED_VALUE

Dengan SIGNATURE_OF_SIGNED_VALUE adalah tanda tangan ED25519 dari nilai bertanda tangan yang dibuat sebelumnya.

HMAC kunci simetris

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4~hmac=HMAC_OF_SIGNED_VALUE

Dengan HMAC_OF_SIGNED_VALUE adalah HMAC kunci simetris dari nilai bertanda yang dibuat sebelumnya.

Contoh menggunakan Headers

Perhatikan contoh berikut menggunakan kolom Headers:

  • Item yang diminta: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Waktu kedaluwarsa: 160000000
  • Nilai PathGlobs: *
  • Header permintaan yang diharapkan:
    • user-agent: browser
    • accept: text/html

Nilai yang ditandatangani adalah:

Expires=160000000~PathGlobs=*~Headers=user-agent=browser,accept=text/html

Untuk membuat token, tanda tangani nilai yang ditandatangani dengan tanda tangan Ed25519 atau HMAC kunci simetris.

Berikut adalah contoh token yang dibuat dari nilai yang ditandatangani:

Tanda tangan Ed25519

Expires=160000000~PathGlobs=*~Headers=user-agent,accept~Signature=SIGNATURE_OF_SIGNED_VALUE

Dengan SIGNATURE_OF_SIGNED_VALUE adalah tanda tangan ED25519 dari nilai bertanda tangan yang dibuat sebelumnya.

HMAC kunci simetris

Expires=160000000~PathGlobs=*~Headers=user-agent,accept~hmac=HMAC_OF_SIGNED_VALUE

Dengan HMAC_OF_SIGNED_VALUE adalah HMAC kunci simetris dari nilai bertanda yang dibuat sebelumnya.

Pada contoh sebelumnya, Headers=user-agent,accept disediakan dalam token, tetapi nilai header yang diharapkan tidak diulang dari nilai yang ditandatangani. Dengan begitu, Anda dapat menandatangani key-value pair header permintaan tertentu tanpa menduplikasi nilai dalam token.