30 de nov. de 2025·5 min de leitura

Guardas de rota no Vue 3 para acesso por papéis: padrões práticos

Guardas de rota do Vue 3 para controle por papéis explicados com padrões práticos: regras em route meta, redirecionamentos seguros, páginas 401/403 amigáveis e como evitar vazamentos de dados.

Guardas de rota no Vue 3 para acesso por papéis: padrões práticos

O que os route guards realmente resolvem (e o que não resolvem)

Route guards fazem uma coisa bem: controlam a navegação. Eles decidem se alguém pode entrar em uma rota e para onde enviar essa pessoa se não puder. Isso melhora a experiência, mas não é a mesma coisa que segurança.

Esconder um item de menu é apenas uma pista, não autorização. Pessoas ainda podem digitar uma URL, atualizar em um link profundo ou abrir um favorito. Se sua única proteção é “o botão não aparece”, você não tem proteção.

Guards brilham quando você quer que o app se comporte de forma consistente enquanto bloqueia páginas que não deveriam ser exibidas, como áreas de admin, ferramentas internas ou portais de clientes com base em papéis.

Guards ajudam você a:

  • Bloquear páginas antes que elas renderizem
  • Redirecionar para login ou um padrão seguro
  • Mostrar uma tela 401/403 clara em vez de uma view quebrada
  • Evitar loops de navegação acidentais

O que os guards não podem fazer é proteger dados por si só. Se uma API retornar dados sensíveis para o navegador, um usuário ainda pode chamar esse endpoint diretamente (ou inspecionar respostas nas dev tools) mesmo que a página esteja bloqueada. Autorização real precisa acontecer também no servidor.

Um bom alvo é cobrir ambos os lados: bloquear páginas e bloquear dados. Se um agente de suporte abrir uma rota só para admins, o guard deve interromper a navegação e mostrar “Acesso negado”. Separadamente, seu backend deve recusar chamadas de API exclusivas para admins, assim dados restritos nunca são retornados.

Escolha um modelo simples de papéis e permissões

Controle de acesso fica confuso quando você começa com uma longa lista de papéis. Comece com um conjunto pequeno que as pessoas realmente entendam, depois adicione permissões mais finas só quando sentir dor de verdade.

Uma divisão prática é:

  • Papéis descrevem quem alguém é no seu app.
  • Permissões descrevem o que essa pessoa pode fazer.

Para a maioria das ferramentas internas, três papéis cobrem muito:

  • admin: gerencia usuários e configurações, vê todos os dados
  • support: lida com registros e respostas dos clientes, mas não com configurações do sistema
  • viewer: acesso somente leitura a telas aprovadas

Decida cedo de onde vêm os papéis. Claims em token (JWT) são rápidos para guards, mas podem ficar desatualizados até serem atualizados. Buscar o perfil do usuário no início do app é sempre mais atual, mas seus guards precisam esperar até que essa requisição termine.

Também separe claramente os tipos de rota: rotas públicas (abertas a todos), rotas autenticadas (exigem sessão) e rotas restritas (exigem um papel ou permissão).

Defina regras de acesso com meta de rota

A forma mais limpa de expressar acesso é declará-lo na própria rota. O Vue Router permite anexar um objeto meta a cada registro de rota para que seus guards possam ler depois. Isso mantém as regras próximas às páginas que eles protegem.

Escolha um formato simples para meta e mantenha-o em toda a aplicação.

const routes = [
  {
    path: "/admin",
    component: () => import("@/pages/AdminLayout.vue"),
    meta: { requiresAuth: true, roles: ["admin"] },
    children: [
      {
        path: "users",
        component: () => import("@/pages/AdminUsers.vue"),
        // inherits requiresAuth + roles from parent
      },
      {
        path: "audit",
        component: () => import("@/pages/AdminAudit.vue"),
        meta: { permissions: ["audit:read"] },
      },
    ],
  },
  {
    path: "/tickets",
    component: () => import("@/pages/Tickets.vue"),
    meta: { requiresAuth: true, permissions: ["tickets:read"], readOnly: true },
  },
]

Para rotas aninhadas, decida como as regras se combinam. Na maioria dos apps, os filhos devem herdar os requisitos do pai. No seu guard, verifique todos os registros de rota correspondentes (não apenas to.meta) para que regras do pai não sejam puladas.

Um detalhe que economiza tempo depois: distinga entre “pode ver” e “pode editar”. Uma rota pode ser visível para support e admins, mas as edições devem ser desabilitadas para support. Uma flag readOnly: true em meta pode dirigir o comportamento da UI (desabilitar ações, esconder botões destrutivos) sem fingir que é segurança.

Prepare o estado de auth para que os guards se comportem de forma confiável

A maioria dos bugs de guard vem de um problema: o guard roda antes do app saber quem é o usuário.

Trate auth como uma pequena máquina de estados e faça dela a única fonte de verdade. Você quer três estados claros:

  • unknown: o app acabou de iniciar, sessão ainda não checada
  • logged out: cheque de sessão finalizado, sem usuário válido
  • logged in: usuário carregado, papéis/permissões disponíveis

A regra: nunca leia papéis enquanto o auth estiver unknown. É assim que você tem flashes de telas protegidas ou redirects-surpresa para o login.

Decida como o refresh de sessão funciona

Escolha uma estratégia de refresh e mantenha-a previsível (por exemplo: ler um token, chamar um endpoint “who am I”, setar o usuário).

Um padrão estável fica assim:

  • No carregamento do app, defina auth como unknown e dispare uma única requisição de refresh
  • Resolva os guards apenas depois que o refresh terminar (ou expirar)
  • Cacheie o usuário em memória, não em meta de rota
  • Em falha, defina auth como logged out
  • Exponha uma promise ready (ou similar) que os guards possam aguardar

Com isso, a lógica do guard fica simples: espere o auth ficar pronto e então decida o acesso.

Passo a passo: implementar autorização a nível de rota

Previna vazamento de dados
Faça com que requisições sensíveis dependam de checagens no backend que você define visualmente no AppMaster.
Proteger dados

Uma abordagem limpa é manter a maior parte das regras em um guard global e usar guards por rota apenas quando uma rota realmente precisar de lógica especial.

1) Adicione um guard global beforeEach

// router/index.js
router.beforeEach(async (to) => {
  const auth = useAuthStore()

  // Step 2: wait for auth initialization when needed
  if (!auth.ready) await auth.init()

  // Step 3: check authentication, then roles/permissions
  if (to.meta.requiresAuth && !auth.isAuthenticated) {
    return { name: 'login', query: { redirect: to.fullPath } }
  }

  const roles = to.meta.roles
  if (roles && roles.length > 0 && !roles.includes(auth.userRole)) {
    return { name: 'forbidden' } // 403
  }

  // Step 4: allow navigation
  return true
})

Isso cobre a maioria dos casos sem espalhar checagens pelos componentes.

Quando beforeEnter é a melhor opção

Use beforeEnter quando a regra for genuinamente específica da rota, como “apenas o dono do ticket pode abrir esta página” e depender de to.params.id. Mantenha-o curto e reutilize a mesma store de auth para que o comportamento seja consistente.

Redirecionamentos seguros sem abrir brechas

Estenda controle de acesso ao mobile
Crie apps nativos iOS e Android com as mesmas regras de papéis por trás das suas APIs.
Criar mobile

Redirecionamentos podem silenciosamente desfazer seu controle de acesso se você tratá-los como confiáveis.

O padrão comum é: quando um usuário está desconectado, leve-o ao login e inclua um returnTo na query. Depois do login, leia isso e navegue para lá. O risco são open redirects (enviar usuários para lugares não intencionados) e loops.

Mantenha o comportamento simples:

  • Usuários desconectados vão para Login com returnTo definido para o caminho atual.
  • Usuários conectados mas não autorizados vão para uma página Forbidden dedicada (não para Login).
  • Só permita valores de returnTo internos que você reconheça.
  • Adicione uma verificação de loop para nunca redirecionar para o mesmo lugar.
const allowedReturnTo = (to) => {
  if (!to || typeof to !== 'string') return null
  if (!to.startsWith('/')) return null
  // optional: only allow known prefixes
  if (!['/app', '/admin', '/tickets'].some(p => to.startsWith(p))) return null
  return to
}

router.beforeEach((to) => {
  if (!auth.isReady) return false

  if (!auth.isLoggedIn && to.name !== 'Login') {
    return { name: 'Login', query: { returnTo: to.fullPath } }
  }

  if (auth.isLoggedIn && !canAccess(to, auth.user) && to.name !== 'Forbidden') {
    return { name: 'Forbidden' }
  }
})

Evite vazar dados restritos durante a navegação

O vazamento mais fácil é carregar dados antes de saber se o usuário tem permissão para vê-los.

No Vue, isso frequentemente acontece quando uma página faz fetch em setup() e o guard do router roda um momento depois. Mesmo se o usuário for redirecionado, a resposta pode chegar em um store compartilhado ou piscar na tela.

Uma regra mais segura é: autorize primeiro, depois carregue.

// router guard: authorize before entering the route
router.beforeEach(async (to) => {
  await auth.ready() // ensure roles are known
  const required = to.meta.requiredRole
  if (required && !auth.hasRole(required)) {
    return { name: 'forbidden' }
  }
})

Também fique atento a requisições tardias quando a navegação muda rapidamente. Cancele requisições (por exemplo com AbortController) ou ignore respostas tardias checando um id de requisição.

Cache é outra armadilha comum. Se você armazenar “último registro de cliente carregado” globalmente, uma resposta só para admin pode ser mostrada depois para um não-admin que visite o mesmo shell de tela. Chaveie caches por id do usuário e papel, e limpe módulos sensíveis no logout (ou quando os papéis mudarem).

Alguns hábitos evitam a maioria dos vazamentos:

  • Não busque dados sensíveis até a autorização ser confirmada.
  • Chaveie dados em cache por usuário e papel, ou mantenha-os locais à página.
  • Cancele ou ignore requisições em voo quando a rota mudar.

Fallbacks amigáveis: 401, 403 e not found

Prototipe suas regras de guardas
Modele papéis e permissões, depois teste deep links e redirecionamentos em um único projeto.
Experimentar o AppMaster

Os caminhos do “não” importam tanto quanto os caminhos do “sim”. Páginas de fallback bem feitas mantêm os usuários orientados e reduzem chamados ao suporte.

401: Login requerido (não autenticado)

Use 401 quando o usuário não estiver logado. Mantenha a mensagem simples: é preciso entrar para continuar. Se você aceita retorno à página original após o login, valide o caminho de retorno para que não aponte para fora do seu app.

403: Acesso negado (autenticado, mas não permitido)

Use 403 quando o usuário estiver logado mas sem permissão. Mantenha o tom neutro e evite dar pistas sobre detalhes sensíveis.

Uma página 403 sólida costuma ter um título claro (“Acesso negado”), uma frase explicativa e um próximo passo seguro (voltar ao dashboard, contatar um admin, trocar de conta se suportado).

404: Não encontrado

Trate 404 separadamente de 401/403. Caso contrário, pessoas assumem que não têm permissão quando a página simplesmente não existe.

Erros comuns que quebram o controle de acesso

A maioria dos bugs de controle de acesso são deslizes simples de lógica que aparecem como loops de redirect, flashes da página errada ou usuários presos.

Os culpados mais comuns:

  • Tratar UI escondida como “segurança”. Sempre aplique papéis no router e na API.
  • Ler papéis de estado desatualizado após logout/login.
  • Redirecionar usuários não autorizados para outra rota protegida (loop instantâneo).
  • Ignorar o momento “auth ainda carregando” ao atualizar.
  • Confundir 401 com 403, o que confunde usuários.

Um exemplo realista: um agente de suporte faz logout e um admin faz login no mesmo computador compartilhado. Se seu guard ler um papel em cache antes da nova sessão ser confirmada, você pode bloquear o admin incorretamente ou, pior, permitir acesso que não deveria por um breve momento.

Checklist rápido antes do deploy

Adicione autorização real no backend
Desenhe APIs e regras de negócio no AppMaster para que os dados fiquem protegidos, não apenas as rotas.
Criar backend

Faça uma checagem curta focando nos momentos onde o controle de acesso normalmente quebra: redes lentas, sessões expiradas e URLs de favoritos.

  • Cada rota protegida tem meta explícito com requisitos.
  • Guards lidam com o estado de carregamento do auth sem piscar UI protegida.
  • Usuários não autorizados caem em uma página 403 clara (não em um retorno confuso para a home).
  • Qualquer redirecionamento de “retornar para” é validado e não cria loops.
  • Chamadas de API sensíveis rodam apenas depois da autorização confirmada.

Depois, teste um cenário do começo ao fim: abra uma URL protegida em uma nova aba enquanto estiver deslogado, faça login como um usuário básico e confirme que você ou chega na página pretendida (se permitido) ou vê um 403 limpo com um próximo passo.

Exemplo: acesso de support vs admin em um app pequeno

Crie páginas de fallback amigáveis
Crie páginas 401, 403 e 404 rapidamente com os construtores de UI do AppMaster.
Construir UI

Imagine um helpdesk com dois papéis: support e admin. Support pode ler e responder tickets. Admin também pode fazer isso, além de gerenciar faturamento e configurações da empresa.

  • /tickets/:id é permitido para support e admin
  • /settings/billing é permitido apenas para admin

Agora um momento comum: um agente de support abre um deep link para /settings/billing de um favorito antigo. O guard deve checar o meta da rota antes que a página carregue e bloquear a navegação. Como o usuário está logado mas não tem o papel, ele deve cair em um fallback seguro (403).

Duas mensagens importam:

  • Login requerido (401): “Por favor, entre para continuar.”
  • Acesso negado (403): “Você não tem acesso às Configurações de Faturamento.”

O que não deve acontecer: o componente de faturamento monta ou os dados de faturamento são buscados, nem que seja por um breve momento.

Mudanças de papel no meio da sessão são outro caso de borda. Se alguém for promovido ou rebaixado, não confie apenas no menu. Rechecque papéis na navegação e decida como tratar páginas ativas: atualize o estado de auth quando o perfil mudar ou detecte mudanças de papel e redirecione de páginas que não sejam mais permitidas.

Próximos passos: mantenha regras de acesso fáceis de manter

Uma vez que os guards funcionem, o risco maior é a deriva: uma rota nova vai ao ar sem meta, um papel é renomeado e as regras ficam inconsistentes.

Transforme suas regras em um pequeno plano de testes que você rode sempre que adicionar uma rota:

  • Como Guest: abra rotas protegidas e confirme que você vai para o login sem ver conteúdo parcial.
  • Como User: abra uma página que você não deveria acessar e confirme que recebe um 403 claro.
  • Como Admin: teste deep links copiados da barra de endereço.
  • Para cada papel: atualize em uma rota protegida e confirme que o resultado é estável.

Se quiser uma camada extra de segurança, adicione uma view (só em dev) ou uma saída no console que liste rotas e seus requisitos meta, assim regras faltantes se destacam imediatamente.

Se você está construindo ferramentas internas ou portais com AppMaster (appmaster.io), pode aplicar a mesma abordagem: mantenha os route guards focados na navegação na UI Vue3 e imponha permissões onde a lógica e os dados vivem — no backend.

Escolha uma melhoria e implemente-a de ponta a ponta: reforçar o gate em fetch de dados sensíveis, melhorar a página 403 ou travar o comportamento de redirecionamentos. Pequenos ajustes são os que resolvem a maioria dos bugs reais no mundo.

FAQ

Guards de rota são segurança de verdade ou só UX?

Route guards controlam navegação, não o acesso a dados. Eles ajudam a bloquear uma página, redirecionar e mostrar um estado 401/403 limpo, mas não impedem que alguém chame sua API diretamente. Sempre aplique as mesmas permissões no backend para que dados restritos nunca sejam retornados.

Por que esconder um item de menu não é suficiente para controle por papéis?

Porque esconder um item de menu altera apenas o que alguém , não o que pode solicitar. Usuários ainda podem digitar uma URL, abrir favoritos ou usar deep links. Você precisa de checagens no router para bloquear a página e de autorização no servidor para bloquear os dados.

Qual é um modelo simples de papéis e permissões para começar?

Comece com um conjunto pequeno que as pessoas entendam e só adicione permissões quando sentir dor real. Uma base comum é admin, support e viewer, e depois acrescente permissões como tickets:read ou audit:read para ações específicas. Separe “quem você é” (role) do que você pode fazer (permission).

Como devo usar `meta` do Vue Router para controle de acesso?

Coloque regras de acesso em meta nos registros de rota, como requiresAuth, roles e permissions. Assim as regras ficam próximas das páginas que protegem e seu guard global se torna previsível. Para rotas aninhadas, verifique todos os registros em to.matched para não pular requiresAuth do pai.

Como lidar com rotas aninhadas para que os filhos herdem restrições do pai?

Leia to.matched e combine os requisitos de todos os registros de rota correspondentes. Assim uma rota filha não contorna o requiresAuth ou roles do pai. Decida uma regra de merge clara desde o início (normalmente: requisitos do pai valem para os filhos).

Como eu evito loops de redirecionamento e flashes de páginas protegidas ao atualizar?

Porque o guard pode rodar antes do app saber quem é o usuário. Trate a autenticação com três estados—unknown, logged out, logged in—e nunca avalie papéis enquanto o auth estiver unknown. Faça os guards aguardarem uma etapa de inicialização (por exemplo, um único pedido “quem sou eu”) antes de decidir.

Quando devo usar um guard global `beforeEach` vs `beforeEnter`?

Prefira um beforeEach global para regras consistentes como “requer login” e “requer papel/permissão”. Use beforeEnter somente quando a regra for realmente específica da rota e depender de params, por exemplo “apenas o dono do ticket pode abrir esta página”. Mantenha ambas as abordagens usando a mesma fonte de verdade do auth.

Como faço redirecionamentos pós-login sem abrir vulnerabilidades de open redirect?

Trate returnTo como input não confiável. Só permita caminhos internos que você reconheça (por exemplo, valores que começam com / e batem com prefixes conhecidos) e acrescente uma verificação contra loops para não redirecionar de volta à mesma rota bloqueada. Usuários desconectados vão para Login; usuários conectados mas sem permissão vão para uma página 403 dedicada.

Como evito vazar dados restritos durante a navegação?

Autorize antes de buscar. Se uma página fizer fetch em setup() e você redirecionar logo depois, a resposta ainda pode chegar em um store ou piscar na tela. Coloque requisições sensíveis atrás de autorização confirmada e cancele ou ignore requisições em andamento quando a rota mudar.

Qual é a forma correta de usar 401 vs 403 vs 404 em uma aplicação Vue?

Use 401 quando o usuário não estiver autenticado, e 403 quando estiver autenticado mas não tiver permissão. Mantenha 404 separado para que as pessoas não pensem que falta permissão quando a rota simplesmente não existe. Fallbacks claros e consistentes reduzem confusão e chamados ao suporte.

Fácil de começar
Criar algo espantoso

Experimente o AppMaster com plano gratuito.
Quando estiver pronto, você poderá escolher a assinatura adequada.

Comece