Implementação de Decodificação de API em Projetos Vue com Integração PHP

No desenvolvimento full-stack, é comum que a API seja fornecida pelo backend e requer a configuração de cabeçalhos ou tokens para decodificar os parâmetros corretamente. Isso protege a API contra exposição indevida e ajuda a evitar engenharia reversa maliciosa.

A abordagem front end pode ser implementada com Vue.js ou PHP, embora os parâmetros necessários para a decodificação sejam semelhanttes. Cada tecnologia possui seus próprios métodos de processamento.

Exemplo de implementação com Vue.js, focando no núcleo da lógica:

// Interceptor de solicitação para manipulação pré-envio
// Adiciona tokens, criptografa parâmetros de forma unificada
const interceptorSolicitacao = (configuracao) => {
    if (!configuracao.headers['Content-Type']) {
        configuracao.headers['Content-Type'] = 'application/json;charset=utf-8';
    }
    
    const identificadorDispositivo = gerarHashDispositivo(obterIdentificadorUnico());
    const tokenAutenticacao = localStorage.getItem('tokenAutenticacao') || '';
    const carimboTempo = Date.now();
    
    configuracao.headers["chaveacesso"] = "x7y8z9"; // Assinatura de cabeçalho
    configuracao.headers["idProduto"] = obterPIDLocal()
    configuracao.headers['idioma'] = obterTipoIdioma() === 'pt' ? 'pt' : obterTipoIdioma()
    configuracao.headers['codPais'] = 'BR-BR'
    configuracao.headers['canal'] = 'WEBFRONTEND'
    configuracao.headers['versao'] = '2.1.0'
    configuracao.headers['sistema'] = 'WEBFRONTEND'
    configuracao.headers['fusoHorario'] = '-3'
    configuracao.headers['plataforma'] = obterPlataforma()
    configuracao.headers['codVersao'] = '2.1.0'
    configuracao.headers['tokenDispositivo'] = 'WEBFRONTEND'
    configuracao.headers['sistemaOperacional'] = 'WEBFRONTEND'
    configuracao.headers['deviceId'] = identificadorDispositivo
    configuracao.headers['tokenAutenticacao'] = tokenAutenticacao
    configuracao.headers['ts'] = carimboTempo
    
    if (configuracao.url) {
        configuracao.headers['assinatura'] = calcularAssinatura(configuracao.url, identificadorDispositivo, carimboTempo)
    }
    
    const tokenArmazenado = obterTokenLocalStorage('get', 'TOKEN_APP')
    if (tokenArmazenado) {
        configuracao.headers['tokenAutenticacao'] = tokenArmazenado
    }
    return configuracao;
};

// Interceptor de resposta para processamento pós-resposta
const interceptorResposta = async (resposta) => {
    let dados = resposta.data;
    if (typeof dados === "string") {
        dados = dados ? JSON.parse(dados) : dados;
    } else {
        if (dados.informacao != null && dados.informacao !== "") {
            let dadosCriptografados = decodificarResposta(dados.informacao);
            const dadosCompactados = decodificarBase64(dadosCriptografados)
                .split("")
                .map((caractere) => caractere.charCodeAt(0));
            const arrayCompactado = new Uint8Array(dadosCompactados);
            const dadosDescompactados = descompactarDados(arrayCompactado);
            dados.informacao = new TextDecoder().decode(dadosDescompactados);
            dados.informacao = JSON.parse(dados.informacao);
        }
        switch (dados.codigo) {
            case 2005:
                localStorage.removeItem('tokenAutenticacao');
                localStorage.removeItem('PERFIL_USUARIO');
                return;
        }
    }
    return dados;
};

Exemplo de implementação com PHP, adaptado para integração com WordPress:

<?php
/**
 * Classe proxy para requisições a APIs externas.
 * Encapsula lógica comum: assinatura, cabeçalhos, decodificação.
 */

require_once get_template_directory() . '/api/Funcoes.php';

class ProxyRequisicao
{
    private const URL_BASE_PADRAO = 'http://servidor-api.exemplo.com';
    private $urlBase;

    public function __construct(?string $urlBase = null)
    {
        $this->urlBase = rtrim($urlBase ?: self::URL_BASE_PADRAO, '/');
    }

    public function solicitarGet(string $caminho, string $idDispositivoRaw = '', array $cabecalhosExtras = []): array
    {
        $caminho = $this->normalizarCaminho($caminho);
        $urlCompleta = $this->urlBase . $caminho;
        
        $idDispositivo = $this->resolverIdDispositivo($idDispositivoRaw);
        $carimboTempo = (string) round(microtime(true) * 1000);
        $assinatura = CalculadorAssinatura::gerar($caminho, $idDispositivo, $carimboTempo, 'chave_secreta123');
        
        $cabecalhos = $this->montarCabecalhos($idDispositivo, $carimboTempo, $assinatura, $cabecalhosExtras);
        
        $resposta = wp_remote_request($urlCompleta, [
            'method' => 'GET',
            'headers' => $cabecalhos,
            'timeout' => 45,
        ]);
        
        if (is_wp_error($resposta)) {
            return [
                'status' => 'erro',
                'mensagem' => 'falha_na_requisicao',
                'dados' => ['erro' => $resposta->get_error_message()],
                'timestamp' => (int) $carimboTempo,
            ];
        }
        
        $corpo = wp_remote_retrieve_body($resposta);
        return ProcessadorResposta::decodificar($corpo, $idDispositivo, $carimboTempo, $assinatura);
    }

    public function solicitarPost(string $caminho, string $idDispositivoRaw = '', array $cabecalhosExtras = [], array $dadosPost = []): array
    {
        $caminho = $this->normalizarCaminho($caminho);
        $urlCompleta = $this->urlBase . $caminho;
        
        $idDispositivo = $this->resolverIdDispositivo($idDispositivoRaw);
        $carimboTempo = (string) round(microtime(true) * 1000);
        $assinatura = CalculadorAssinatura::gerar($caminho, $idDispositivo, $carimboTempo, 'chave_secreta123');
        
        $cabecalhos = $this->montarCabecalhos($idDispositivo, $carimboTempo, $assinatura, $cabecalhosExtras);
        
        $resposta = wp_remote_request($urlCompleta, [
            'method' => 'POST',
            'headers' => $cabecalhos,
            'body' => json_encode($dadosPost, JSON_UNESCAPED_UNICODE),
            'timeout' => 45,
        ]);
        
        if (is_wp_error($resposta)) {
            return [
                'status' => 'erro',
                'mensagem' => 'falha_na_requisicao',
                'dados' => ['erro' => $resposta->get_error_message()],
                'timestamp' => (int) $carimboTempo,
            ];
        }
        
        $corpo = wp_remote_retrieve_body($resposta);
        return ProcessadorResposta::decodificar($corpo, $idDispositivo, $carimboTempo, $assinatura);
    }

    private function montarCabecalhos(string $idDispositivo, string $ts, string $assinatura, array $extras = []): array
    {
        $cabecalhosBase = [
            'Accept' => 'application/json',
            'Content-Type' => 'application/json;charset=utf-8',
            'chaveacesso' => 'x7y8z9',
            'idProduto' => 'ABCDEF123',
            'idioma' => 'pt',
            'codPais' => 'BR-BR',
            'canal' => 'WEBFRONTEND',
            'versao' => '2.1.0',
            'sistema' => 'WEBFRONTEND',
            'fusoHorario' => '-3',
            'plataforma' => 'WEBFRONTEND',
            'codVersao' => '2.1.0',
            'tokenDispositivo' => 'WEBFRONTEND',
            'sistemaOperacional' => 'WEBFRONTEND',
            'deviceId' => $idDispositivo,
            'ts' => $ts,
            'assinatura' => $assinatura,
        ];
        
        return array_merge($cabecalhosBase, $extras);
    }

    private function normalizarCaminho(string $caminho): string
    {
        $caminho = trim($caminho);
        return empty($caminho) ? '/' : '/' . ltrim($caminho, '/');
    }

    private function resolverIdDispositivo(string $idRaw): string
    {
        $idRaw = trim($idRaw);
        if (!empty($idRaw)) {
            return hash('sha256', $idRaw);
        }
        return hash('sha256', gethostname() . ($_SERVER['SERVER_ADDR'] ?? '0.0.0.0'));
    }
}

Tags: Vue.js PHP API Security Request Interceptors Response Decryption

Publicado em 6-8 18:59 por Thomas