computer_security_stock_image-100582931-orig

Segurança: não coloque o usuário logado no controller

É incrível como você aprende com a experiência. Saca só a jornada que tive para aprender a implementar segurança na web…

Quando comecei minha carreira como programador, lá por volta de 2005, e tive que implementar meu primeiro controle de acesso eu só sabia fazer isso de uma forma, e era jogando os dados do usuário logado diretamente na sessão:

public String logar(String login, String senha) {
    Usuario usuario = this.autentica(login, senha);
    if (usuario != null) {
        Session sessao = this.getSession();
        sessao.setAttribute("usuarioLogado", usuario); // coloca usuario na sessão
        return "/home.jsp";
    }
    return "/login.jsp";
}

Um dos problemas chatos dessa abordagem é que eu tinha que ficar testando se o usuário existia na sessão antes de fazer minhas lógicas de segurança – um saco! Era algo muito parecido com o código abaixo:

Usuario usuario = sessao.getAttribute("usuarioLogado");
if (usuario != null && usuario.getTipo() == ADMIN) {
    // logica de negocio...
}

Daí o tempo passou e depois aprendi outra forma mais esperta, que era deixando o usuário logado dentro do controller, no caso do JSF eu deixava dentro do managed bean:

@ManagedBean
@SessionScoped // managed bean na sessão
public class LoginBean {
    // outros atributos
    private Usuario usuarioLogado;

    public String logar() {
        // lógica para autenticar usuário e setar atributo usuarioLogado
    }
}

Resolveu? Ahhh… não!

Essa abordagem também trazia problemas sutis que eu levei alguns anos pra enxergar, apesar de sutis eles foram bem sérios e sobrecarregaram a memoria do servidor. Por esse motivo, para você não cometer os erros que eu cometi, eu bloguei sobre como representar o usuário logado no meu sistema mas desta vez tirando proveito da OO de verdade:

[Post] OO na prática: representando usuário logado no sistema

Após ler o post sobre essa prática você vai se perguntar porque diabos você não fez isso antes, afinal de contas, basta usar OO para modelar conceitos em objetos, que é algo que fazemos dia a dia para nossas entidades mas que ignoramos quando falamos de segurança.

E aí, como você tem feito pra guardar os dados do usuário logado?

Controle Transaciona Programatico

OO no mundo real: evite vazamento de conexões com controle transacional programático

No último Javou (evento da JavaCE) o prof. Alexandre Martins comentou na sua palestra que um dev junior conseguiu derrubar por alguns minutos o sistema de previdência social da Dataprev. Estou falando do principal sistema da empresa… muito louco não?

Sabe como? Ele esqueceu de colocar essa linha de código aqui:

conexao.close();  // fecha conexão com o banco

Simmm! Ele esqueceu de fechar uma conexão com o banco de dados! Se você não sabe, uma conexão é um recurso muito caro e, para piorar, é muito limitado; todo banco tem um limite de conexões que podem ser abertas, se esse limite estourar já era, a aplicação não consegue mais conversar com o banco!

(Eu nem culpo esse dev junior, até porque eu já fiz algo parecido em alguns sistemas… mas não conta pra ninguém, tá?)

Mas como ele poderia ter evitado?

A boa prática diz que todo recurso caro que for aberto deve sempre ser fechado quando não for mais utilizado. Uma boa maneira de fazer isso é através do bloco try-catch-finally do Java:

Connection conexao = null;
try {
    conexao = ConnectionFactory.getConnection();
    // logica de negócio

} catch (SQLException e) {
    // trata erro
} finally {
    conexao.close(); // fecha conexão com o banco
}

O problema é que repetir esse código em todo o sistema é muito frágil, cedo ou tarde alguém esquece e termina com o sistema fora do ar. Aí já viu né, é justa causa…

E se eu te dissesse que tem uma maneira mais elegante de resolver isso usando OO? É o que chamamos de controle transacional programático e traz diversos benefícios para qualquer sistema, entre eles a garantia que toda conexão com o banco é fechada!

Pra te ajudar a entender do que estou falando, eu bloguei sobre o assunto:

[Post] Controle Transacional Programático em Sistemas Legados

Após ler esse artigo eu te garanto que você vai levar o controle de conexões e transações na sua aplicação mais a sério, seja seu sistema legado ou mais moderno com JPA e Hibernate.

junit-exceptions

Como você testa os fluxos alternativos do seu código?

Deixa eu te perguntar: quantos cenários de testes você enxerga no código abaixo:

/**
 * Registrar novo lance no leilão
 */
public void darLance(Lance lance) {
    
    if (lance.getValor() <= 0)
        throw new RuntimeException("Lance com valor negativo");
    
    this.lances.add(lance);
}

E aí, quantos? 1, 2 ou 3 casos de teste? Não é tão simples assim não, é mesmo?

Um especialista de Q&A responderia facilmente, afinal ele estudou muito para isso! Mas nós desenvolvedores temos maior dificuldade, precisamos de experiência para identificar cenários em código e muitas vezes até ferramentas de cobertura de código:

analisando-cobertura-de-codigo-eclemma_large

Não identificar fluxos de negócio em um trecho de código pode trazer diversos bugs para o sistema. Não é por acaso, como testar algo que não conseguimos enxergar?

Não dá né…

Um boa maneira de melhorar sua percepção é escrevendo testes automatizados. Escrever testes aumenta nossa percepção a um nível surpreendente. 

Para te ajudar a entender toda a problemática, acabamos de blogar sobre o assunto e porque você deveria se preocupar com os fluxos alternativos do seu código:

[Post] jUnit: testando fluxos de exceção e erro

Cobrir fluxos alternativos e excepcionais com testes é muito barato se comparado a testes manuais. Você nem vai precisar levantar seu Tomcat outra vez…

PS: e aí, quantos cenários você encontrou? Comenta aí embaixo, vamos bater uma bola…