O plug-in jwt-logout usa o Redis para implementar gerenciamento de estado fraco para JSON Web Tokens (JWTs), permitindo a invalidação proativa de JWTs e o controle de login em dispositivo único. Por exemplo, quando um usuário faz login em um novo dispositivo, a sessão anterior é invalidada automaticamente.
Tipo de plug-in
Plug-in de autenticação e autorização.
Campos
|
Campo |
Tipo de dados |
Obrigatório |
Valor padrão |
Descrição |
|
jwks |
string |
Não |
- |
String JSON para verificação de JWT. Dispensável quando este plug-in é usado com o plug-in jwt-auth. Para mais informações, consulte JSON Web Key (JWK). |
|
clock_skew |
number |
Não |
60 |
Diferença de horário permitida durante a verificação dos campos exp e iat em um JWT. Unidade: segundos. |
|
token_header |
string |
Não |
Authorization |
Cabeçalho da requisição de onde o JWT é extraído. |
|
token_prefix |
string |
Não |
"Bearer" |
Prefixo do valor do cabeçalho. Após a remoção do prefixo, a parte restante é usada como JWT. |
|
redis |
Redis |
Sim |
- |
Configuração do serviço Redis. |
|
logout |
Logout |
Não |
- |
Configuração do recurso de logoff de JWT. Se não configurado, o logoff de JWT fica desativado. |
|
login |
Login |
Não |
- |
Configuração do recurso de login em dispositivo único para JWT. Se não configurado, o login em dispositivo único fica desativado. |
A tabela a seguir descreve os campos de configuração do tipo Redis.
|
Campo |
Tipo de dados |
Obrigatório |
Valor padrão |
Descrição |
|
service |
string |
Sim |
- |
Nome do serviço Redis. Exemplos:
|
|
port |
number |
Sim |
- |
Número da porta do serviço Redis. |
|
username |
string |
Não |
- |
Nome de usuário para o comando AUTH do Redis. |
|
password |
string |
Não |
- |
Senha para o comando AUTH do Redis. |
|
timeout |
number |
Não |
1000 |
Tempo limite para comandos do Redis. Unidade: milissegundos. |
A tabela a seguir descreve os campos de configuração do tipo Logout.
|
Campo |
Tipo de dados |
Obrigatório |
Valor padrão |
Descrição |
|
key_prefix |
string |
Não |
higress_jwt_logout_ |
Prefixo da chave armazenada no Redis. |
|
key |
array of string |
Não |
["jti"] |
Chave no payload do JWT que identifica um JWT. Se a mesma chave aparecer em múltiplos payloads de JWT, os JWTs serão tratados como idênticos. Caso a chave esteja ausente no payload do JWT, um erro 401 invalid token será retornado. |
|
path |
string |
Não |
/jwt_logout |
String usada para corresponder ao sufixo do caminho da URL. Se houver correspondência, o JWT da requisição atual será invalidado e não poderá mais ser usado. |
|
error_status |
number |
Não |
401 |
Código de status HTTP retornado quando um JWT com logoff é usado. |
|
error_body |
string |
Não |
'{"message":"invalid token"}' |
Corpo da resposta retornado quando um JWT com logoff é usado. |
|
ttl |
number |
Não |
- |
Tempo de vida (TTL) da chave armazenada no Redis. Unidade: segundos. Determina por quanto tempo o JWT permanece invalidado após o logoff. Se não especificado, o TTL será igual ao valor do campo exp no payload menos o horário atual. Caso o payload não contenha o campo exp, o TTL padrão será de 86.400 segundos (24 horas). |
A tabela a seguir descreve os campos de configuração do tipo Login.
|
Campo |
Tipo de dados |
Obrigatório |
Valor padrão |
Descrição |
|
key_prefix |
string |
Não |
higress_jwt_logout_ |
Prefixo da chave armazenada no Redis. |
|
key |
array of string |
Não |
["iss","aud","sub"] |
Identificador de login em dispositivo único no payload do JWT. Se o JWT da requisição atual contiver os mesmos valores de campo que um JWT autenticado anteriormente, mas não for exatamente o mesmo JWT, a requisição será rejeitada como login duplicado. O acesso é negado até que a chave do Redis para o JWT autenticado expire. Se o campo estiver ausente no payload do JWT, um erro 401 invalid token será retornado. |
|
path |
string |
Não |
/jwt_login |
String usada para corresponder ao sufixo do caminho da URL. Se houver correspondência, o JWT da requisição atual se tornará o token de sessão ativo, e qualquer JWT autenticado anteriormente com as mesmas características de payload será invalidado. |
|
error_status |
number |
Não |
403 |
Código de status HTTP retornado para tentativas de login duplicado. |
|
error_body |
string |
Não |
'{"message":"already login on other device"}' |
Corpo da resposta retornado para tentativas de login duplicado. |
|
ttl |
number |
Não |
- |
TTL da chave armazenada no Redis. Unidade: segundos. Determina o período de validade de um JWT de login em dispositivo único. Se não especificado, o TTL será igual ao valor do campo exp no payload menos o horário atual. Caso o payload não contenha o campo exp, o TTL padrão será de 86.400 segundos (24 horas). |
Exemplos de configuração
Usar uma instância do ApsaraDB for Redis
Crie uma instância do ApsaraDB for Redis. Para mais informações, consulte Visão geral.
Ative as configurações de logoff e login. Ao processar uma requisição, o plug-in gera uma requisição de leitura do Redis para verificar a configuração de logoff e duas requisições de leitura para verificar a configuração de login. Em casos raros, como logoff ou primeiro login, duas requisições adicionais de escrita no Redis são geradas. Para estimar a capacidade da instância do ApsaraDB for Redis, multiplique o throughput das requisições processadas pelo plug-in por 2.
Após configurar a instância do ApsaraDB for Redis, obtenha o endpoint da VPC da instância. Exemplo: r-xxxxxxx.redis.rds.aliyuncs.com.
Adicione um serviço. Selecione DNS Domain Name na lista suspensa Service Source, insira o número da porta do Redis (geralmente 6379) no campo Service Port, insira o endpoint da VPC no campo Domain Names e selecione Disabled na lista suspensa TLS Mode. Para mais informações, consulte Criar um serviço.
-
Adicione o seguinte conteúdo à configuração do plug-in para conectar-se à instância do ApsaraDB for Redis:
redis: service: redis.dns port: 6379Se você configurar uma senha para o serviço Redis, adicione o seguinte conteúdo:
redis: service: redis.dns port: 6379 password: ****** # Enter the password that you specify.
Implementar o recurso de logoff de JWT
Cenários de uso
Os JWTs são tokens sem estado que permanecem válidos até a expiração. Use o plug-in jwt-logout para forçar a invalidação de um JWT específico antes que ele expire.
Como funciona
Se o sufixo do caminho da requisição corresponder ao caminho na configuração do plug-in, o mecanismo de logoff será acionado. A instância do ApsaraDB for Redis registra o JWT para o logoff. A chave armazenada na instância do ApsaraDB for Redis consiste no prefixo configurado e na chave e valor extraídos do payload do JWT atual.
Caso o payload do JWT transportado na requisição corresponda às características da chave armazenada na instância do ApsaraDB for Redis, o sistema considera o JWT atual inválido e rejeita a requisição.
O tempo de expiração padrão da chave armazenada na instância do ApsaraDB for Redis é calculado com base no campo
expdo payload do JWT. Isso significa que a chave pode permanecer armazenada até que o JWT expire.
A chave de logoff recomendada por padrão pelo plug-in é ["jti"], onde
jtié o campo do payload que identifica exclusivamente um JWT. Conforme especificado nos padrões de JWT, se o valorjtino payload do JWT for igual ao registrado na instância do ApsaraDB for Redis, todo o JWT será exatamente o mesmo. Portanto, o jti pode ser usado para marcar o status de logoff de um JWT.Regra de concatenação da chave armazenada na instância do ApsaraDB for Redis:
<key_prefix><PayloadKey>##<PayloadValue>. PayloadKey é uma lista de chaves delimitadas por sinais de cerquilha (#). PayloadValue é uma lista de valores de chave delimitados por sinais de cerquilha (#). Exemplo:higress_jwt_logout_jti#iss##xxxxx#abcde.Este plug-in só pode invalidar o token transportado na requisição atual durante o logoff. Você também pode especificar manualmente o token no console do ApsaraDB for Redis com base na regra de concatenação de chave anterior. Se o gateway detectar que a chave existe na instância do ApsaraDB for Redis, ele rejeitará a requisição de acesso baseada no JWT relevante.
Exemplo
Configuração do plug-in:
redis:
service: redis.dns
port: 6379
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
logout:
path: "/jwt_logout"
key: ["jti"]
error_status: 401
error_body: |
{"message":"invalid token"}
-
Acione um logoff.
curl http://xxx.hello.com/test/jwt_logout -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJ4eHh4IiwiaXNzIjoiYWJjZCIsInN1YiI6InRlc3QiLCJhdWQiOiJ3d3cudGVzdC5jb20iLCJpYXQiOjE2NjU2NjA1MjcsImV4cCI6MTg2NTY3MzgxOX0.tmKF6qc1mOWNyCCzBOT2XKNoEGeEgr3EbhTKAQfq1io' # The following result is returned: {"message": "logout success"}Payload do token:
{ "jti": "xxxx", "iss": "abcd", "sub": "test", "aud": "www.test.com", "iat": 1665660527, "exp": 1865673819 }Neste caso, a chave
higress_jwt_logout_jti##xxxxé armazenada na instância do ApsaraDB for Redis. -
Após o logoff, o acesso baseado no JWT não é permitido.
curl http://xxx.hello.com/test/abc -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJ4eHh4IiwiaXNzIjoiYWJjZCIsInN1YiI6InRlc3QiLCJhdWQiOiJ3d3cudGVzdC5jb20iLCJpYXQiOjE2NjU2NjA1MjcsImV4cCI6MTg2NTY3MzgxOX0.tmKF6qc1mOWNyCCzBOT2XKNoEGeEgr3EbhTKAQfq1io' # The following result is returned: {"message":"invalid token"}
Implementar logoff forçado com base em JWTs
Cenários de uso
Quando uma conta faz login em vários dispositivos com autenticação JWT, diferentes JWTs podem ser emitidos para cada dispositivo. Use o plug-in jwt-logout para impor o login em dispositivo único, garantindo que apenas uma sessão permaneça ativa por vez.
Como funciona
Ao iniciar uma requisição, o sistema extrai o JWT da requisição atual e compõe uma chave do Redis com base no prefixo configurado e na chave e valor extraídos do payload do JWT atual. Em seguida, o sistema consulta o valor correspondente à chave na instância do ApsaraDB for Redis. Se o valor for inconsistente com o valor da chave do JWT atual, o sistema rejeita a requisição de acesso.
Se o valor não existir, o JWT atual será gravado na chave do Redis. O tempo de expiração padrão da chave na instância do ApsaraDB for Redis é calculado com base no campo
expdo payload do JWT. Isso significa que a chave pode permanecer armazenada até que o JWT expire. Durante o período de validade do JWT, requisições de acesso baseadas em outros JWTs que tenham as mesmas características de payload não serão permitidas.Caso o sufixo da requisição corresponda ao
pathna configuração do plug-in, o mecanismo de logoff forçado será acionado e o JWT atual será gravado no valor da chave associada do Redis. O JWT usado para um login bem-sucedido e que possua as mesmas características de payload será invalidado no momento do logoff. Isso garante o login em dispositivo único.
A chave de login recomendada por padrão pelo plug-in é ["iss","aud","sub"]. Na chave,
issindica o emissor do JWT,audindica o cenário de uso do JWT esubindica o assunto do JWT. Na maioria dos casos, essa combinação garante o login em dispositivo único.Regra de concatenação da chave armazenada na instância do ApsaraDB for Redis:
<key_prefix><PayloadKey>##<PayloadValue>. PayloadKey é uma lista de chaves delimitadas por sinais de cerquilha (#). PayloadValue é uma lista de valores de chave delimitados por sinais de cerquilha (#). Exemplo:higress_jwt_login_iss#aud#sub##xxxxx#abcde#fffff.Para JWTs que possuem as mesmas características de payload, o primeiro JWT usado nas requisições é automaticamente gravado na instância do ApsaraDB for Redis. Isso ajuda a implementar o recurso de login em dispositivo único do plug-in. Portanto, não é necessário chamar a operação de API para login forçado. A chamada à operação de API é necessária apenas quando o logoff forçado for exigido em cenários de login.
Ao acionar a lógica de logoff, a chave de login do JWT atual armazenada na instância do ApsaraDB for Redis é limpa. Essa regra também se aplica quando você ativa o recurso de logoff de JWT.
Exemplo
Configuração do plug-in:
redis:
service: redis.dns
port: 6379
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
login:
path: "/jwt_login"
key: ["iss","aud","sub"]
error_status: 403
error_body: |
{"message":"already login on other device"}
-
Execute uma operação de login bem-sucedida.
curl http://xxx.hello.com/test/abc -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJ6enp6IiwiaXNzIjoiYWJjZCIsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6InRlc3QiLCJpYXQiOjE2NjU2NjA1MjcsImV4cCI6MTg2NTY3MzgxOX0.WljMr5ucxfLF8SmeaaL25c0QG3IX04HoD0als9gglYg'Payload do token:
{ "jti": "zzzz", "iss": "abcd", "aud": "www.example.com", "sub": "test", "iat": 1665660527, "exp": 1865673819 }Neste caso, a chave
higress_jwt_login_iss#aud#sub##abcd#www.example.com#abcdé armazenada na instância do ApsaraDB for Redis e o valor é igual ao do JWT atual. -
Rejeite uma requisição de acesso quando o payload do JWT na requisição tiver as mesmas características de payload.
curl http://xxx.hello.com/test/abc -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJqdGkiOiJ5eXl5eSIsImlzcyI6ImFiY2QiLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJ0ZXN0IiwiaWF0IjoxNjY1NjYwNTI5LCJleHAiOjE4NjU2NzM4MTl9.6vi6eKPWSKHQxfzBPrj3-SWI4Q5zGtWhqp38JIN3FEo' # The following result is returned: {"message":"already login on other device"}Payload do token:
{ "jti": "yyyyy", "iss": "abcd", "aud": "www.example.com", "sub": "test", "iat": 1665660527, "exp": 1865673819 }As características do payload são iguais para o JWT atual e o JWT da Etapa 1. No entanto, o campo não característico
jtidifere entre os dois JWTs. Por isso, a requisição de acesso baseada no JWT é rejeitada. -
Realize um login forçado.
curl http://xxx.hello.com/test/jwt_login -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJqdGkiOiJ5eXl5eSIsImlzcyI6ImFiY2QiLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJ0ZXN0IiwiaWF0IjoxNjY1NjYwNTI5LCJleHAiOjE4NjU2NzM4MTl9.6vi6eKPWSKHQxfzBPrj3-SWI4Q5zGtWhqp38JIN3FEo' # The following result is returned: {"message":"login success"}Neste caso, a chave
higress_jwt_login_iss#aud#sub##abcd#www.example.com#abcdé armazenada na instância do ApsaraDB for Redis e o valor é substituído pelo valor do JWT atual.Use o JWT atual para acessar a página com sucesso.
curl http://xxx.hello.com/test/abc -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJqdGkiOiJ5eXl5eSIsImlzcyI6ImFiY2QiLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJ0ZXN0IiwiaWF0IjoxNjY1NjYwNTI5LCJleHAiOjE4NjU2NzM4MTl9.6vi6eKPWSKHQxfzBPrj3-SWI4Q5zGtWhqp38JIN3FEo'Se você usar o JWT da Etapa 1, uma mensagem de falha será retornada.
curl http://xxx.hello.com/test/abc -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJqdGkiOiJ4eHh4IiwiaXNzIjoiYWJjZCIsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6InRlc3QiLCJpYXQiOjE2NjU2NjA1MjcsImV4cCI6MTg2NTY3MzgxOX0.P0WtBTHJzUJvklu9q8XSRszfPbgojrZHg7t4ZaYfKGo' # The following result is returned: {"message":"already login on other device"}
Códigos de erro
|
Código de status HTTP |
Mensagem de erro |
Motivo |
|
401 |
invalid token |
Nenhum JWT foi fornecido no cabeçalho da requisição, o formato do JWT é inválido, ou o JWT expirou ou foi invalidado. |
|
500 |
redis server error |
A instância do ApsaraDB for Redis está inacessível ou a conexão atingiu o tempo limite. |