Artigo do tipo Exemplos Práticos
Recursos especiais neste artigo:
Artigo no estilo Curso Online.
Conhecendo o Apache Shiro – Parte 1
Este artigo apresenta o Apache Shiro, um framework de segurança mantido pela Apache como um de seus “Top Level Projects”. Esse framework pode ser usado para implementar segurança nas mais diversas plataformas Java, diferente do Spring Security, que funciona apenas em aplicações web.


Em que situação o tema é útil
Este tema é útil a todos os desenvolvedores que precisam implementar segurança nas mais diversas aplicações Java (não só na plataforma Web) e que não querem ter complicações para isso. O Apache Shiro torna essa implementação tão fácil e intuitiva quanto possível, visto que esse é o seu objetivo principal.

Em 2003, a realidade das soluções de segurança para a plataforma Java não era das melhores. As poucas opções existentes não possuíam todas as funcionalidades necessárias e eram difíceis de usar. Uma das únicas soluções era o Java Authentication and Authorization Service (JAAS), que não supria bem o controle de segurança de uma aplicação. Nesse cenário, nasceu o JSecurity, predecessor do Apache Shiro. Desde o início, o principal objetivo do projeto sempre foi a facilidade de uso, para acabar com as frustrações dos desenvolvedores no desenvolvimento da segurança das aplicações.

Depois de anos de projeto e uma comunidade crescente, ele passou para a incubadora de projetos da Apache, mudando seu nome para Ki. Em 2010 se tornou um dos chamados “Top Level Projects” da Apache, assim como Tomcat, Cassandra e tantos outros. Ao alcançar esse nível, o projeto foi renomeado para Shiro, uma palavra japonesa que significa Castelo. Essa promoção ajudou a manter o crescimento da comunidade de desenvolvedores e utilizadores.

Se olharmos para o Apache Shiro desde o seu nascimento, quando ainda era chamado JSecurity, veremos que seu principal objetivo sempre foi a simplicidade e a facilidade de uso. Pode-se dizer que esse objetivo é bem cumprido, pois o embasamento conceitual é bastante intuitivo e ele possui uma boa API para utilização.

No entanto, antes de partirmos para os conceitos usados em sua implementação, é importante conhecermos o escopo do projeto. O núcleo do Apache Shiro tem interesse em resolver os seguintes pontos: Autenticação, Autorização, Criptografia e gerenciamento de sessão do usuário independente de plataforma. Além disso, ele também se preocupa com funcionalidades auxiliares e de integração com diversas plataformas. Entre essas funcionalidades, estão: suporte a aplicações Web (filtragem de URLs, por exemplo), integração com bibliotecas de cache para evitar o lento acesso às informações necessárias para a segurança, funcionalidade de “remember me”, possibilidade de um usuário assumir temporariamente a identidade de outro (o que pode ser útil em casos de suporte ao cliente), entre outros. Um resumo da estrutura do Apache Shiro pode ser visto na Figura 1, retirada da documentação do projeto.

Figura 1. Visão das principais funcionalidades do Apache Shiro.

Com o escopo do projeto em mente, podemos nos concentrar nos conceitos de segurança empregados. Para começar, veremos as principais funcionalidades: Autenticação e Autorização.

Subject e Autenticação

A segurança, no Apache Shiro, é centralizada na entidade Subject. Essa nada mais é do que o sujeito que está operando o sistema no momento. Ao invés de se chamar User, eles preferiram a palavra Subject, pois ela pode significar qualquer coisa que esteja interagindo com o sistema. Muitas vezes, esse sujeito será um usuário comum, mas também pode ser outro sistema interagindo com a aplicação protegida, por exemplo.

Para obter essa entidade, o Apache Shiro expõe uma API de fácil uso. O código necessário para essa operação é mostrado na Listagem 1. Vale destacar que esse código sempre retornará o Subject corrente da aplicação, independente de onde for usado.

Listagem 1. Obtendo uma instância do Subject corrente.

Subject subject = SecurityUtils.getSubject(); 

Quando esse método é chamado pela primeira vez, antes da autenticação, ele retornará uma instância considerada anônima. Depois que o usuário é autenticado, esse mesmo código mostrado na Listagem 1 retornará o Subject considerado autenticado. Assim, esse método jamais retornará null, mas sim uma instância com estado diferente.

Como foi dito antes, a API do Apache Shiro é centralizada nessa entidade. Portanto, para que possamos autenticar o usuário, usamos o método login() do Subject. Como parâmetro, passamos um objeto com a identificação e as credenciais fornecidas pelo sujeito que está tentando autenticar na aplicação. Na Listagem 2, podemos ver a forma mais comum de credenciais fornecidas: nome de usuário e senha.

Listagem 2. Autenticando um usuário através de username e password.


AuthenticationToken token = new UsernamePasswordToken(username, password);
  try{
      SecurityUtils.getSubject().login(token);
  } catch(UnknownAccountException e){
   
  } catch(DisabledAccountException e){
   
  } catch(IncorrectCredentialsException e){
   
  } catch(AuthenticationException e){
   
  } 

O objeto passado como parâmetro para o método login() deve ser um implementador da interface AuthenticationToken. Essa interface possui dois métodos a serem implementados: getPrincipal() e getCredentials(). Esses métodos simplesmente retornam dois atributos: principal e credentials. Em uma aplicação comum, o principal será o nome do usuário (login) e as credentials (ou credenciais) representarão a senha do mesmo. Assim, podemos entender que o principal é algo que identifica unicamente um usuário no sistema e as credenciais provam que o usuário é quem ele diz ser.

Foi decidido pela criação dessa interface por que nem todos os sistemas usarão apenas login e senha para autenticar usuários. Existem casos em que o usuário poderá ser autenticado através de um web service via um token de autorização, por exemplo, que é o caso do padrão conhecido como OAuth. Para os usos mais simples em que o usuário é autenticado por seu login e senha, já existe uma implementação dessa interface: UsernamePasswordToken. Em nosso exemplo, criamos uma instância dessa classe simplesmente passando o username e password do usuário.

Também notamos, na Listagem 2, o que acontece numa falha de autenticação. Podemos observar que várias exceções podem ser lançadas em uma falha de autenticação, sendo todas elas subclasses de AuthenticationException. Cada subclasse identifica precisamente o que causou o erro de autenticação. Elas vão desde usuário desconhecido até senha incorreta, além de outras. Além disso, também poderemos implementar nossas próprias exceções que serão lançadas no processo de autenticação.

Dica de segurança: evite dar informações detalhadas sobre o porquê de a autenticação ter falhado, pois isso pode ajudar um hacker a acessar uma conta que não o pertence.

Quando o método login() é chamado, o Apache Shiro coordena uma série de ações para verificar a autenticidade do token submetido para autenticação. Tudo isso é controlado pelo SecurityManager, que é responsável por gerenciar todas as operações de todos os sujeitos. Mais detalhes sobre essa classe serão explicados em breve neste artigo. Antes de entrar nesses detalhes, é importante saber sobre o que controla o que determinado sujeito pode fazer na aplicação.

Autorização

Saber que um sujeito é quem ele diz ser não é suficiente para tornar nossa aplicação segura. Também devemos saber o que ele pode e o que não pode fazer na aplicação. Afinal, não é qualquer usuário do sistema que poderá excluir outros usuários ou ver informações confidenciais, por exemplo. Nesse contexto, entra o conceito de Autorização.

Esse conceito é o que define as permissões de um sujeito no sistema. Essas permissões podem ser dadas de acordo com o papel realizado por ele. Para deixar mais claro, considere um sistema fictício de um Banco. Nele teríamos os papéis: gerente de contas, cliente, operador do caixa convencional, entre outros. Cada um dos usuários que exigem esses papéis possui determinadas permissões no sistema. O cliente não pode fechar diretamente sua conta, por exemplo. Apenas o gerente de contas possui permissão para realizar essa operação.

Como podemos perceber, um papel a ser exercido no sistema contempla um grupo de permissões para fazer suas operações. O sistema tem a responsabilidade de fazer as verificações de autorização antes que qualquer uma dessas operações seja realizada.

O Apache Shiro fornece duas formas de fazer essas verificações: através do papel (role) e através da permissão (permission) do usuário. Para tornar mais claro, imagine que possuímos, no sistema, a operação de fechar conta. Antes de atendermos a solicitação do usuário, podemos verificar se ele é um gerente de contas (possui a role “Gerente de Contas”) ou se ele simplesmente possui permissão para fechar uma conta. A diferença entre essas duas formas de verificação é bem simples: se fizermos uma verificação pelo papel exercido, deixaremos essa funcionalidade do sistema presa a esse papel. Enquanto isso, se verificarmos a permissão, o sistema poderá mudar com mais facilidade, atribuindo essa permissão a outros papéis e usuários.

Pelas diferenças apontadas entre as duas formas de verificação, podemos imaginar que é mais recomendada a verificação por permissões, por dar melhor dinamismo ao sistema. Caso seja necessário criar um novo papel e garantir a ele acesso a determinada operação, pode-se simplesmente cadastrá-lo e o sistema poderá reagir a essa reação sem que precise ser modificado. Se for feita uma verificação pelo papel, qualquer papel novo no sistema precisará de uma modificação em seu código para atualizar essa verificação.

Portanto, apesar do Apache Shiro suportar que seja verificado se determinado sujeito possui determinado papel, recomenda-se não fazer isso diretamente. Ao invés disso, deve-se fornecer um meio de criação de papéis a serem armazenados em um banco de dados e vinculação das permissões aos usuários e aos papéis. Assim, antes que as operações sejam realizadas, o sistema verifica se o usuário possui direta ou indiretamente determinada permissão. Ele possuirá diretamente quando essa permissão for vinculada à sua conta e indiretamente se ele possuir um papel que tenha essa permissão.

...

Quer ler esse conteúdo completo? Tenha acesso completo