Um guia para cookies HTTP em Java
1. Visão geral
Neste artigo, vamos explorar operações de baixo nível com programação de rede Java. Analisaremos mais detalhadamente os cookies.
A plataforma Java vem com suporte de rede embutido, empacotado no pacotejava.net:
import java.net.*;
2. Cookies HTTP
Sempre que um cliente envia uma solicitação HTTP para um servidor e recebe uma resposta, ele se esquece desse cliente. Na próxima vez que o cliente solicitar novamente, ele será visto como um cliente totalmente novo.
No entanto, os cookies, como sabemos, possibilitam estabelecer uma sessão entre o cliente e o servidor, para que o servidor possa se lembrar do cliente através de vários pares de resposta de solicitação.
A partir desta seção, aprenderemos como usar cookies para aprimorar nossas comunicações cliente-servidor na programação de redes Java.
A classe principal no pacotejava.net para lidar com cookies éCookieHandler. Existem outras classes e interfaces auxiliares, comoCookieManager,CookiePolicy,CookieStore eHttpCookie.
3. A classeCookieHandler
Considere este cenário; estamos nos comunicando com o servidor emhttp://example.com, ou qualquer outra URL que usa protocolo HTTP, o objeto URL estará usando um mecanismo chamado manipulador de protocolo HTTP.
Este manipulador de protocolo HTTP verifica se há uma instânciaCookieHandler padrão no sistema. Se houver, invoca-o para se encarregar da gestão do estado.
Portanto, a classeCookieHandler tem o propósito de fornecer um mecanismo de retorno de chamada para o benefício do manipulador de protocolo HTTP.
CookieHandler é uma classe abstrata. Ele tem um métodogetDefault() estático que pode ser chamado para recuperar a instalaçãoCookieHandler atual ou podemos chamarsetDefault(CookieHandler) para definir o nosso. Observe que chamarsetDefault instala um objetoCookieHandler em todo o sistema.
Ele também temput(uri, responseHeaders) para salvar quaisquer cookies no armazenamento de cookies. Esses cookies são recuperados dos cabeçalhos de resposta da resposta HTTP do URI fornecido. É chamado sempre que uma resposta é recebida.
Um método de API relacionado -get(uri,requestHeaders) recupera os cookies salvos no URI fornecido e os adiciona arequetHeaders. É chamado antes de uma solicitação ser feita.
Todos esses métodos devem ser implementados em uma classe concreta deCookieHandler. Neste ponto, a classeCookieManager merece nossa atenção. Esta classe oferece uma implementação completa da classeCookieHandler para os casos de uso mais comuns.
Nas próximas duas seções, vamos explorar a classeCookieManager; primeiro no modo padrão e depois no modo personalizado.
4. OCookieManager padrão
Para ter uma estrutura completa de gerenciamento de cookies, precisamos ter implementações deCookiePolicyeCookieStore.
CookiePolicy estabelece as regras para aceitar e rejeitar cookies. É claro que podemos alterar essas regras para atender às nossas necessidades.
Em seguida -CookieStore faz exatamente o que seu nome sugere, ele possui métodos para salvar e recuperar cookies. Naturalmente, podemos ajustar o mecanismo de armazenamento aqui também, se precisarmos.
Vejamos primeiro os padrões. Para criar oCookieHandler padrão e defini-lo como o padrão para todo o sistema:
CookieManager cm = new CookieManager();
CookieHandler.setDefault(cm);
Devemos observar que oCookieStore padrão terá memória volátil, ou seja, ele vive apenas por toda a vida da JVM. Para ter um armazenamento mais persistente para cookies, precisamos personalizá-lo.
Quando se trata deCookiePolicy, a implementação padrão éCookiePolicy.ACCEPT_ORIGINAL_SERVER. Isso significa que, se a resposta for recebida através de um servidor proxy, o cookie será rejeitado.
5. UmCookieManager personalizado
Agora vamos personalizar oCookieManager padrão, fornecendo nossa própria instância deCookiePolicy ouCookieStore (ou ambos).
5.1. CookiePolicy
CookiePolicy fornece algumas implementações predefinidas para conveniência:
-
CookiePolicy.ACCEPT_ORIGINAL_SERVER - apenas cookies do servidor original podem ser salvos (a implementação padrão)
-
CookiePolicy.ACCEPT_ALL - todos os cookies podem ser salvos independentemente de sua origem
-
CookiePolicy.ACCEPT_NONE - nenhum cookie pode ser salvo
Para simplesmente alterar oCookiePolicy atual sem implementar o nosso próprio, chamamossetCookiePolicy na instânciaCookieManager:
CookieManager cm=new CookieManager();
cm.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
Mas podemos fazer muito mais personalização do que isso. Conhecendo o comportamento deCookiePolicy.ACCEPT_ORIGINAL_SERVER, vamos supor que confiamos em um servidor proxy específico e gostaríamos de aceitar cookies dele em cima do servidor original.
Teremos que implementar a interfaceCookiePolicy e implementar o métodoshouldAccept; é aqui que alteraremos a regra de aceitação, adicionando o nome de domínio do servidor proxy escolhido.
Vamos chamar a nova política -ProxyAcceptCookiePolicy. Basicamente, ele rejeitará qualquer outro servidor proxy de sua implementaçãoshouldAccept além do endereço de proxy fornecido e, em seguida, chamará o métodoshouldAccept deCookiePolicy.ACCEPT_ORIGINAL_SERVER para concluir a implementação:
public class ProxyAcceptCookiePolicy implements CookiePolicy {
private String acceptedProxy;
public boolean shouldAccept(URI uri, HttpCookie cookie) {
String host = InetAddress.getByName(uri.getHost())
.getCanonicalHostName();
if (HttpCookie.domainMatches(acceptedProxy, host)) {
return true;
}
return CookiePolicy.ACCEPT_ORIGINAL_SERVER
.shouldAccept(uri, cookie);
}
// standard constructors
}
Quando criamos uma instância deProxyAcceptCookiePolicy, passamos uma String do endereço de domínio do qual gostaríamos de aceitar cookies além do servidor original.
Em seguida, definimos essa instância como a política de cookies da instânciaCookieManager antes de defini-la como CookieHandler padrão:
CookieManager cm = new CookieManager();
cm.setCookiePolicy(new ProxyAcceptCookiePolicy("example.com"));
CookieHandler.setDefault(cm);
Desta forma, o manipulador de cookies aceitará todos os cookies do servidor original e também aqueles dehttp://www.example.com.
5.2. CookieStore
CookieManager adiciona os cookies aCookieStore para cada resposta HTTP e recupera cookies deCookieStore para cada solicitação HTTP.
A implementação padrãoCookieStore não tem persistência, em vez disso, perde todos os seus dados quando o JVM é reiniciado. Mais como RAM em um computador.
Portanto, se quisermos que a implementação deCookieStore se comporte como o disco rígido e retenha os cookies nas reinicializações da JVM, devemos personalizar seu mecanismo de armazenamento e recuperação.
Uma coisa a notar é que não podemos passar uma instânciaCookieStore paraCookieManager após a criação. Nossa única opção é passá-lo durante a criação deCookieManager ou obter uma referência para a instância padrão chamando newCookieManager().getCookieStore()e complementando seu comportamento.
Aqui está a implementação dePersistentCookieStore:
public class PersistentCookieStore implements CookieStore, Runnable {
private CookieStore store;
public PersistentCookieStore() {
store = new CookieManager().getCookieStore();
// deserialize cookies into store
Runtime.getRuntime().addShutdownHook(new Thread(this));
}
@Override
public void run() {
// serialize cookies to persistent storage
}
@Override
public void add(URI uri, HttpCookie cookie) {
store.add(uri, cookie);
}
// delegate all implementations to store object like above
}
Observe que recuperamos uma referência à implementação padrão no construtor.
Implementamosrunnable para que possamos adicionar um gancho de desligamento que é executado quando a JVM está desligando. Dentro do métodorun, mantemos todos os nossos cookies na memória.
Podemos serializar os dados em um arquivo ou em qualquer armazenamento adequado. Observe também que dentro do construtor, primeiro lemos todos os cookies da memória persistente emCookieStore. Esses dois recursos simples tornam oCookieStore padrão essencialmente persistente (de uma forma simplista).
6. Conclusão
Neste tutorial, abordamos os cookies HTTP e mostramos como acessá-los e manipulá-los programaticamente.
O código-fonte completo do artigo e todos os trechos de código podem ser encontrados emGitHub project.