Générer des jetons

Ce guide explique comment générer un jeton et présente les champs obligatoires et facultatifs pour les jetons.

Pour créer un jeton, vous devez composer une chaîne à signer, que nous appelons une dans ce guide. La valeur signée inclut des paramètres qui décrivent le contenu que vous protégez, l'heure d'expiration de la valeur signée, etc. avant.

Vous utilisez la valeur signée lors de la création d'une chaîne de jeton. Vous créez une chaîne de jeton en composant les paramètres du jeton, par exemple un code d'authentification des messages basé sur le hachage (HMAC, Hash-based Message Authentication Code) avec clé symétrique de la valeur signée.

Media CDN utilise le jeton composé final pour protéger votre contenus.

Créer un jeton

  1. Créez une valeur signée en concaténant une chaîne contenant le champs de jeton obligatoires et jeton facultatif souhaité . Séparez chaque champ et les paramètres avec Un tilde ~.

  2. Signez la valeur signée avec une signature Ed25519 ou un HMAC à clé symétrique.

  3. Composez le jeton en concaténant une chaîne contenant les éléments de jetons et les champs facultatifs de jeton. Séparez chaque champ des champs avec un tilde ~.

    Lors de la composition du jeton, les valeurs de chacun des paramètres sont les entre la valeur signée et la chaîne de jeton, avec ce qui suit : exceptions:

    • FullPath
    • Headers

L'exemple de code suivant montre comment créer un jeton de manière programmatique:

Python

Pour vous authentifier auprès de Media CDN, configurez les identifiants par défaut de l'application. Pour en savoir plus, consultez Configurer l'authentification pour un environnement de développement local.

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

Pour vous authentifier auprès de Media CDN, configurez les identifiants par défaut de l'application. Pour en savoir plus, consultez Configurer l'authentification pour un environnement de développement local.


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 + '\''
          + '}';
    }
  }

}

Les sections suivantes décrivent les champs utilisés par les jetons.

Champs obligatoires pour les jetons

Les champs suivants sont obligatoires pour chaque jeton :

  • Expires
  • L'un des éléments suivants:
    • PathGlobs
    • URLPrefix
    • FullPath
  • Choisissez l'une des options suivantes:
    • Signature
    • hmac

Sauf indication contraire, les noms des paramètres et leurs valeurs sont sensibles à la casse.

Le tableau suivant explique chaque paramètre :

Nom / Alias du champ Paramètres de jeton Valeur signée

Expires

exp

Nombre entier de secondes écoulées depuis l'époque Unix (1970-01-01T00:00:00Z) Expires=EXPIRATION_TIME, après quoi le jeton n'est plus valide.

PathGlobs

paths, acl

Liste de cinq segments de chemin d'accès auxquels accorder l'accès (maximum) Les segments peut être délimité par une virgule (,) ou un point d'exclamation (!), mais pas les deux.

PathGlobs accepte l'utilisation de caractères génériques dans les chemins à l'aide de des astérisques (*) et des points d'interrogation (?). Une seule L'astérisque (*) recouvre n'importe quel nombre de segments de chemin. contrairement à la syntaxe de correspondance de modèle pour pathMatchTemplate.

Les paramètres de chemin d'accès, indiqués par des points-virgules (;), sont Non autorisé, car ils créent une ambiguïté lors de la mise en correspondance.

Par conséquent, assurez-vous que votre URL ne contient pas les éléments suivants : Caractères spéciaux: ,!*?;

PathGlobs=PATHS
URLPrefix

URL encodée au format base64 adapté au Web et incluant le protocole http:// ou https:// jusqu'à un point de votre choisir.

Par exemple, des valeurs URLPrefix valides pour "https://example.com/foo/bar.ts" correspond à "https://example.com", `https://example.com/foo` et `https://example.com/foo/bar`.

URLPrefix=BASE_64_URL_PREFIX
FullPath Aucun Lorsque vous spécifiez FullPath dans un jeton, ne dupliquez pas au chemin d'accès que vous avez spécifié dans la valeur signée. Dans un jeton, incluez le champ sans =. FullPath=FULL_PATH_TO_OBJECT
Signature Version de la signature encodée en base64 adaptée au Web. Non applicable
hmac Version de la valeur HMAC encodée en base64 adaptée au Web. Non applicable

Syntaxe du caractère générique PathGlobs

Le tableau suivant explique la syntaxe du caractère générique PathGlobs.

Opérateur Correspondance établie Examples
* (astérisque) Établit une correspondance avec zéro ou plusieurs caractères du chemin de l'URL, y compris ou une barre oblique (/).
  • /videos/* correspond à n'importe quel chemin commençant par /videos/
  • /videos/s*/4k/* correspond à /videos/s/4k/ et /videos/s01/4k/main.m3u8
  • /manifests/*/4k/* correspondances /manifests/s01/4k/main.m3u8 et /manifests/s01/e01/4k/main.m3u8 Il ne correspond pas à /manifests/4k/main.m3u8.
? (point d'interrogation) Correspond à un seul caractère dans le chemin d'accès de l'URL, sans inclure les caractères de barre oblique (/). /videos/s?main.m3u8 correspondances /videos/s1main.m3u8 Elle ne correspond à aucune des /videos/s01main.m3u8 ou /videos/s/main.m3u8.

Les expressions génériques doivent commencer par un astérisque (*) ou une barre oblique (/) pour les chemins d'URL.

Étant donné que * et /* correspondent à tous les chemins d'URL, nous vous déconseillons de les utiliser dans vos jetons signés. Pour une protection maximale, vérifier que vos globs correspondent au contenu auquel vous souhaitez accorder l'accès.

Champs de jeton facultatifs

Sauf indication contraire, les noms des paramètres et leurs valeurs sont sensibles à la casse.

Le tableau suivant explique les noms de paramètres, les éventuels alias et les détails des paramètres facultatifs :

Nom / Alias du champ Paramètres Valeur signée

Starts

st

Nombre de secondes entières depuis l'époque Unix (1970-01-01T00:00:00Z) Starts=START_TIME
IPRanges

Une liste comportant jusqu'à cinq adresses IPv4 et IPv6 au format CIDR pour cette URL est valide au format base64 adapté au Web. Par exemple, pour spécifier les plages d'adresses IP "192.6.13.13/32,193.5.64.135/32", spécifiez IPRanges=MTkyLjYuMTMuMTMvMzIsMTkzLjUuNjQuMTM1LzMy.

Les plages d'adresses IP peuvent ne pas être utiles à inclure dans les jetons lorsque les clients sont susceptibles d'effectuer des migrations WAN ou dans les cas où le chemin d'accès réseau à l'interface de l'application est différent du chemin de distribution. Media CDN rejette les clients disposant d'un code HTTP 403 lorsqu'ils se connectent avec une adresse IP ne faisant pas partie de la requête signée.

Les cas suivants peuvent entraîner le rejet des clients avec le code HTTP 403 dans Media CDN :

  • Environnements double pile (IPv4, IPv6)
  • Migration de connexion (Wi-Fi vers mobile et mobile vers Wi-Fi)
  • Réseaux mobiles utilisant la passerelle NAT (CGNAT ou CGN)
  • TCP multi-chemin (MPTCP)

Tous ces facteurs peuvent contribuer à ce qu'un client donné ait une adresse IP non déterministe pendant une session de lecture vidéo. Si l'adresse IP du client change après avoir accordé l'accès et que le client tente de télécharger un segment vidéo dans son tampon de lecture, il reçoit une erreur HTTP 403 de Media CDN.

IPRanges=BASE_64_IP_RANGES

SessionID

id

Chaîne arbitraire utile pour l'analyse ou la lecture de journaux du traçage.

Pour éviter de créer un jeton non valide, utilisez un encodage % ou sécurisé pour le Web les chaînes encodées en base64. Les caractères suivants ne doivent pas être utilisés pour SessionID, car ils entraînent la non-validité du jeton: "~", "&" ou " " (espace).

SessionID=SESSION_ID_VALUE

Data

data, payload

Chaîne arbitraire, utile pour l'analyse des journaux.

Pour éviter de créer un jeton non valide, utilisez un encodage % ou sécurisé pour le Web les chaînes encodées en base64. Les caractères suivants ne doivent pas être utilisés pour Data, car ils entraînent la non-validité du jeton: "~", "&" ou " " (espace).

data=DATA_VALUE
Headers Liste des noms des champs d'en-tête séparés par une virgule. Les noms des en-têtes sont non sensible à la casse pour les recherches dans la requête. Noms des en-têtes dans la partie sont sensibles à la casse. Si un en-tête est manquant, la valeur est vide . S'il existe plusieurs copies d'un en-tête, elles sont concaténés par une virgule. Headers=HEADER_1_NAME=HEADER_1_EXPECTED_VALUE, HEADER_2_NAME=HEADER_2_EXPECTED_VALUE

Examples

Les sections suivantes présentent des exemples de génération de jetons.

Exemple avec FullPath

Prenons l'exemple suivant utilisant le champ FullPath :

  • Élément demandé: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Date/Heure d'expiration: 160000000

La valeur signée est la suivante :

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

Pour créer un jeton, signez la valeur signée avec une signature Ed25519 ou un HMAC à clé symétrique.

Voici des exemples de jetons créés à partir d'une valeur signée:

Signature ED25519

Expires=160000000~FullPath~Signature=SIGNATURE_OF_SIGNED_VALUE

SIGNATURE_OF_SIGNED_VALUE est la signature ED25519 du valeur signée précédemment créée.

HMAC à clé symétrique

Expires=160000000~FullPath~hmac=HMAC_OF_SIGNED_VALUE

HMAC_OF_SIGNED_VALUE est le code HMAC à clé symétrique du modèle précédemment créée.

Dans les exemples précédents, FullPath est fourni dans le jeton, mais la valeur n'est pas répété à partir du chemin spécifié dans la valeur signée. Cela vous permet de signer le chemin complet de la requête sans dupliquer la requête dans le jeton.

Exemple avec URLPrefix

Prenons l'exemple suivant avec le champ URLPrefix:

  • Élément demandé: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Date/Heure d'expiration: 160000000

La valeur signée est la suivante :

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4

Dans l'exemple précédent, nous avons remplacé le chemin d'accès à l'élément demandé, http://example.com/tv/my-show/s01/e01/playlist.m3u8 par le chemin d'accès à l'élément ; au format Base64 adapté au Web, aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4

Pour créer un jeton, signez la valeur signée avec une signature Ed25519 ou un HMAC à clé symétrique.

Voici des exemples de jetons créés à partir d'une valeur signée:

Signature ED25519

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4~Signature=SIGNATURE_OF_SIGNED_VALUE

SIGNATURE_OF_SIGNED_VALUE est la signature ED25519 du valeur signée précédemment créée.

HMAC à clé symétrique

Expires=160000000~URLPrefix=aHR0cDovL2V4YW1wbGUuY29tL3R2L215LXNob3cvczAxL2UwMS9wbGF5bGlzdC5tM3U4~hmac=HMAC_OF_SIGNED_VALUE

HMAC_OF_SIGNED_VALUE est le code HMAC à clé symétrique du modèle précédemment créée.

Exemple avec Headers

Prenons l'exemple suivant utilisant le champ Headers :

  • Élément demandé: http://example.com/tv/my-show/s01/e01/playlist.m3u8
  • Date d'expiration : 160000000
  • Valeur PathGlobs: *
  • En-têtes de requête attendus:
    • user-agent: browser
    • accept: text/html

La valeur signée est la suivante:

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

Pour créer un jeton, signez la valeur signée avec une signature Ed25519 ou un HMAC à clé symétrique.

Voici des exemples de jetons créés à partir d'une valeur signée:

Signature ED25519

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

SIGNATURE_OF_SIGNED_VALUE est la signature ED25519 du valeur signée précédemment créée.

HMAC à clé symétrique

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

HMAC_OF_SIGNED_VALUE est le code HMAC à clé symétrique du modèle précédemment créée.

Dans les exemples précédents, Headers=user-agent,accept est fourni dans le jeton. mais les valeurs d'en-tête attendues ne sont pas répétées à partir de la valeur signée. Cela permet vous signez des paires clé/valeur spécifiques en-tête de requête sans dupliquer les valeurs. dans le jeton.