Implementação de Controle de Estado com Cookies em Java Servlets

Mecanismo de Funcionamento

O protocolo HTTP opera de maneira sem estado, o que significa que cada requisição é tratada independentemente. Para superar essa limitação e manter o contexto do usuário, os desenvolvedores utilizam cookies. Quando o lado do servidor define uma propriedade de resposta adicionando um cabeçalho Set-Cookie, o navegadro armazena esses dados localmente. Em solicitações subsequentes para o mesmo domínio, o cliente transmite automaticamente esses valores através do cabeçalho de requisição Cookie.

Embora os cookies sejam um padrão da indústria, existem restrições práticas, como o limite aproximado de 20 cookies por site dependendo das configurações do agente do usuário.

Gestão do Ciclo de Vida

Por padrão, um cookie é volátil e expira assim que a sessão do navegador termina. Para persistir os dados além disso, o método setMaxAge(int segundos) deve ser configurado explicitamente no objeto Cookie. Um valor positivo indica quantos segnudos o dado permanece válido no armazenamento local.

No ambiente server-side, a leitura dos dados enviados pelo cliente é realizada acessando o objeto HttpServletRequest. O método getCookies() retorna um array de objetos Cookie. É necessário percorrer esse array manualmente para localizar o identificador dseejado, pois não existe um método nativo de busca direta por nome.

A exclusão efetiva ocorre criando-se um novo cookie com o mesmo nome e definindo seu tempo máximo de vida (maxAge) para zero antes de enviar ao cliente.

Segurança e Restrições de Acesso

Na especificação Servlet 3.0, foi introduzido o método setHttpOnly(true). Ao ativar esta flag, o atributo HttpOnly é anexado ao cabeçalho de resposta. Isso impede que scripts executados no lado do cliente, como JavaScript, acessem o conteúdo do cookie, mitigando riscos associados a ataques XSS (Cross-Site Scripting). O status dessa configuração pode ser verificado posteriormente usando isHttpOnly().

Vale ressaltar que a principal vulnerabilidade reside na possibilidade de o usuário desabilitar o suporte a cookies nas configurações do seu navegador, tornando a dependência exclusiva dessa tecnologia arriscada para sessões críticas.

Exemplo Prático de Implementação

A seguir, apresenta-se uma implementação refinada de dois servlets para a gestão de tokens de acesso, alterando a estrutura lógica e nomenclatura em relação ao modelo básico.

package com.example.security;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(name = "TokenGenerator", value = "/generar-token")
public class TokenGenerator extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        definirCookiePermanente(response);
        response.setStatus(HttpServletResponse.SC_OK);
        response.getWriter().write("Token gerado com sucesso.");
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        doPost(request, response);
    }

    private void definirCookiePermanente(HttpServletResponse resp) {
        // Criação de cookie com nome específico e valor seguro
        Cookie authKey = new Cookie("access_token", "xyz-987-zzz");
        
        // Configuração de validade para 1 dia (86400 segundos)
        authKey.setMaxAge(86400); 
        
        // Aplica restrições de segurança
        authKey.setHttpOnly(true); 
        authKey.setPath("/"); 
        
        resp.addCookie(authKey);
    }
}

package com.example.security;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Optional;

@WebServlet(name = "TokenValidator", value = "/verificar-token")
public class TokenValidator extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        
        Optional<Cookie> tokenOpt = buscarTokenEspecifico(req);
        PrintWriter out = resp.getWriter();
        
        if (tokenOpt.isPresent()) {
            Cookie tokenAtual = tokenOpt.get();
            out.println("<strong>Status:</strong> Autenticado");
            out.println("<strong>ID:</strong> " + tokenAtual.getName());
            
            // Lógica opcional para remover cookie após leitura
            removerCookie(resp, tokenAtual.getName());
        } else {
            resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            out.println("<strong>Status:</strong> Nenhum token encontrado.");
        }
    }

    private Optional<Cookie> buscarTokenEspecifico(HttpServletRequest request) {
        Cookie[] todosTokens = request.getCookies();
        
        if (todosTokens != null) {
            for (Cookie c : todosTokens) {
                if ("access_token".equals(c.getName())) {
                    return Optional.of(c);
                }
            }
        }
        return Optional.empty();
    }

    private void removerCookie(HttpServletResponse response, String nomeCookie) {
        Cookie deleteCookie = new Cookie(nomeCookie, "");
        deleteCookie.setMaxAge(0);
        deleteCookie.setPath("/");
        response.addCookie(deleteCookie);
    }
}

Tags: java-servlet http-cookies session-management Web-Security jakarta-ee

Publicado em 10-22 19:27