O Alibaba Cloud API Gateway usa JSON Web Token (JWT) para autorizar o acesso à API com base no seu sistema de usuários, permitindo configurações de segurança personalizadas.
1. Autenticação baseada em token
Visão geral
Muitas APIs públicas precisam identificar os chamadores para determinar se concedem acesso aos recursos solicitados. O token é um mecanismo de autenticação. Com a autenticação baseada em token, a aplicação não precisa armazenar informações de autenticação ou sessão do usuário no servidor. Isso viabiliza a autorização de aplicações web distribuídas e sem estado, além de simplificar o dimensionamento da aplicação.
Fluxo de trabalho
O API Gateway usa o plug-in de autenticação JWT para gerenciar a autenticação. O fluxo de trabalho é o seguinte:
Um cliente envia uma solicitação com token ao API Gateway.
O API Gateway usa a chave pública configurada no plug-in de autenticação JWT para verificar o token. Se o token for válido, o API Gateway encaminha a solicitação ao serviço de backend.
O serviço de backend processa a solicitação e retorna uma resposta.
O API Gateway devolve a resposta do serviço de backend ao cliente.
Durante todo esse processo, o API Gateway emprega a autenticação por token para autorizar o acesso à API com base no seu sistema de usuários. As seções a seguir descrevem o JSON Web Token (JWT), usado pelo API Gateway para autenticação.
JWT
1,1 Visão geral
O JSON Web Token (JWT) é um padrão aberto baseado em JSON (RFC 7519) para transmitir declarações (claims) com segurança entre partes em um ambiente de aplicação web. Um JWT funciona como um token de autenticação autossuficiente, capaz de incluir identidade do usuário, funções e permissões. Ele também pode conter declarações adicionais exigidas pela lógica de negócios, tornando os JWTs ideais para autenticação em aplicações distribuídas.
1,2 Estrutura do JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
Conforme o exemplo anterior, um JWT é uma string composta por três partes:
Header
Payload
Signature
Header
O Header contém duas partes:
O tipo do token, que é JWT.
O algoritmo de assinatura usado.
Um Header completo é um objeto JSON, conforme o exemplo a seguir:
{
'typ': 'JWT',
'alg': 'HS256'
}
Em seguida, o Header é codificado em Base64Url para formar a primeira parte do JWT.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
Payload
O Payload contém as declarações (claims).
iss: Issuer. The principal that issued the token. This claim is a string.
sub: Subject. The principal that is the subject of the token. This value is unique within the scope of the issuer. It is a case-sensitive string with a maximum length of 255 ASCII characters.
aud: Audience. The recipients that the token is intended for. This is a case-sensitive array of strings.
exp: Expiration Time. The time after which the token is considered invalid. This claim is an integer that represents the number of seconds from 1970-01-01T00:00:00Z.
iat: Issued At. The time at which the token was issued. This claim is an integer that represents the number of seconds from 1970-01-01T00:00:00Z.
jti: JWT ID. A unique identifier for the token. This value is unique for each token created by the same issuer and is often a cryptographically random value to prevent collisions. This value adds a random entropy component to the structured token that an attacker cannot obtain, which helps prevent token guessing and replay attacks.
Você também pode adicionar declarações personalizadas exigidas pelo seu sistema de usuários. Por exemplo, adicione uma declaração name para o apelido de um usuário:
{
"sub": "1234567890",
"name": "John Doe"
}
O Payload é então codificado em Base64Url para formar a segunda parte do JWT:
JTdCJTBBJTIwJTIwJTIyc3ViJTIyJTNBJTIwJTIyMTIzNDU2Nzg5MCUyMiUyQyUwQSUyMCUyMCUyMm5hbWUlMjIlM0ElMjAlMjJKb2huJTIwRG9lJTIyJTBBJTdE
Signature
Para criar a Signature, una o Header e o Payload codificados em Base64Url com um ponto (.). Em seguida, assine a string resultante usando o algoritmo especificado no Header e uma chave privada ($secret). Isso forma a terceira parte do JWT.
// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, '$secret');
Una as três partes com um ponto (.) para criar o JWT completo, conforme o exemplo de JWT inicial.
1,3 Escopo de autorização e validade
O API Gateway considera um token autorizado a acessar todas as APIs vinculadas ao plug-in JWT dentro de um grupo de APIs. Para um controle de acesso mais refinado, seu serviço de backend deve analisar o token e executar a autorização. O API Gateway valida o campo exp no token. Caso o token tenha expirado, o API Gateway rejeita a solicitação imediatamente. Defina um tempo de expiração inferior a 7 dias.
1,4 Principais características do JWT
Por padrão, os JWTs não são criptografados. Não inclua dados confidenciais em um JWT.
Um JWT serve tanto para autenticação quanto para troca de informações, reduzindo o número de consultas ao banco de dados no servidor. A principal desvantagem é sua natureza sem estado: não é possível revogar um token ou alterar suas permissões antes da expiração. Uma vez emitido, um JWT permanece válido até expirar, a menos que o servidor implemente uma lógica personalizada de revogação.
Um JWT contém informações de autenticação. Se houver vazamento, qualquer pessoa que obtenha o token terá todas as permissões associadas. Para mitigar esse risco, defina um curto período de validade para os JWTs. Em ações de alta segurança, reautentique o usuário antes de conceder acesso.
Para reduzir o risco de roubo, não transmita JWTs em texto simples via HTTP. Use HTTPS.
2. Proteja APIs com o plug-in JWT
2,1 Gere um par de JSON Web Key (JWK)
2,1,1 Gerar um par de chaves on-line
Acesse https://tools.top/jwt-encode.html para gerar uma chave privada e uma chave pública destinadas à geração e verificação de tokens. O serviço de autorização usa a chave privada para emitir JWTs, enquanto a chave pública é configurada no plug-in de autenticação JWT para que o API Gateway verifique as assinaturas das solicitações. O API Gateway suporta o algoritmo RSA SHA256 e tamanho de chave de 2048 bits.
Após abrir o site, selecione a aba JWK format. A página gera automaticamente um par de chaves. Clique em Copy Public Key ou Copy Private Key para obter o conteúdo JWK correspondente.
2,1,2 Gerar um par de chaves localmente
O exemplo a seguir usa Java. Encontre ferramentas semelhantes em outras linguagens de programação para gerar pares de chaves. Crie um projeto Maven e adicione a seguinte dependência:
<dependency>
<groupId>org.bitbucket.b_c</groupId>
<artifactId>jose4j</artifactId>
<version>0.7.0</version>
</dependency>
Use o código a seguir para gerar um par de chaves RSA:
RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
rsaJsonWebKey.setKeyId("authServer");
final String publicKeyString = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
final String privateKeyString = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
2,2 Implemente um serviço de emissão de tokens
Use a string JSON JWK da chave pública gerada on-line na Seção 2,1 ou a string JSON privateKeyString gerada localmente como chave privada para emitir tokens. Esses tokens autorizam usuários confiáveis a acessar APIs protegidas. Para mais informações, consulte Código de exemplo para o serviço de emissão e autenticação de tokens. A forma de emissão dos tokens depende dos seus cenários de negócios. Implante o recurso de emissão de tokens em um ambiente de produção como uma API regular que permite aos visitantes obter um token com nome de usuário e senha, ou gere um token localmente e forneça-o diretamente a um usuário específico.
2,3 Configure o plug-in JWT
Faça login no console do API Gateway.
No painel de navegação à esquerda, escolha .
Na página de gerenciamento de plug-ins, clique em Create Plug-in no canto superior direito.
Na página Create Plug-in, defina Plug-in Name e selecione um Plug-in Type. O código a seguir é um exemplo de configuração para um plug-in de autenticação JWT. Para mais detalhes sobre a configuração, consulte Plug-in de autenticação JWT.
---
parameter: X-Token # The parameter from which to retrieve the JWT. This corresponds to an API parameter.
parameterLocation: header # The location from which to read the JWT. This parameter is optional if the API is in mapping mode, but required if the API is in pass-through mode. Valid values: `query` and `header`.
claimParameters: # Claim parameter mapping. API Gateway maps JWT claims to backend parameters.
- claimName: aud # The name of the claim. Public and private claims are supported.
parameterName: X-Aud # The name of the mapped parameter.
location: header # The location of the mapped parameter. Valid values: `query`, `header`, `path`, and `formData`.
- claimName: userId # The name of the claim. Public and private claims are supported.
parameterName: userId # The name of the mapped parameter.
location: query # The location of the mapped parameter. Valid values: `query`, `header`, `path`, and `formData`.
preventJtiReplay: false # Specifies whether to enable anti-replay checks for the `jti` claim. Default value: false.
# The public key of the JSON Web Key (JWK) generated in section 2.1.
jwk:
kty: RSA
e: AQAB
use: sig
kid: uniq_key
alg: RS256
n: qSVxcknOm0uCq5vGsOmaorPDzHUubBmZZ4UXj-9do7w9X1uKFXAnqfto4TepSNuYU2bA_-tzSLAGBsR-BqvT6w9SjxakeiyQpVmexxnDw5WZwpWenUAcYrfSPEoNU-0hAQwFYgqZwJQMN8ptxkd0170PFauwACOx4Hfr-9FPGy8NCoIO4MfLXzJ3mJ7xqgIZp3NIOGXz-GIAbCf13ii7kSStpYqN3L_zzpvXUAos1FJ9IPXRV84tIZpFVh2lmRh0h8ImK-vI42dwlD_hOIzayL1Xno2R0T-d5AwTSdnep7g-Fwu8-sj4cCRWq3bd61Zs2QOJ8iustH0vSRMYdP5oYQ
2,4 Vincule o plug-in JWT a uma API
Na página Plug-ins, localize o plug-in de autenticação JWT criado e clique em Bind API. Na caixa de diálogo, selecione as APIs do grupo de APIs e do ambiente especificados, adicione-as à lista à direita e clique em OK.
Se uma API já estiver vinculada a um plug-in do mesmo tipo, o novo plug-in substituirá o existente. Prossiga com cautela.
O recurso de depuração de API no console do API Gateway não oferece suporte ao plug-in de autenticação JWT. Para testar APIs vinculadas ao plug-in, use uma ferramenta como o Postman ou execute o comando curl.
3. Código de exemplo para um serviço de emissão de tokens
import java.security.PrivateKey;
import org.jose4j.json.JsonUtil;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.NumericDate;
import org.jose4j.lang.JoseException;
public class GenerateJwtDemo {
public static void main(String[] args) throws JoseException {
// Use the keyId that is set in API Gateway.
String keyId = "uniq_key";
// Use the key pair that you generated in Section 2.1.
String privateKeyJson = "{\n"
+ " \"kty\": \"RSA\",\n"
+ " \"d\": "
+
"\"O9MJSOgcjjiVMNJ4jmBAh0mRHF_TlaVva70Imghtlgwxl8BLfcf1S8ueN1PD7xV6Cnq8YenSKsfiNOhC6yZ_fjW1syn5raWfj68eR7cjHWjLOvKjwVY33GBPNOvspNhVAFzeqfWneRTBbga53Agb6jjN0SUcZdJgnelzz5JNdOGaLzhacjH6YPJKpbuzCQYPkWtoZHDqWTzCSb4mJ3n0NRTsWy7Pm8LwG_Fd3pACl7JIY38IanPQDLoighFfo-Lriv5z3IdlhwbPnx0tk9sBwQBTRdZ8JkqqYkxUiB06phwr7mAnKEpQJ6HvhZBQ1cCnYZ_nIlrX9-I7qomrlE1UoQ\",\n"
+ " \"e\": \"AQAB\",\n"
+ " \"kid\": \"myJwtKey\",\n"
+ " \"alg\": \"RS256\",\n"
+ " \"n\": \"vCuB8MgwPZfziMSytEbBoOEwxsG7XI3MaVMoocziP4SjzU4IuWuE_DodbOHQwb_thUru57_Efe"
+
"--sfATHEa0Odv5ny3QbByqsvjyeHk6ZE4mSAV9BsHYa6GWAgEZtnDceeeDc0y76utXK2XHhC1Pysi2KG8KAzqDa099Yh7s31AyoueoMnrYTmWfEyDsQL_OAIiwgXakkS5U8QyXmWicCwXntDzkIMh8MjfPskesyli0XQD1AmCXVV3h2Opm1Amx0ggSOOiINUR5YRD6mKo49_cN-nrJWjtwSouqDdxHYP-4c7epuTcdS6kQHiQERBd1ejdpAxV4c0t0FHF7MOy9kw\"\n"
+ "}";
JwtClaims claims = new JwtClaims();
claims.setGeneratedJwtId();
claims.setIssuedAtToNow();
// The expiration time must be set and be less than 7 days.
NumericDate date = NumericDate.now();
date.addSeconds(120*60);
claims.setExpirationTime(date);
claims.setNotBeforeMinutesInThePast(1);
claims.setSubject("YOUR_SUBJECT");
claims.setAudience("YOUR_AUDIENCE");
// Add custom parameters. All values must be of the String type.
claims.setClaim("userId", "1213234");
claims.setClaim("email", "userEm***@youapp.com");
JsonWebSignature jws = new JsonWebSignature();
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
// This parameter is required.
jws.setKeyIdHeaderValue(keyId);
jws.setPayload(claims.toJson());
PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyJson)).getPrivateKey();
jws.setKey(privateKey);
String jwtResult = jws.getCompactSerialization();
System.out.println("Generate Json Web token , result is " + jwtResult);
}
}
Observe os seguintes pontos:
O
keyIddeve ser globalmente exclusivo e corresponder nos três locais a seguir:
O
keyIdespecificado ao gerar um par de chaves na seção 2,1 Gerar um par de JSON Web Key (JWK).O
kidconfigurado no plug-in de autenticação JWT na seção 2,3 Configurar a chave pública no plug-in de autenticação JWT.O
keyIdno código, que corresponde ao valor da propriedadeKeyIdHeaderValuedo objetoJsonWebSignature. Esta propriedade é obrigatória.
-
Para
privateKeyJson, use a string JSON JWK da chave privada. Pode ser aquela gerada on-line na Seção 2,1 ou a string JSONprivateKeyStringcaso você tenha gerado o par de chaves localmente.privateKeyStringé uma string JSON. O período de validade é obrigatório e deve ser inferior a sete dias.
Use valores do tipo String para todos os parâmetros personalizados.
4. Respostas de erro do API Gateway
|
Status |
Code |
Message |
Description |
|
400 |
I400JR |
JWT required |
Parâmetro JWT não encontrado. |
|
403 |
S403JI |
Claim jti is required when preventJtiReplay:true |
A declaração |
|
403 |
S403JU |
Claim jti in JWT is used |
O |
|
403 |
A403JT |
Invalid JWT: ${Reason} |
O JWT fornecido na solicitação é inválido. |
|
400 |
I400JD |
JWT Deserialize Failed: ${Token} |
Falha ao desserializar o JWT fornecido na solicitação. |
|
403 |
A403JK |
No matching JWK, kid:${kid} not found |
O |
|
403 |
A403JE |
JWT is expired at ${Date} |
O JWT fornecido na solicitação expirou. |
|
400 |
I400JP |
Invalid JWT plugin config: ${JWT} |
O plug-in de autenticação JWT está configurado incorretamente. |
Caso receba um código de status inesperado, verifique o cabeçalho de resposta X-Ca-Error-Code para obter o ErrorCode e o cabeçalho X-Ca-Error-Message para obter a ErrorMessage. Se o código de erro for A403JT ou I400JD, use jwt.io para verificar a validade e o formato do seu token.