Primeiros Passos com Shiro: Criando seu Primeiro Demo

Antes de ler este artigo, você provavelmente não ouviu falar do Shiro. Então, o que é Shiro? Shiro é um projeto de código aberto muito conhecido da Apache Foundation (site oficial: http://shiro.apache.org/). A introdução oficial é a seguinte:

Apache Shiro™ é um framework de segurança Java poderoso e fácil de usar, que executa autenticação, autorização, criptografia e gerenciamento de sessões. Com a API de fácil compreensão do Shiro, você pode proteger rapidamente qualquer aplicação — desde os menores aplicativos móveis até as maiores aplicações web e corporatvias.

Agora, vamos conhecer o Shiro de perto.

1. Criando um Projeto Maven

1.1 Selecionando o Template Maven e Preenchendo as Informações do Projeto

Selecione Maven e escolha o template quickstart.

Preencha as informações do projeto.

Configure o Maven do projeto; se não houver configurações especiais, basta avançar.

Defina o caminho de salvamento do projeto.

O projeto Maven está criado. Agora vamos criar o demo do Shiro.

2. Desenvolvendo o Demo do Shiro

2.1 Adicionando as Dependências

Adicione as seguintse dependências ao seu arquivo pom.xml:

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.2.6</version>
</dependency>

2.2 Implementando o Código Java

package com.exemplo Shiro;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.
 */

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Aplicação demonstrando como utilizar a API do Shiro.
 */
public class DemonstracaoShiro {

    private static final Logger logger = LoggerFactory.getLogger(DemonstracaoShiro.class);


    public static void main(String[] args) {

        // A maneira mais simples de criar um SecurityManager configurado
        // é usar o arquivo de configuração INI
        Factory<SecurityManager> fabrica = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager gerenciadorSeguranca = fabrica.getInstance();

        // Disponibiliza o SecurityManager como um singleton JVM
        SecurityUtils.setSecurityManager(gerenciadorSeguranca);

        // Obtém o usuário atualmente em execução
        Subject usuarioAtual = SecurityUtils.getSubject();

        // Trabalhando com Session (não precisa de container web ou EJB!)
        Session sessao = usuarioAtual.getSession();
        sessao.setAttribute("chaveTeste", "valorExemplo");
        String valorRecuperado = (String) sessao.getAttribute("chaveTeste");
        
        if (valorRecuperado.equals("valorExemplo")) {
            logger.info("Valor correto recuperado! [{}]", valorRecuperado);
        }

        // Realiza o login para verificar papéis e permissões
        if (!usuarioAtual.isAuthenticated()) {
            UsernamePasswordToken token = new UsernamePasswordToken("usuario1", "senha123");
            token.setRememberMe(true);
            
            try {
                usuarioAtual.login(token);
            } catch (UnknownAccountException ex) {
                logger.info("Usuário '{}' não encontrado", token.getPrincipal());
            } catch (IncorrectCredentialsException ex) {
                logger.info("Senha incorreta para '{}'", token.getPrincipal());
            } catch (LockedAccountException ex) {
                logger.info("Conta '{}' está bloqueada", token.getPrincipal());
            } catch (AuthenticationException ex) {
                logger.info("Erro de autenticação");
            }
        }

        // Exibe informações do usuário logado
        logger.info("Usuário [{}] autenticado com sucesso.", usuarioAtual.getPrincipal());

        // Verifica papéis do usuário
        if (usuarioAtual.hasRole("admin")) {
            logger.info("Papel de administrador確認!");
        } else {
            logger.info("Acesso padrão.");
        }

        // Verifica permissões específicas
        if (usuarioAtual.isPermitted("recurso:acao")) {
            logger.info("Permissão para executar ação no recurso concedida.");
        } else {
            logger.info("Sem permissão para esta ação.");
        }

        // Verifica permissão em nível de instância
        if (usuarioAtual.isPermitted("sistema:operacao:identificador1")) {
            logger.info("Permissão para operar 'identificador1' concedida.");
        } else {
            logger.info("Acesso negado para 'identificador1'.");
        }

        // Finaliza a sessão
        usuarioAtual.logout();

        System.exit(0);
    }
}

O código acima é uma adaptação do exemplo oficial do Shiro, simplificado para facilitar o entendimento.

2.3 Importando os Arquivos de Configuração

Arquivo de configuração de log, nome: log4j.properties:

log4j.rootLogger=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n

log4j.logger.org.apache=WARN
log4j.logger.org.springframework=WARN
log4j.logger.org.apache.shiro=INFO
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN

Arquivo de configuração do Shiro, nome: shiro.ini:

#
# Licensed to the Apache Software Foundation (ASF)
#

# -----------------------------------------------------------------------------
# Usuários e seus papéis atribuídos
# -----------------------------------------------------------------------------
[users]
# Formato: usuario = senha, papel1, papel2
root = senha123, administrador
convidado =123456, visitante
usuario1 = senha123, usuario, operacao
admin = admin123, administrador, supervisor
operador = oper123, operador

# -----------------------------------------------------------------------------
# Papéis com permissões atribuídas
# -----------------------------------------------------------------------------
[roles]
# Administrador tem todas as permissões
administrador = *
# Papel operador pode acessar recursos específicos
operador = recurso:*
# Supervisor tem permissões administrativas
supervisor = sistema:*

3. Executando o Demo

Execute o método main da classe Java. É importante notar que o Shiro requer o SLF4J para logging. Adicione as seguintes dependências:

<!-- Configuração de logging -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.7.24</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.24</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-nop</artifactId>
    <version>1.7.24</version>
</dependency>

Após a execução, você verá os seguintes logs no console:

2019-10-19 12:00:04,751 INFO [org.apache.shiro.session.mgt.AbstractValidatingSessionManager] - Habilitando scheduler de validação de sessão
2019-10-19 12:00:05,508 INFO [com.exemploShiro.DemonstracaoShiro] - Valor correto recuperado! [valorExemplo]
2019-10-19 12:00:05,512 INFO [com.exemploShiro.DemonstracaoShiro] - Usuário [usuario1] autenticado com sucesso.
2019-10-19 12:00:05,512 INFO [com.exemploShiro.DemonstracaoShiro] - Papel de administrador確認!
2019-10-19 12:00:05,513 INFO [com.exemploShiro.DemonstracaoShiro] - Permissão para executar ação no recurso concedida.
2019-10-19 12:00:05,514 INFO [com.exemploShiro.DemonstracaoShiro] - Permissão para operar 'identificador1' concedida.

Nosso primeiro demo do Shiro está funcionando corretamente. Agora vamos analisar o código em detalhes.

4. Análise do Código

Vamos explorar os principais conceitos do Shiro demonstrados no código.

4.1 Arquivo de Configuração do Shiro

O arquivo shiro.ini define usuários e permissões de forma simples:

[users]
# Define usuários com formato: nome=senha,papeis
root = senha123, administrador
usuario1 = senha123, usuario, operacao

[roles]
# Define permissões para cada papel
administrador = *
usuario = recurso:*
operacao = sistema:*

4.2 Fluxo de Inicialização do Shiro

public static void main(String[] args) {
    // 1. Criar fábrica do SecurityManager a partir do arquivo INI
    Factory<SecurityManager> fabrica = new IniSecurityManagerFactory("classpath:shiro.ini");
    
    // 2. Obter instância do SecurityManager
    SecurityManager gerenciadorSeguranca = fabrica.getInstance();
    
    // 3. Configurar o SecurityManager globalmente
    SecurityUtils.setSecurityManager(gerenciadorSeguranca);
    
    // 4. Obter o Subject (representação do usuário atual)
    Subject usuarioAtual = SecurityUtils.getSubject();
    
    // 5. Trabalhar com sessão do Shiro
    Session sessao = usuarioAtual.getSession();
    sessao.setAttribute("dados", "valor");
    
    // 6. Realizar autenticação
    if (!usuarioAtual.isAuthenticated()) {
        UsernamePasswordToken token = new UsernamePasswordToken("usuario1", "senha123");
        try {
            usuarioAtual.login(token);
        } catch (AuthenticationException ex) {
            // Tratar erro de autenticação
        }
    }
    
    // 7. Verificar autorizações
    usuarioAtual.hasRole("administrador");
    usuarioAtual.isPermitted("recurso:acao");
    
    // 8. Encerrar sessão
    usuarioAtual.logout();
}

4.3 Conceitos Fundamentais

Subject: Representa o usuário atual ou entidade executando a operação. É o ponto central de toda operação de segurança no Shiro.

SecurityManager: É o coração do Shiro. Gerencia todos os Subjects e coordena a autenticação, autorização e manipulação de sessões.

Session: O Shiro oferece seu próprio sistema de sessões que funciona em qualquer ambiente (Java SE, Java EE, aplicações web). Uma vantagem significativa é que, em projetos web, ele se integra automaticamente com a HttpSession, permitindo acesso transparente aos dados da sessão.

AuthenticationException: É a classe base para exceções de autenticação. As subclasses mais comuns incluem UnknownAccountException (usuário inexistente), IncorrectCredentialsException (senha incorreta) e LockedAccountException (conta bloqueada).

UsernamePasswordToken: Classe que encapsula as credenciais do usuário. O método setRememberMe(true) permite que o usuário permaneça autenticado em sessões futuras.

O Shiro gerencia três aspectos principais de segurança: autenticação (verificar idetnidade do usuário), autorização (verificar permissões de acesso) e gerenciamento de sessões (manter estado do usuário). Cada um desses aspectos pode ser configurado de forma independente através de diferentes realms, permitindo integração com diversas fontes de dados como bancos de dados, LDAP ou serviços web.

Tags: java Shiro apache-shiro Security authentication

Publicado em 9-9 11:10