Desenvolvimento seguro com Apache Shiro – Parte 2
Recursos especiais neste artigo:
Artigo no estilo Curso Online.
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.
...Confira outros conteúdos:
Introdução ao JDBC
Novidades do Java
Teste unitário com JUnit
Black November
Desconto exclusivo para as primeiras 200 matrículas!
Pagamento anual
12x no cartão
De: R$ 69,00
Por: R$ 54,90
Total: R$ 658,80
Garanta o desconto
- Formação FullStack Completa
- Carreira Front-end I e II, Algoritmo e Javascript, Back-end e Mobile
- +10.000 exercícios gamificados
- +50 projetos reais
- Comunidade com + 200 mil alunos
- Estude pelo Aplicativo (Android e iOS)
- Suporte online
- 12 meses de acesso
Pagamento recorrente
Cobrado mensalmente no cartão
De: R$ 79,00
Por: R$ 54,90 /mês
Total: R$ 658,80
Garanta o desconto
- Formação FullStack Completa
- Carreira Front-end I e II, Algoritmo e Javascript, Back-end e Mobile
- +10.000 exercícios gamificados
- +50 projetos reais
- Comunidade com + 200 mil alunos
- Estude pelo Aplicativo (Android e iOS)
- Suporte online
- Fidelidade de 12 meses
- Não compromete o limite do seu cartão
<Perguntas frequentes>
Nossos casos de sucesso
Eu sabia pouquíssimas coisas de programação antes de começar a estudar com vocês, fui me especializando em várias áreas e ferramentas que tinham na plataforma, e com essa bagagem consegui um estágio logo no início do meu primeiro período na faculdade.
Estudo aqui na Dev desde o meio do ano passado!
Nesse período a Dev me ajudou a crescer muito aqui no trampo.
Fui o primeiro desenvolvedor contratado pela minha
empresa. Hoje eu lidero um time de desenvolvimento!
Minha meta é continuar estudando e praticando para ser um
Full-Stack Dev!
Economizei 3 meses para assinar a plataforma e sendo sincero valeu muito a pena, pois a plataforma é bem intuitiva e muuuuito didática a metodologia de ensino. Sinto que estou EVOLUINDO a cada dia. Muito obrigado!
Nossa! Plataforma maravilhosa. To amando o curso de desenvolvimento front-end, tinha coisas que eu ainda não tinha visto. A didática é do jeito que qualquer pessoa consegue aprender. Sério, to apaixonado, adorando demais.
Adquiri o curso de vocês e logo percebi que são os melhores do Brasil. É um passo a passo incrível. Só não aprende quem não quer. Foi o melhor investimento da minha vida!
Foi um dos melhores investimentos que já fiz na vida e tenho aprendido bastante com a plataforma. Vocês estão fazendo parte da minha jornada nesse mundo da programação, irei assinar meu contrato como programador graças a plataforma.
Wanderson Oliveira
Comprei a assinatura tem uma semana, aprendi mais do que 4 meses estudando outros cursos. Exercícios práticos que não tem como não aprender, estão de parabéns!
Obrigado DevMedia, nunca presenciei uma plataforma de ensino tão presente na vida acadêmica de seus alunos, parabéns!
Eduardo Dorneles
Aprendi React na plataforma da DevMedia há cerca de 1 ano e meio... Hoje estou há 1 ano empregado trabalhando 100% com React!
Adauto Junior
Já fiz alguns cursos na área e nenhum é tão bom quanto o de vocês. Estou aprendendo muito, muito obrigado por existirem. Estão de parabéns... Espero um dia conseguir um emprego na área.
Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.