first commit
This commit is contained in:
172
app/Common/Services/JWTService.php
Executable file
172
app/Common/Services/JWTService.php
Executable file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
namespace Zampet\Services;
|
||||
|
||||
use DateTime;
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
use Zampet\Config\Config;
|
||||
|
||||
class JWTService {
|
||||
private static string $issuer;
|
||||
private static int $expiration;
|
||||
private static string $algorithm;
|
||||
private static string $secretKey;
|
||||
|
||||
/**
|
||||
* Inicializa as configurações para a geração de tokens JWT.
|
||||
*
|
||||
* Este método estático carrega as informações necessárias para a criação e validação de tokens
|
||||
* JWT (JSON Web Tokens) a partir de uma classe de configuração (`Config`). Ele busca a chave secreta,
|
||||
* o algoritmo de criptografia, o emissor e o tempo de expiração definidos para a aplicação
|
||||
* e os armazena como propriedades estáticas da classe.
|
||||
*
|
||||
* Este método deve ser chamado antes de qualquer operação que envolva a geração ou
|
||||
* verificação de tokens, garantindo que as configurações estejam prontas para uso.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(): void {
|
||||
$Config = Config::getInstance();
|
||||
self::$issuer = $Config->get(key: 'JWT_ISSUER');
|
||||
self::$algorithm = $Config->get(key: 'JWT_ALGO');
|
||||
self::$secretKey = $Config->get(key: 'JWT_SECRET');
|
||||
self::$expiration = $Config->get(key: 'JWT_EXPIRATION');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gera um token JWT com base em um payload e nas configurações da aplicação.
|
||||
*
|
||||
* Este método cria um token JWT válido a partir de um array de dados fornecido (`$payload`),
|
||||
* adicionando automaticamente metadados essenciais como a data de emissão, a data de expiração e o emissor.
|
||||
*
|
||||
* O processo detalhado inclui:
|
||||
* 1. **Inicialização:** Chama o método `init()` para garantir que todas as configurações do JWT
|
||||
* (chave secreta, algoritmo, etc.) estejam carregadas.
|
||||
* 2. **Definição dos Timestamps:** Calcula o timestamp de emissão (`iat`) e o de expiração (`exp`)
|
||||
* com base no tempo de expiração definido nas configurações.
|
||||
* 3. **Montagem do Payload:** Adiciona as chaves `iat`, `exp` e `iss` (emissor) ao array `$payload`.
|
||||
* 4. **Codificação do Token:** Utiliza a biblioteca `JWT::encode()` para codificar o payload
|
||||
* com a chave secreta e o algoritmo definidos nas configurações.
|
||||
*
|
||||
* @param array $payload Um array associativo contendo os dados personalizados que serão incluídos no token.
|
||||
* @return string O token JWT gerado como uma string.
|
||||
*/
|
||||
public static function generateToken(array $payload): string {
|
||||
// Carrega as informações do arquivo de configuração
|
||||
self::init();
|
||||
|
||||
// Define data de emissão e calcula a data de expiração do token
|
||||
$expiration = self::$expiration;
|
||||
$createdAt = (new DateTime())->getTimestamp();
|
||||
$expireAt = (new DateTime())->modify(modifier: "+{$expiration} seconds")->getTimestamp();
|
||||
|
||||
// Adiciona data de criação ao payload
|
||||
$payload['iat'] = $createdAt;
|
||||
// Adiciona a expiração ao payload
|
||||
$payload['exp'] = $expireAt;
|
||||
// Adiciona o emissor ao payload
|
||||
$payload['iss'] = self::$issuer;
|
||||
|
||||
return JWT::encode(payload: $payload, key: self::$secretKey, alg: self::$algorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Valida um token JWT e retorna o objeto de payload decodificado.
|
||||
*
|
||||
* Este método estático decodifica e valida um token JWT fornecido, utilizando
|
||||
* a chave secreta e o algoritmo de criptografia configurados na classe.
|
||||
*
|
||||
* #### Como funciona:
|
||||
* 1. Recebe o token (`$token`) que precisa ser validado.
|
||||
* 2. Usa a biblioteca `JWT::decode()` para decodificar o token.
|
||||
* 3. Para a decodificação, ele cria uma nova instância de `Key`, passando
|
||||
* a chave secreta (`self::$secretKey`) e o algoritmo (`self::$algorithm`).
|
||||
* Isso garante que o token só será decodificado se tiver sido assinado com a chave e algoritmo corretos.
|
||||
* 4. Se o token for válido e a assinatura corresponder, a função retorna o payload do token
|
||||
* como um objeto.
|
||||
* 5. Se o token for inválido (por exemplo, assinatura incorreta, expirado ou formato incorreto),
|
||||
* a biblioteca JWT lançará uma exceção, que a função que chama este método deve capturar.
|
||||
*
|
||||
* @param string $token O token JWT a ser validado e decodificado.
|
||||
* @return object O objeto de payload decodificado do token.
|
||||
*/
|
||||
public static function validateToken(string $token): object {
|
||||
// Inicializa as configurações se ainda não estiverem definidas
|
||||
if (!isset(self::$secretKey)) {
|
||||
self::init();
|
||||
}
|
||||
|
||||
return JWT::decode(jwt: $token, keyOrKeyArray: new Key(keyMaterial: self::$secretKey, algorithm: self::$algorithm));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodifica um token JWT e retorna seu payload como um array associativo.
|
||||
*
|
||||
* Este método estático é responsável por decodificar e validar um token JWT. Antes de decodificar, ele garante que as configurações de chave secreta e algoritmo (`self::$secretKey` e `self::$algorithm`) já foram inicializadas chamando o método `init()` se necessário.
|
||||
*
|
||||
* #### Como Funciona:
|
||||
* 1. **Inicialização:** Primeiro, verifica se a chave secreta (`self::$secretKey`) já foi carregada. Se não, ele chama `self::init()` para carregar as configurações de um arquivo externo.
|
||||
* 2. **Decodificação:** Usa a biblioteca `Firebase\JWT\JWT::decode()` para decodificar o token fornecido. O método passa a chave secreta e o algoritmo para verificar a assinatura do token.
|
||||
* 3. **Conversão:** O payload decodificado, que por padrão é um objeto, é convertido para uma string JSON e depois para um array associativo, o que facilita o acesso aos dados.
|
||||
* 4. **Retorno:** Retorna o payload do token como um array associativo. Se o token for inválido (assinatura incorreta, expirado, etc.), a biblioteca JWT lançará uma exceção, que a função que chama este método deve capturar.
|
||||
*
|
||||
* @param string $token O token JWT a ser decodificado.
|
||||
* @return array O payload do token decodificado como um array associativo.
|
||||
*/
|
||||
public static function decode(string $token): array {
|
||||
// Inicializa as configurações se ainda não estiverem definidas
|
||||
if (!isset(self::$secretKey)) {
|
||||
self::init();
|
||||
}
|
||||
|
||||
$decoded = JWT::decode(jwt: $token, keyOrKeyArray: new Key(keyMaterial: self::$secretKey, algorithm: self::$algorithm));
|
||||
return json_decode(json: json_encode(value: $decoded), associative: true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extrai o token de autenticação do tipo 'Bearer' do cabeçalho da requisição HTTP.
|
||||
*
|
||||
* Este método estático é um utilitário projetado para buscar um token JWT (JSON Web Token)
|
||||
* que é enviado no cabeçalho `Authorization`. Ele é robusto o suficiente para verificar
|
||||
* o token em diferentes superglobais do PHP, dependendo da configuração do servidor web.
|
||||
*
|
||||
* #### Fluxo de Busca:
|
||||
* 1. Primeiro, tenta obter o cabeçalho `Authorization` diretamente da superglobal `$_SERVER`.
|
||||
* 2. Em seguida, tenta a chave `HTTP_AUTHORIZATION`, que é um nome de variável comum em alguns ambientes.
|
||||
* 3. Por último, usa a função `apache_request_headers()` para tentar obter o cabeçalho, caso as
|
||||
* superglobais não estejam preenchidas. Isso garante compatibilidade com servidores Apache onde o cabeçalho pode não ser exposto.
|
||||
* 4. Se o cabeçalho for encontrado, o valor é sanitizado (removendo espaços em branco) e a busca continua.
|
||||
* 5. Se nenhum cabeçalho de autenticação for encontrado, o método retorna `null`.
|
||||
*
|
||||
* #### Extração do Token:
|
||||
* - Depois de encontrar o cabeçalho, o método usa uma expressão regular (`/Bearer\s(\S+)/`)
|
||||
* para verificar se o valor do cabeçalho está no formato `Bearer <token>`.
|
||||
* - Se o padrão corresponder, ele extrai e retorna a string do token.
|
||||
* - Se o padrão não corresponder, o método retorna `null`.
|
||||
*
|
||||
* @return string|null O token de autenticação do tipo 'Bearer' como uma string, ou `null`
|
||||
* se o cabeçalho não for encontrado ou não estiver no formato esperado.
|
||||
*/
|
||||
public static function getBearerToken(): ?string {
|
||||
if (!isset(self::$secretKey)) {
|
||||
self::init();
|
||||
}
|
||||
|
||||
// Tenta pegar o header de todas as fontes possíveis
|
||||
$headers = $_SERVER['Authorization'] ?? $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? (function_exists(function: 'apache_request_headers') ? apache_request_headers() : []);
|
||||
|
||||
// Se veio do apache_request_headers, normaliza chaves
|
||||
if (is_array(value: $headers)) {
|
||||
$headers = array_change_key_case(array: $headers, case: CASE_LOWER);
|
||||
$headers = $headers['authorization'] ?? '';
|
||||
}
|
||||
|
||||
$headers = trim(string: $headers);
|
||||
|
||||
if ($headers && preg_match(pattern: '/Bearer\s(\S+)/', subject: $headers, matches: $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user