Membuat token

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

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

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

Media CDN menggunakan token gabungan akhir 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 tilde ~.

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

  3. Buat token dengan menggabungkan string yang berisi kolom token wajib dan kolom token opsional. Pisahkan setiap kolom dan parameter apa pun dengan karakter tilde ~.

    Saat menyusun token, nilai untuk setiap parameter 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 berikut ini:
    • 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

Detik bilangan bulat 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 akan diberi akses. Segmen dapat dipisahkan dengan koma (,) atau tanda seru (!), tetapi tidak keduanya.

PathGlobs mendukung karakter pengganti di jalur dengan menggunakan tanda bintang (*) dan tanda tanya (?). Satu karakter tanda bintang (*) mencakup sejumlah segmen jalur, tidak seperti sintaksis pencocokan pola untuk pathMatchTemplate.

Parameter jalur, yang ditunjukkan menggunakan titik koma (;), tidak diizinkan karena menyebabkan ambiguitas saat pencocokan.

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

PathGlobs=PATHS
URLPrefix

URL yang dienkode base64 dan aman untuk web, termasuk protokol http:// atau https:// hingga titik yang Anda pilih.

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 duplikat 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 dalam base64 yang sesuai untuk web. Tidak berlaku
hmac Versi nilai HMAC yang dienkode dalam base64 yang sesuai untuk 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 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. Tidak cocok dengan /manifests/4k/main.m3u8.
? (tanda tanya) Mencocokkan satu karakter dalam jalur URL, tidak termasuk karakter garis miring (/). /videos/s?main.m3u8 cocok dengan /videos/s1main.m3u8. Nilai 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 salah satu di token yang ditandatangani. Untuk perlindungan maksimum, pastikan 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

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

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

IPRanges mungkin tidak membantu untuk disertakan dalam token jika klien berisiko mengalami migrasi WAN atau kasus saat jalur jaringan ke frontend aplikasi Anda berbeda dengan jalur pengiriman. Media CDN menolak klien dengan kode HTTP 403 saat 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 stack ganda (IPv4, IPv6)
  • Migrasi koneksi (Wi-Fi ke seluler, dan seluler ke Wi-Fi)
  • Jaringan seluler yang menggunakan NAT Gateway Operator (CGNAT atau CGN)
  • Multi-jalur TCP (MPTCP)

Semua faktor ini dapat berkontribusi pada klien tertentu yang memiliki alamat IP non-deterministik selama sesi pemutaran video. Jika alamat IP klien berubah setelah Anda memberikan akses, dan klien mencoba mendownload segmen video ke buffering pemutaran, klien 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 yang dienkode % atau dienkode base64 yang 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 yang dienkode % atau dienkode base64 yang 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 penggunaan FullPath

Perhatikan contoh berikut yang menggunakan kolom FullPath:

  • Item yang diminta: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Waktu habis masa berlaku: 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 dibuat sebelumnya.

HMAC kunci simetris

Expires=160000000~FullPath~hmac=HMAC_OF_SIGNED_VALUE

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

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

Contoh penggunaan URLPrefix

Perhatikan contoh berikut yang menggunakan kolom URLPrefix:

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

Nilai yang ditandatangani adalah:

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4

Pada 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 web-safe, 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 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 yang ditandatangani yang dibuat sebelumnya.

Contoh penggunaan Headers

Perhatikan contoh berikut yang menggunakan kolom Headers:

  • Item yang diminta: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Waktu habis masa berlaku: 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 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 yang ditandatangani 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 demikian, Anda dapat menandatangani key-value pair header permintaan tertentu tanpa menduplikasi nilai dalam token.