Estos contenidos se han traducido de forma automática para su comodidad, pero Huawei Cloud no garantiza la exactitud de estos. Para consultar los contenidos originales, acceda a la versión en inglés.
Centro de ayuda/ Object Storage Service/ Referencia de la API/ Llamar a APIs/ Autenticación/ Autenticación de la firma transportada en la tabla cargada con un navegador
Actualización más reciente 2024-09-18 GMT+08:00

Autenticación de la firma transportada en la tabla cargada con un navegador

OBS admite la carga de objetos basada en navegador utilizando el método POST. Las firmas de dichas solicitudes se cargan en tablas. En primer lugar, cree una política de seguridad y especifique los requisitos de la solicitud, por ejemplo, nombre de bucket y prefijo de nombre de objeto. A continuación, cree una firma basada en esta política. El formulario de solicitud que se va a firmar debe contener una firma y una política válidas. Por último, cree una tabla para cargar el objeto en el bucket.

El proceso de cálculo de firmas es el siguiente:

  1. El contenido de la política está codificado en formato UTF-8.
  2. Realizar codificación de Base64 sobre el resultado obtenido del paso anterior.
  3. Utilice la SK para realizar el cálculo de la firma HMAC-SHA1 en el resultado obtenido del paso 2.
  4. Realice la codificación de Base64 en el resultado del paso 3 para obtener la firma.
StringToSign = Base64( UTF-8-Encoding-Of( policy ) )
Signature = Base64( HMAC-SHA1( YourSecretAccessKeyID, StringToSign ) )

El contenido de la política es el siguiente:
{ "expiration": "2017-12-31T12:00:00.000Z",
  "conditions": [
    {"x-obs-acl": "public-read" },
    {"x-obs-security-token": "YwkaRTbdY8g7q...." },
    {"bucket": "book" },
    ["starts-with", "$key", "user/"]
  ]
}

La póliza contiene el período de validez (véase Caducidad) y las condiciones (véase Condiciones)

Caducidad

El campo de expiration describe cuándo caducará la firma, que se expresa en el formato según ISO 8601 UTC. Por ejemplo, expiration: 2017-12-31T12:00:00.000Z en el ejemplo significa que la solicitud se invalida después de las 12:00:00 de 31 de diciembre de 2017. Este campo debe especificarse en una política. Solo puede estar en el formato aaaa-MM-dd'T'HH:mm:ss'Z' o aaaa-MM-dd'T'HH:mm:ss.SSS'Z'.

Condiciones

Un mecanismo utilizado para verificar la validez de una solicitud. Las condiciones se utilizan para definir el contenido que debe estar contenido en una solicitud. En el ejemplo, el nombre del bucket solicitado es book, el nombre del objeto tiene el prefijo user/ y la ACL del objeto es de lectura pública. Todos los elementos del formulario, excepto AccessKeyId, signature, file, policy, token, field names y el prefijo x-ignore-, deben incluirse en la política. En la siguiente tabla se enumeran los elementos que deben incluirse en las condiciones.

Tabla 1 Condiciones contenidas en una política

Elemento

Descripción

x-obs-acl

ACL en la solicitud.

Soporta la coincidencia exacta y la coincidencia condicional, como starts-with.

content-length-range

Longitud máxima y mínima de un objeto que se va a cargar. El valor puede ser un rango.

Cache-Control, Content-Type, Content-Disposition, Content-Encoding, Expires

Encabezados especiales para solicitudes de REST

Soporta la coincidencia exacta y la coincidencia condicional, como starts-with.

key

Nombre de un objeto que se va a cargar.

Soporta la coincidencia exacta y la coincidencia condicional, como starts-with.

bucket

Nombre del bucket solicitado.

Soporta coincidencia exacta.

success_action_redirect

Dirección de redirección después de que la carga se haya realizado correctamente. Para obtener más información, véase Carga de objetos - POST.

Soporta la coincidencia exacta y la coincidencia condicional, como starts-with.

success_action_status

Si no se especifica success_action_redirect, el código de estado se devuelve al cliente cuando la carga se realiza correctamente. Para obtener más información, véase Carga de objetos - POST.

Soporta coincidencia exacta.

x-obs-meta-*

Metadatos definidos por el usuario.

Las palabras clave de un elemento no pueden contener los caracteres no de ASCII o irreconocibles. Si son necesarios los caracteres no de ASCII o irreconocibles, deben codificarse y decodificarse en el lado del cliente. La codificación URL o la codificación Base64 son aceptables, pero el servidor no realiza la decodificación.

Soporta la coincidencia exacta y la coincidencia condicional, como starts-with.

x-obs-*

Otros campos de encabezado con prefijo x-obs-.

Soporta la coincidencia exacta y la coincidencia condicional, como starts-with.

x-obs-security-token

Nombre del campo en el encabezado de solicitud.

Campo obligatorio para la autenticación temporal de AK/SK y token de seguridad.

La tabla siguiente describe cómo se pueden emparejar las condiciones de política.

Tabla 2 Métodos de coincidencia de condiciones de la política

Método de coincidencia

Descripción

Exact Matches

Coincidencia exacta por defecto. El valor de la tabla POST debe ser el mismo que el de la política. Por ejemplo, si el objeto ACL se establece en public-read cuando se carga el objeto, el valor del elemento x-obs-acl en la tabla es public-read. Por lo tanto, las condiciones de la política se pueden establecer en

{"x-obs-acl": "public-read"} o ["eq", "$x-obs-acl", "public-read"], que son equivalentes.

Starts With

Si se utiliza esta condición, el valor establecido en la tabla POST debe comenzar con una string de caracteres fija. Por ejemplo, si el nombre de los objetos cargados debe tener el prefijo user/, el valor del elemento key en la tabla puede ser user/test1, user/test2 y así sucesivamente. Por lo tanto, las condiciones de la política se pueden establecer en:

["starts-with", "$key", "user/"]

Matching Any Content

El elemento correspondiente en la tabla POST puede ser cualquier valor. Por ejemplo, si la dirección de redirección tras el éxito de la solicitud puede ser cualquier dirección, el valor del elemento success_action_redirect en la tabla puede ser cualquier valor. Por lo tanto, las condiciones de la política se pueden establecer en:

["starts-with", "$success_action_redirect", ""]

Specifying Ranges

La longitud de contenido del elemento file en la tabla POST puede ser un rango especificado y se utiliza solo para limitar el tamaño del objeto. Por ejemplo, si el tamaño del objeto cargado está entre 1 MB y 10 MB, la longitud de contenido del elemento file de la tabla puede ser de 1048576 a 10485760. Por lo tanto, las condiciones de la política se pueden establecer en (el valor no contiene comillas)

["content-length-range", 1048576, 10485760]

Una política está en formato JSON. Las condiciones se pueden poner entre corchetes rizados {} y corchetes cuadrados []. Los elementos clave y valor de la tabla se escriben entre corchetes rizados {}, que están separados por dos puntos (:). Los corchetes [] contienen el tipo de condición, la clave y el valor. Estos tres elementos están separados por comas (,). El signo de dólar ($) delante de la clave indica que la clave es una variable.

La siguiente tabla muestra los caracteres que deben escaparse en una política.

Tabla 3 Personajes que deben escaparse en una política

Personaje después de escapar

Carácter real

\\

Barra diagonal inversa (\)

\$

Símbolo del dólar ($)

\b

Tecla retroceso

\f

Página arriba y abajo

\n

Saltos de línea

\r

Ingresar

\t

Tabla horizontal

\v

Tabla vertical

\uxxxx

Todos los caracteres de Unicode

Ejemplos de solicitud y política

En las tablas siguientes se proporcionan ejemplos de solicitudes y políticas.

Ejemplo 1: Subir el objeto testfile.txt al bucket examplebucket y establecer el objeto ACL a public-read.

Solicitud

Política

POST / HTTP/1.1

Host: examplebucket.obs.region.myhuaweicloud.com

Content-Type: multipart/form-data; boundary=7e32233530b26

Content-Length: 1250

--7e32233530b26

Content-Disposition: form-data; name="key"

testfile.txt

--7e32233530b26

Content-Disposition: form-data; name="x-obs-acl"

public-read

--7e32233530b26

Content-Disposition: form-data; name="content-type"

text/plain

--7e32233530b26

Content-Disposition: form-data; name="AccessKeyId"

UDSIAMSTUBTEST000002

--7e32233530b26

Content-Disposition: form-data; name="policy"

ewogICJleHBpcmF0aW9uIjogIjIwMTktMDctMDFUMTI6MDA6MDAuMDAwWiIsCiAgImNvbmRpdGlvbnMiOiBbCiAgICB7ImJ1Y2tldCI6ICJleGFtcGxlYnVja2V0IiB9LAogICAgWyJlcSIsICIka2V5IiwgInRlc3RmaWxlLnR4dCJdLAoJeyJ4LW9icy1hY2wiOiAicHVibGljLXJlYWQiIH0sCiAgICBbImVxIiwgIiRDb250ZW50LVR5cGUiLCAidGV4dC9wbGFpbiJdLAogICAgWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsIDYsIDEwXQogIF0KfQo=

--7e32233530b26

Content-Disposition: form-data; name="signature"

xxl7bZs/5FgtBUggOdQ88DPZUo0=

--7e32233530b26

Content-Disposition: form-data; name="file"; filename="E:\TEST_FILE\TEST.txt"

Content-Type: text/plain

123456

--7e32233530b26

Content-Disposition: form-data; name="submit"

Upload

--7e32233530b26--

{

"expiration": "2019-07-01T12:00:00.000Z",

"conditions": [

{"bucket": "examplebucket" },

["eq", "$key", "testfile.txt"],

{"x-obs-acl": "public-read" },

["eq", "$Content-Type", "text/plain"]

]

}

Ejemplo 2: Sube el objeto file/obj1 al bucket examplebucket y configura los cuatro elementos de metadatos personalizados del objeto.

Solicitud

Política

POST / HTTP/1.1

Host: examplebucket.obs.region.myhuaweicloud.com

Content-Type: multipart/form-data; boundary=7e329d630b26

Content-Length: 1597

--7e3542930b26

Content-Disposition: form-data; name="key"

file/obj1

--7e3542930b26

Content-Disposition: form-data; name="AccessKeyId"

UDSIAMSTUBTEST000002

--7e3542930b26

Content-Disposition: form-data; name="policy"

ewogICJleHBpcmF0aW9uIjogIjIwMTktMDctMDFUMTI6MDA6MDAuMDAwWiIsCiAgImNvbmRpdGlvbnMiOiBbCiAgICB7ImJ1Y2tldCI6ICJleGFtcGxlYnVja2V0IiB9LAogICAgWyJzdGFydHMtd2l0aCIsICIka2V5IiwgImZpbGUvIl0sCiAgICB7Ingtb2JzLW1ldGEtdGVzdDEiOiJ2YWx1ZTEifSwKICAgIFsiZXEiLCAiJHgtb2JzLW1ldGEtdGVzdDIiLCAidmFsdWUyIl0sCiAgICBbInN0YXJ0cy13aXRoIiwgIiR4LW9icy1tZXRhLXRlc3QzIiwgImRvYyJdLAogICAgWyJzdGFydHMtd2l0aCIsICIkeC1vYnMtbWV0YS10ZXN0NCIsICIiXQogIF0KfQo=

--7e3542930b26

Content-Disposition: form-data; name="signature"

HTId8hcaisn6FfdWKqSJP9RN4Oo=

--7e3542930b26

Content-Disposition: form-data; name="x-obs-meta-test1"

value1

--7e3542930b26

Content-Disposition: form-data; name="x-obs-meta-test2"

value2

--7e3542930b26

Content-Disposition: form-data; name="x-obs-meta-test3"

doc123

--7e3542930b26

Content-Disposition: form-data; name="x-obs-meta-test4"

my

--7e3542930b26

Content-Disposition: form-data; name="file"; filename="E:\TEST_FILE\TEST.txt"

Content-Type: text/plain

123456

--7e3542930b26

Content-Disposition: form-data; name="submit"

Upload

--7e3542930b26--

{

"expiration": "2019-07-01T12:00:00.000Z",

"conditions": [

{"bucket": "examplebucket" },

["starts-with", "$key", "file/"],

{"x-obs-meta-test1":"value1"},

["eq", "$x-obs-meta-test2", "value2"],

["starts-with", "$x-obs-meta-test3", "doc"],

["starts-with", "$x-obs-meta-test4", ""]

]

}

Cálculo de signatura en Java:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class SignDemo {

    private static final String DEFAULT_ENCODING = "UTF-8";

    private static final String EXPIRATION_DATE_FORMATTER = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";

    private static final TimeZone GMT_TIMEZONE = TimeZone.getTimeZone("GMT");

    private static final long DEFAULT_EXPIRE_SECONDS = 300;

    private String ak;

    private String sk;

    private String join(List<?> items, String delimiter) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < items.size(); i++) {
            String item = items.get(i).toString();
            sb.append(item);
            if (i < items.size() - 1) {
                sb.append(delimiter);
            }
        }
        return sb.toString();
    }

    public String hmacSha1(String input)
        throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
        SecretKeySpec signingKey = new SecretKeySpec(this.sk.getBytes(DEFAULT_ENCODING), "HmacSHA1");
        Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(signingKey);
        return Base64.getEncoder()
            .encodeToString(mac.doFinal(Base64.getEncoder().encode(input.getBytes(DEFAULT_ENCODING))));
    }

    private String stringToSign(List<String> conditions, String expiration) {
        StringBuilder policy = new StringBuilder();
        policy.append("{\"expiration\":").append("\"").append(expiration).append("\",")
            .append("\"conditions\":[");

        policy.append(join(conditions, ","));
        policy.append("]}");
        return policy.toString();
    }

    private String getFormatExpiration(Date requestDate, long expires) {
        requestDate = requestDate != null ? requestDate : new Date();
        SimpleDateFormat expirationDateFormat = new SimpleDateFormat(EXPIRATION_DATE_FORMATTER);
        expirationDateFormat.setTimeZone(GMT_TIMEZONE);
        Date expiryDate = new Date(requestDate.getTime() + (expires <= 0 ? DEFAULT_EXPIRE_SECONDS : expires) * 1000);
        String expiration = expirationDateFormat.format(expiryDate);
        return expiration;
    }

    public String postSignature(List<String> conditions, String expiration) throws Exception {
        return hmacSha1(this.stringToSign(conditions, expiration));
    }

    public static void main(String[] args) throws Exception {
        SignDemo demo = new SignDemo();

        /* Hard-coded or plaintext AK and SK are risky. For security purposes, encrypt your AK and SK and store them in the configuration file or environment variables.
        In this example, the AK and SK are stored in environment variables for identity authentication. Before running the code in this example, configure environment variables HUAWEICLOUD_SDK_AK and HUAWEICLOUD_SDK_SK. */
	demo.ak = System.getenv("HUAWEICLOUD_SDK_AK");
	demo.sk = System.getenv("HUAWEICLOUD_SDK_SK");

        String expiration = demo.getFormatExpiration(null, 0);
        List<String> conditions = new ArrayList<>();
        String[] tmpConditions = {
            "{\"x-obs-acl\": \"public-read\" }",
            "{\"x-obs-security-token\": \"YwkaRTbdY8g7q....\" }",
            "{\"bucket\": \"book\" }",
            "[\"starts-with\", \"$key\", \"user/\"]"
        };
        for (String condition : tmpConditions) {
            conditions.add(condition);
        }

        System.out.println(expiration);
        System.out.println(demo.postSignature(conditions, expiration));
    }
}

Cálculo de signatura en Python:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# coding=utf-8
import hashlib
import hmac
import time
import pytz
import binascii

from datetime import datetime


class SignatureDemo:
    EXPIRATION_DATE_FORMATTER = "%Y-%m-%dT%H:%M:%S.%f"

    DEFAULT_ENCODING = "UTF-8"

    # The default expiration time is 5 minutes.
    DEFAULT_EXPIRE_SECONDS = 300

    GMT_TIMEZONE = "GMT"

    def __init__(self, ak=None, sk=None):
        self.ak = ak
        self.__sk = sk

    def set_secret_access_key(self, sk):
        self.__sk = sk

    # request_date and expires should be set to timestamps, for example, 1675651495.979.
    def get_format_expiration(self, request_date, expires):
        request_date = request_date if request_date else time.time()
        expiry_date = request_date + (expires if expires > 0 else self.DEFAULT_EXPIRE_SECONDS)
        expiration = datetime.fromtimestamp(expiry_date, pytz.timezone(self.GMT_TIMEZONE)).strftime(
            self.EXPIRATION_DATE_FORMATTER)[:-3] + "Z"
        return expiration

    def hmac_sha1(self, ipt):
        # If binascii or encode("base64") is used, newline characters must be removed.
        policy_base64 = binascii.b2a_base64(ipt.encode(self.DEFAULT_ENCODING)).rstrip()
        hashed = hmac.new(self.__sk.encode(self.DEFAULT_ENCODING), policy_base64, hashlib.sha1)
        return binascii.b2a_base64(hashed.digest()).rstrip()

    def post_signature(self, conditions, expiration):
        return self.hmac_sha1(self.string_to_sign(conditions, expiration))

    @staticmethod
    def string_to_sign(conditions, expiration):
        policy = ""
        policy += "{\"expiration\":" + "\"" + expiration + "\"," + "\"conditions\":["
        policy += ",".join(conditions) + "]}"
        return policy


if __name__ == "__main__":
    demo = SignatureDemo()

    # Hard-coded or plaintext AK and SK are risky. For security purposes, encrypt your AK and SK and store them in the configuration file or environment variables.
    # In this example, the AK and SK are stored in environment variables for identity authentication. Before running the code in this example, configure environment variables HUAWEICLOUD_SDK_AK and HUAWEICLOUD_SDK_SK.
    demo.ak = os.getenv('HUAWEICLOUD_SDK_AK')
    demo.set_secret_access_key(os.getenv('HUAWEICLOUD_SDK_SK'))

    exp = demo.get_format_expiration(None, 0)
    conditions_example = [
        "{\"x-obs-acl\": \"public-read\" }",
        "{\"x-obs-security-token\": \"YwkaRTbdY8g7q....\" }",
        "{\"bucket\": \"book\" }",
        "[\"starts-with\", \"$key\", \"user/\"]"
    ]
    print(exp)
    print(demo.post_signature(conditions_example, exp))