Pular para o conteúdo principal

Exemplo de Implementação de Refresh no Frontend

JavaScript/TypeScript

class AuthService {
private token: string | null = null;
private refreshTimer: NodeJS.Timeout | null = null;

constructor() {
this.token = localStorage.getItem('token');
this.startRefreshTimer();
}

// Método para fazer refresh do token
async refreshToken(): Promise<boolean> {
try {
const response = await fetch('/seguranca/autenticacao/token/refresh', {
method: 'GET',
headers: {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json'
}
});

if (response.ok) {
const data = await response.json();
this.token = data.token;
localStorage.setItem('token', data.token);

// Reiniciar o timer de refresh
this.startRefreshTimer();

return true;
} else {
// Token inválido ou expirado
this.logout();
return false;
}
} catch (error) {
console.error('Erro ao renovar token:', error);
this.logout();
return false;
}
}

// Timer para refresh automático (a cada 50 minutos)
private startRefreshTimer() {
if (this.refreshTimer) {
clearTimeout(this.refreshTimer);
}

this.refreshTimer = setTimeout(() => {
this.refreshToken();
}, 50 * 60 * 1000); // 50 minutos
}

// Interceptor para requisições HTTP
async makeRequest(url: string, options: RequestInit = {}): Promise<Response> {
const headers = {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json',
...options.headers
};

let response = await fetch(url, { ...options, headers });

// Se receber 401, tentar refresh e refazer a requisição
if (response.status === 401) {
const refreshed = await this.refreshToken();
if (refreshed) {
headers['Authorization'] = `Bearer ${this.token}`;
response = await fetch(url, { ...options, headers });
}
}

return response;
}

logout() {
this.token = null;
localStorage.removeItem('token');
if (this.refreshTimer) {
clearTimeout(this.refreshTimer);
}
// Redirecionar para login
window.location.href = '/login';
}
}

// Uso do serviço
const authService = new AuthService();

// Exemplo de uso em uma requisição
async function buscarProdutos() {
try {
const response = await authService.makeRequest('/api/produtos');
const produtos = await response.json();
return produtos;
} catch (error) {
console.error('Erro ao buscar produtos:', error);
}
}

React Hook

import { useState, useEffect, useCallback } from 'react';

export const useAuth = () => {
const [token, setToken] = useState<string | null>(localStorage.getItem('token'));
const [isRefreshing, setIsRefreshing] = useState(false);

const refreshToken = useCallback(async (): Promise<boolean> => {
if (!token || isRefreshing) return false;

setIsRefreshing(true);
try {
const response = await fetch('/seguranca/autenticacao/token/refresh', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});

if (response.ok) {
const data = await response.json();
setToken(data.token);
localStorage.setItem('token', data.token);
return true;
} else {
logout();
return false;
}
} catch (error) {
console.error('Erro ao renovar token:', error);
logout();
return false;
} finally {
setIsRefreshing(false);
}
}, [token, isRefreshing]);

const logout = useCallback(() => {
setToken(null);
localStorage.removeItem('token');
}, []);

// Refresh automático a cada 50 minutos
useEffect(() => {
if (!token) return;

const interval = setInterval(() => {
refreshToken();
}, 50 * 60 * 1000);

return () => clearInterval(interval);
}, [token, refreshToken]);

return { token, refreshToken, logout, isRefreshing };
};

Axios Interceptor

import axios from 'axios';

const api = axios.create({
baseURL: '/api'
});

// Interceptor para adicionar token nas requisições
api.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});

// Interceptor para tratar 401 e fazer refresh
api.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;

if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;

try {
const response = await axios.get('/seguranca/autenticacao/token/refresh', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});

const { token } = response.data;
localStorage.setItem('token', token);

// Refazer a requisição original
originalRequest.headers.Authorization = `Bearer ${token}`;
return api(originalRequest);
} catch (refreshError) {
// Refresh falhou, redirecionar para login
localStorage.removeItem('token');
window.location.href = '/login';
return Promise.reject(refreshError);
}
}

return Promise.reject(error);
}
);

export default api;

Considerações Importantes

  1. Frequência do Refresh: Configure para renovar antes do token expirar (ex: 50 minutos para token de 1 hora)

  2. Tratamento de Erros: Sempre trate erros de refresh e redirecione para login quando necessário

  3. Estado de Loading: Mostre indicador de loading durante o refresh para melhor UX

  4. Múltiplas Requisições: Evite múltiplos refreshes simultâneos usando flags de controle

  5. Logout Automático: Implemente logout automático quando refresh falha

  6. Permissões Atualizadas: Após refresh bem-sucedido, as permissões estarão atualizadas no novo token