Como forçar o Jetty a solicitar credenciais com a autenticação BASIC após invalidar a session?

Estou usando o jetty 6.1.22 com autenticação BASIC como meu mecanismo de login. Na primeira vez que faço login no aplicativo da web, o navegador solicita o nome de usuário e a senha.

Se ele tentar efetuar logout usando um session.invalidate (), a session será invalidada, mas as credenciais serão armazenadas em cache. Isso significa que, se eu tentar me conectar a uma URL segura, verei um ID de session diferente, mas nenhum diálogo para nome de usuário e senha.

(Eu percebo que essa pergunta é antiga, mas é algo que outras pessoas podem querer uma resposta)

A autenticação básica realmente não permite o que você está pedindo, mas você pode conseguir algo que funciona um pouco como o que você quer, se você estiver disposto a viver com algumas “peculiaridades”.

A autenticação BASIC tem 2 aspectos que dificultam o controle dessa maneira

  • É apátrida
  • É do lado do cliente

Ambos os aspectos são resources, mas dificultam a vinculação da autenticação BASIC às sessões Java. É por isso que existem mecanismos de login alternativos (como o login do Java FORM)

Autenticação BASIC é apátrida
Isso significa que o cliente não tem absolutamente nenhuma idéia do que está acontecendo no lado do servidor, e certamente não tem como vincular um conjunto de credenciais de autenticação do BASIC a um cookie (que é o que mais controla a session java)
O que acontece é que o navegador simplesmente envia o nome de usuário + senha a cada solicitação, até que ele decida parar (geralmente porque o navegador foi fechado e uma nova session do navegador foi criada).
O navegador não sabe o que o servidor está fazendo com as credenciais. Ele não sabe se eles são mais necessários, ou não, e não sabe quando o lado do servidor decidiu que uma “nova session” foi iniciada, ele continua enviando esse nome de usuário + senha a cada solicitação.

Autenticação BASIC é do lado do cliente
Esse diálogo para o usuário / senha é tratado pelo cliente. Tudo o que o servidor diz é “O URL que você solicitou precisa de um nome de usuário + senha e nós nomeamos este domínio de segurança ‘xyz'”.
O servidor não sabe se o usuário está digitando uma senha toda vez ou se o cliente as armazenou em cache. Ele não sabe se realmente existe um usuário ou se a senha foi retirada de um arquivo.
A única coisa que o servidor pode fazer é dizer “Você precisa me dar um usuário + senha antes de acessar este URL”

Como fingir
Essencialmente, você precisa detectar (ou seja, fazer um palpite) quando o navegador está enviando credenciais antigas e, em seguida, enviar a resposta “Por favor, me dê um usuário + senha” (HTTP 401) novamente.

A maneira mais simples de fazer isso é ter um filtro em seu aplicativo que detecta a primeira vez que o usuário efetuou login nessa session e envia um código de resposta 401 . Algo como:

  if(session.getAttribute("auth") == null) { response.setStatus(401); response.setHeader("WWW-Authenticate", "basic realm=\"Auth (" + session.getCreationTime() + ")\"" ); session.setAttribute("auth", Boolean.TRUE); writer.println("Login Required"); return; } 

Nesse exemplo, eu nomeei o reino com o horário de criação da session (embora não seja muito bonito – você provavelmente quer formatá-lo de forma diferente). Isso significa que o nome do domínio de segurança muda cada vez que você invalida a session, o que ajuda a evitar que o cliente fique confuso (por que ele está me pedindo um usuário + senha novamente, para o mesmo domínio, quando eu acabei de dar um?) .
O novo nome da região não irá confundir o contêiner do servlet, porque ele nunca o verá – a resposta do cliente não inclui o nome da região.

O truque é que você não quer que o usuário seja solicitado pela senha duas vezes na primeira vez que fizer logon. E, fora da checkbox, essa solução fará isso – uma vez que o contêiner peça e, novamente, quando o filtro o fizer.

Como evitar 2 checkboxs de login Existem 4 opções.

  1. Faça tudo no código.
  2. Use um cookie de session secundária para tentar saber se o usuário está efetuando login pela primeira vez.
  3. Ter seu URL raiz inseguro (mas não faça nada)
  4. Ter todas as suas inscrições sem segurança, exceto por uma página, e redirect os usuários para essa página se eles não estiverem conectados.

Faça no código

Se você estiver satisfeito em fazer toda a segurança dentro de seu próprio servlet / filtro e não obter ajuda do contêiner do servlet (Jetty), então isso não será muito difícil. Você simplesmente desativa a autenticação BASIC no seu web.xml e faz tudo no código. (Há um bom bocado de trabalho, e você precisa ter certeza de não deixar nenhum buraco de segurança aberto, mas é bem fácil conceitualmente)
A maioria das pessoas não quer fazer isso.

Cookies Secundários
Depois que um usuário faz o login no seu aplicativo, você pode definir um cookie (“autenticado”) que expira no final da session do navegador. Este cookie está ligado à session do navegador e não à session Java.
Quando você invalida a session Java – não invalida o cookie “autenticado” .
Se um usuário efetuar login em uma session java nova (ou seja, session.getAttribute("auth")==null ), mas ainda tiver esse “autenticado” , você saberá que eles estão reutilizando uma session de navegador existente e provavelmente -utilizando credenciais de autenticação HTTP existentes. Nesse caso, você faz o truque 401 para forçá-los a dar novas credenciais.

URL raiz não seguro
Se o seu URL raiz não estiver seguro e você souber que esse é o URL para o qual seus usuários sempre fazem login, você pode simplesmente colocar sua verificação “auth” / 401 nesse URL e isso resolverá o problema. Mas certifique-se de não abrir acidentalmente uma falha de segurança.
Este URL não deve ter outra funcionalidade além de redirect os usuários para o aplicativo “real” (que é protegido)

URL segura única
Ter 1 URL protegido (por exemplo, “/ login”).
Assim como o seu filtro “auth” / 401 , tenha outro filtro (ou código adicional no mesmo filtro) em todas as páginas (além do login) que verifica se request.getUserPrincipal() está definido. Caso contrário, redirecione o usuário para “/ login”.

A solução muito mais fácil
Basta usar um método de login FORM em vez de autenticação BASIC. Ele foi projetado para resolver esse problema.