Buat token

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

Untuk membuat token, tulis string untuk ditandatangani, yang kami sebut sebagai ditandatangani nilai dalam panduan ini. Nilai yang ditandatangani mencakup parameter yang mendeskripsikan konten yang dilindungi, waktu habis masa berlaku nilai yang ditandatangani, dan dan seterusnya.

Anda menggunakan nilai yang ditandatangani saat membuat string token. Anda membuat string token dengan menyusun parameter untuk token, seperti kode otentikasi pesan berbasis {i>hash <i} simetris (HMAC) yang ditandatangani dengan sejumlah nilai.

Media CDN menggunakan token akhir yang telah disusun untuk membantu melindungi saat ini.

Buat token

  1. Buat nilai yang ditandatangani dengan menyambungkan string yang berisi kolom token wajib dan token opsional yang diinginkan kolom. Pisahkan setiap bidang dan parameter dengan karakter ~ gelombang.

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

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

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

    • FullPath
    • Headers

Contoh kode berikut menunjukkan cara membuat token secara terprogram:

Python

Untuk mengautentikasi ke Media CDN, siapkan Kredensial Default Aplikasi. Untuk 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 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 ini sudah tidak 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 (?). Satu karakter tanda bintang (*) menjangkau sejumlah segmen jalur, tidak seperti sintaksis pencocokan pola untuk pathMatchTemplate.

Parameter jalur, dilambangkan menggunakan titik koma (;), adalah tidak diperbolehkan karena mereka menciptakan ambiguitas saat melakukan {i>matching<i}.

Oleh karena itu, pastikan URL Anda tidak berisi hal-hal berikut karakter khusus: ,!*?;

PathGlobs=PATHS
URLPrefix

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

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 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 karakter atau lebih di jalur URL, termasuk karakter garis miring (/).
  • /videos/* cocok dengan jalur apa pun yang dimulai dengan /videos/.
  • /videos/s*/4k/* cocok dengan /videos/s/4k/ dan /videos/s01/4k/main.m3u8.
  • /manifests/*/4k/* kecocokan /manifests/s01/4k/main.m3u8 dan /manifests/s01/e01/4k/main.m3u8. Tidak cocok /manifests/4k/main.m3u8.
? (tanda tanya) Cocok dengan satu karakter di jalur URL, tidak termasuk garis miring (/) karakter. /videos/s?main.m3u8 kecocokan /videos/s1main.m3u8. Data tersebut tidak sesuai dengan salah satu /videos/s01main.m3u8 atau /videos/s/main.m3u8.

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

Sebaiknya jangan gunakan karena * dan /* cocok dengan semua jalur URL menggunakan keduanya di token yang ditandatangani. Untuk perlindungan maksimal, pastikan bahwa globs 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 yang berisi hingga lima alamat IPv4 dan IPv6 dalam format CIDR untuk yang 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 migrasi WAN atau kasus di mana jalur jaringan ke frontend aplikasi berbeda dengan jalur pengiriman. Media CDN menolak klien dengan HTTP 403 kode ketika mereka 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 kepada klien tertentu yang memiliki alamat IP nondeterministik selama sesi pemutaran video. Jika alamat IP klien berubah setelah Anda mengeluarkan akses, dan mencoba mendownload segmen video ke dalam pemutaran mereka buffer, mereka menerima HTTP 403 dari dan Media CDN.

IPRanges=BASE_64_IP_RANGES

SessionID

id

String arbitrer, berguna untuk analisis log atau pemutaran penelusuran sumber daya.

Untuk menghindari pembuatan token yang tidak valid, gunakan %-enkode atau aman untuk web string berenkode base64. 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 %-enkode atau aman untuk web string berenkode base64. 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 {i>header<i} adalah tidak peka huruf besar/kecil untuk pencarian dalam permintaan. Nama header dalam peka huruf besar/kecil. Jika header tidak ada, nilainya adalah kosong {i>string<i}. Jika ada beberapa salinan {i>header<i}, semua itu yang 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 yang ditandatangani, yang telah dibuat sebelumnya.

HMAC kunci simetris

Expires=160000000~FullPath~hmac=HMAC_OF_SIGNED_VALUE

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

Pada contoh sebelumnya, FullPath diberikan dalam token, tetapi nilainya tidak diulang dari jalur yang ditentukan dalam nilai yang ditandatangani. Hal ini memungkinkan Anda untuk 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 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 yang ditandatangani, yang telah 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 sebelumnya yang telah dibuat.

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 yang ditandatangani, yang telah 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 sebelumnya yang telah dibuat.

Pada contoh sebelumnya, Headers=user-agent,accept diberikan dalam token, tetapi nilai header yang diharapkan tidak diulang dari nilai yang ditandatangani. Hal ini memungkinkan Anda menandatangani key-value pair header permintaan tertentu tanpa menduplikasi nilai di token.