Segurança Neste capítulo será tratada uma questão de grande importância para a maioria das aplicações e motivo de infindáveis discussões nas equipes de desenvolvimento: controle de acesso. Assim como tudo relacionado ao framework, a implementação de segurança foi projetada de forma simples e flexível, independente de camada de apresentação ou tecnologia, te deixando livre para implementar sua própria solução ou utilizar as extensões existentes. Para utilizar o modelo de segurança proposto basta utilizar o Framework Demoiselle, pois no núcleo do framework estão as interfaces e anotações que definem o comportamento básico da implementação.
Configurando Para um correto funcionamento do Demoiselle é necessário inserir os interceptadores de segurança no arquivo src/main/webapp/WEB-INF/beans.xml. br.gov.frameworkdemoiselle.security.RequiredPermissionInterceptor br.gov.frameworkdemoiselle.security.RequiredRoleInterceptor ]]> Opcionalmente é possível configurar o comportamento do módulo de segurança definindo propriedades no arquivo demoiselle.properties da sua aplicação. Propriedades de segurança do Framework Demoiselle Propriedade Descrição Valor padrão frameworkdemoiselle.​security.​enabled Habilita ou desabilita o mecanismo de segurança true frameworkdemoiselle.​security.​authenticator.​class Define a classe que implementa o mecanismo de autenticação. (Detalhes na seção Criando sua implementação) - frameworkdemoiselle.​security.​authorizer.​class Define a classe que implementa o mecanismo de autorização. (Detalhes na seção Criando sua implementação) -
Autenticação O mecanismo de autenticação busca verificar a identidade do usuário de um sistema. A forma mais conhecida - e comum - para executar essa verificação se dá por meio de um formulário de login, geralmente solicitando um nome de usuário e sua respectiva senha. No entanto, outras formas como reconhecimento biométrico e autenticação por token, para citar apenas duas, tem ganhado um grande número de adeptos. O Framework Demoiselle deixa o desenvolvedor livre para definir qual forma usar, de acordo com a sua conveniência e necessidade. A peça chave para tornar isso possível é o contexto de segurança, representado pela interface SecurityContext. Nessa estão definidos os métodos responsáveis por gerenciar os mecanismos de autenticação como, por exemplo, executar login/logout de usuários e verificar se os mesmos estão ou não autenticados. Para utilizar o SecurityContext, basta injetá-lo em seu código. O método login ativa o mecanismo de autenticação e o método logout remove as credenciais atualmente autenticadas do sistema. A classe SecurityContext possui outros métodos que permitem verificar se há um usuário autenticado e acessar o objeto gerente (representado pela classe javax.security.Principal), um objeto que contém dados adicionais sobre o usuário atualmente autenticado. Consulte a documentação da classe SecurityContext para consultar as funcionalidades que ela oferece. Um exemplo do uso do SecurityContext para autenticação segue abaixo:
Autorização Em certos sistemas é necessário não apenas autenticar um usuário, mas também proteger funcionalidades individuais e separar usuários em grupos que possuem diferentes autorizações de acesso. O mecanismo de autorização é responsável por garantir que apenas usuários autorizados tenham o acesso concedido a determinados recursos de um sistema. No modelo de segurança do Framework Demoiselle, a autorização pode acontecer de duas formas: Permissão por funcionalidade, através da anotação @RequiredPermission Permissão por papel, através da anotação @RequiredRole
Protegendo o sistema com <emphasis>@RequiredPermission</emphasis> A anotação @RequiredPermission permite marcar uma classe ou método e informar que acesso a esse recurso requer a permissão de executar uma operação. Operação nesse contexto é um nome definido pelo desenvolvedor que representa uma funcionalidade do sistema. Por exemplo, determinada classe pode ter métodos responsávels por criar, editar, listar e remover bookmarks, o desenvolvedor pode decidir agrupar esses métodos sobre a operação gerenciar bookmark. listarBookmarks() { //Codigo do metodo } @RequiredPermission public List apagarBookmark(Long idBookmark) { public List listarBookmarks() { } }]]> Perceba que a anotação @RequiredPermission sobre o método apagarBookmark não contém parâmetros. Quando não são passados parâmetros o valor padrão para o parâmetro resource é o nome da classe e o valor padrão para operation é o nome do método. É possível anotar a classe inteira com @RequiredPermission, isso protegerá o acesso a todos os métodos dessa classe. listarBookmarks() { //Codigo do metodo } public List apagarBookmark(Long idBookmark) { public List listarBookmarks() { } }]]>
Protegendo o sistema com <emphasis>@RequiredRole</emphasis> Diferente de @RequiredPermission, a anotação @RequiredRole utiliza o conceito de papéis - ou perfís - para proteger recursos. Uma classe ou método anotado com @RequiredRole exigirá que o usuário autenticado possua o papel indicado para acessar o recurso. Voltando ao exemplo de nosso aplicativo de bookmarks, vamos supor que a função de listar os bookmarks existentes pode ser acessada por qualquer usuário autenticado, mas apenas administradores podem criar um novo bookmark. A classe responsável por tais funcionalidades pode ser criada da seguinte forma: listarBookmarks() { } }]]> É possível informar mais de um papel para a anotação @RequiredRole, neste caso basta que o usuário autenticado possua um dos papéis listados para ter acesso ao recurso. Da mesma forma que a anotação @RequiredPermission, a anotação @RequiredRole pode ser usada a nível de classe para proteger todos os métodos contidos nessa classe.
Protegendo porções do código É possível proteger apenas parte de um código ao invés de todo o método ou toda a classe. Isso pode ser necessário em expressões condicionais, onde um trecho só deve ser executado caso o usuário possua a autorização necessária. Para isso voltamos a usar a interface SecurityContext, pois ela contém métodos que são funcionalmente equivalentes às anotações @RequiredPermission e @RequiredRole. Como um exemplo, vamos supor que ao remover um bookmark um email seja enviado ao administrador, mas se o próprio administrador executou a operação não é necessário enviar o email.
Protegendo porções de páginas <code>Java Server Faces</code> As restrições de segurança podem ser utilizadas ainda em páginas web, com o auxílio de Expression Language. A interface SecurityContext está automaticamente disponível para páginas Java Server Faces como um bean de nome securityContext, bastando então acessar seus métodos a partir deste bean. ]]> Nesse caso, a habilitação de um botão está condicionada à existência de permissão para o usuário autenticado no momento executar a operação “insert” no recurso “contact”.
Redirecionando automaticamente para um formulário de acesso Se sua aplicação usa a extensão demoiselle-jsf ou se você utilizou o arquétipo demoiselle-jsf-jpa durante a criação de seu projeto, então você pode definir uma página de login e o Framework Demoiselle vai automaticamente lhe redirecionar para essa página caso haja a tentativa de acessar um recurso protejido e nenhum usuário esteja autenticado no sistema. Para acrescentar a extensão demoiselle-jsf em um projeto Maven, adicione a dependência abaixo no arquivo pom.xml. br.gov.frameworkdemoiselle demoiselle-jsf compile ]]> O arquétipo demoiselle-jsf-jpa já contém essa extensão, se você criou seu projeto baseado nesse arquétipo nada precisa ser feito. Por padrão a página contendo o formulário de login deve se chamar login.jsp ou login.xhtml (a depender de como sua aplicação esteja configurada para mapear páginas JSF). Para mudar esse padrão, é possível editar o arquivo demoiselle.properties para configurar qual página deve ser utilizada. Propriedades de segurança da extensão <emphasis>demoiselle-jsf</emphasis> Propriedade Descrição Valor padrão frameworkdemoiselle.​security.​login.​page Define a página de login da aplicação. "/login" frameworkdemoiselle.​security.​redirect.​after.​login Define a tela para qual o usuário será redirecionado após o processo de login bem sucedido. "/index" frameworkdemoiselle.​security.​redirect.​after.​logout Define a tela para qual o usuário será redirecionado após o processo de logout bem sucedido. "/login" frameworkdemoiselle.​security.​redirect.​enabled Habilita ou desabilita o redirecionamento automático para a página de login após uma tentativa de acessar recurso protegido. true
Integrando o Framework Demoiselle com a especificação JAAS Até agora vimos como criar código protegido em uma aplicação Demoiselle, mas nada foi dito sobre a tecnologia que implementa essa proteção. A verdade é que o Framework Demoiselle dá ao desenvolvedor a liberdade de implementar a solução que mais se adequa ao sistema desenvolvido, mas o framework também conta com suporte nativo à especificação JAAS (Java Authentication and Authorization Service). O suporte a JAAS é fornecido para aplicações WEB e está implementado na extensão demoiselle-servlet, então é necessário declarar a dependência a essa extensão em sua aplicação. Para acrescentar a extensão demoiselle-servlet em um projeto Maven, adicione a dependência abaixo no arquivo pom.xml. br.gov.frameworkdemoiselle demoiselle-servlet compile ]]> O arquétipo demoiselle-jsf-jpa já conta com a dependência à extensão demoiselle-jsf, que por sua vez depende da extensão demoiselle-servlet. Se sua aplicação é baseada no arquétipo demoiselle-jsf-jpa você já possui a extensão demoiselle-servlet. Uma vez que sua aplicação contenha a extensão demoiselle-servlet, tudo que você precisa fazer é configurar o suporte a JAAS em seu servidor de aplicação e criar os usuários e papéis necessários. Esta configuração depende do servidor de aplicação utilizado e foge ao escopo deste documento. Para autenticar um usuário presente no servidor de aplicação através do JAAS, a extensão demoiselle-servlet oferece a classe Credentials, que deve ser injetada em seu código. O código abaixo mostra como realizar a autenticação a partir de um servlet. Uma vez autenticado o usuário, a anotação @RequiredRole passará a verificar se o usuário presente no JAAS possui o papel informado. A especificação JAAS não prevê o uso de permissões para proteger recursos, apenas papéis de usuários. Por isso ao utilizar a segurança da especificação JAAS o uso da anotação @RequiredPermission fica vetado. Utilizar essa anotação em um sistema que utilize JAAS para autorização causará uma exceção quando o recurso for acessado. É possível utilizar o JAAS para autenticar e autorizar papéis de usuários mas criar sua própria implementação para implementar a autorização de permissões. Para isso crie uma classe que herde a classe br.gov.frameworkdemoiselle.security.ServletAuthorizer e sobrescreva o método hasPermission(String resource, String operation) para implementar seu próprio mecanismo. Feito isso, basta definir sua classe no arquivo demoiselle.properties usando a propriedade frameworkdemoiselle.security.authorizer.class. Mais detalhes sobre como criar sua própria implementação ou extender uma implementação existente podem ser vistos na seção Criando sua implementação.
Criando sua implementação Para os mecanismos de autenticação não cobertos pelo Framework Demoiselle, é possível criar sua própria implementação e integra-la ao framework. Também é possível extender uma implementação existente e acrescentar funcionalidades inexistentes. O ponto de extensão para o módulo de segurança são as interfaces Authenticator e Authorizer. Para criar um novo mecanismo de autenticação e autorização, é necessário apenas implementar essas duas interfaces em sua aplicação. Segue abaixo um exemplo de implementação. Fique atento! Note que para garantir que o usuário não esteja logado, não basta lançar uma exceção (InvalidCredencialsException) no método authenticator.autenticate(), mas é preciso que o método authenticator.getUser() retorne null para que o método securityContext.isLoggedIn() retorne false. Pronto! Sua aplicação já possui uma implementação de segurança definida. Você nunca deve chamar diretamente em sua aplicação as implementações das interfaces Authenticator e Authorizer, o Framework Demoiselle vai automaticamente chamar os métodos implementados quando for necessário. Em um sistema que use as anotações @RequiredRole ou @RequiredPermission, deve haver pelo menos uma implementação dessas duas interfaces. Ao processar essas anotações, o Framework Demoiselle vai buscar uma implementação para essas interfaces e disparar uma exceção caso não encontre uma implementação adequada. Se existe mais de uma implementação de Authenticator e/ou Authorizer (o que pode acontecer, por exemplo, quando seja necessário uma implementação na aplicação principal e outra para os testes), é possível definir no arquivo demoiselle.properties a classe que deve ser usada por padrão: Propriedade Descrição Valor padrão frameworkdemoiselle.​security.​authenticator.​class Define a classe concreta utilizada como implementação da interface Authenticator nenhum, se houver apenas uma implementação o framework a detectará automaticamente sem necessidade de definir essa propriedade frameworkdemoiselle.​security.​authorizer.​class Define a classe concreta utilizada como implementação da interface Authorizer nenhum, se houver apenas uma implementação o framework a detectará automaticamente sem necessidade de definir essa propriedade