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.RequiredPermissionInterceptorbr.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 DemoisellePropriedadeDescriçãoValor padrãoframeworkdemoiselle.security.enabled
Habilita ou desabilita o mecanismo de segurança
trueframeworkdemoiselle.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 @RequiredPermissionPermissão por papel, através da anotação @RequiredRoleProtegendo o sistema com @RequiredPermission
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 @RequiredRole
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 Java Server Faces
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.frameworkdemoiselledemoiselle-jsfcompile
]]>
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 demoiselle-jsfPropriedadeDescriçãoValor padrãoframeworkdemoiselle.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.frameworkdemoiselledemoiselle-servletcompile
]]>
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:
PropriedadeDescriçãoValor padrãoframeworkdemoiselle.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