Demoiselle Framework's Blog

25/02/2011

Segurança no Demoiselle 2 através de EJB 3.1 no JBoss 6.0

Arquivado em: Demoiselle,ejb,jboss6,segurança — esaito @ 18:41

Demonstraremos como utilizar JAAS+EJB 3.1 com Demoiselle 2.0 no JBoss 6.0.

O Framework Demoiselle em sua versão 2.x trás diversas mudanças e melhorias com relação à versão anterior, sendo a principal delas a sua completa aderência à especificação JSR 316: Java Platform, Enterprise Edition 6, ou simplesmente Java EE 6. Assim, a compatibilidade com soluções voltadas à esta especificação é garantida.

Por isso a utilização do JAAS(Java Authentication and Authorization Service) como solução de autenticação e EJB 3.1 para Autorização é relativametne simples de ser efetuada.

Relembrando para aqueles que já conhecem, ou esclarecendo para os demais, na versão anterior do Demoiselle havia uma solução de segurança baseada também no JAAS que provia uma anotação (@RequiredRole) que era utilizada para anotar os métodos no Bussiness Controller e assim garantir o controle de acesso.

Na versão atual é possível que se utilize as facilidades providas pela especificação EJB 3.1 que já é implementada pelos servidores de aplicação como é o caso do JBoss 6.0 que será demonstrado neste texto.

A razão de um exemplo especifico para um determinado servidor de aplicação é porque cada implementação servidor possuí alguns pequenos detalhes em sua solução, mas a maioria das instruções são semelhantes pela especificação EJB 3.1. Mas tentaremos abranger sempre que possível, em outros posts, o uso com outros servidores.

Para fins de entendimento e facilitar a explicação tomaremos como base a aplicação de exemplo chamada Contactlist-JSF (https://demoiselle.svn.sourceforge.net/svnroot/demoiselle/sample/tags/contactlist-2.0.0) que é uma implementação utilizando o Demoiselle 2 e o ambiente de Desenvolvimento o Eclipse Helios e Jboss 6.0 disponibilizado pelo projeto Infra (http://demoiselle.sourceforge.net/infra).

Feita a importação do projeto (File → Import → Check Out Maven Project from SCM), teremos uma aplicação funcional mas ainda sem nenhum mecanismo de autenticação e autorização. Consideraremos também que a integração com o servidor esteja configurada.

Primeiro, temos modificar o comportamento da aplicação para que ela acione os mecanismos do JAAS, alterando o arquivo web.xml (localizado em /src/main/webapp/WEB-INF/), incluindo as tag abaixo:

<security-constraint>
  <display-name>JAAS Security </display-name>
  <web-resource-collection>
   <web-resource-name>all</web-resource-name>
   <url-pattern>/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
   <role-name>*</role-name>
  </auth-constraint>
 </security-constraint>
 <login-config>
  <auth-method>FORM</auth-method>
  <form-login-config>
   <form-login-page>/login.xhtml</form-login-page>
   <form-error-page>/access.xhtml</form-error-page>
  </form-login-config>
 </login-config>

Na tag login-config, apontamos as páginas (formulários) de login e error, que serão as paginas personalizadas para o projeto que irão responder nestes eventos. Isso porque informamos como FORM no <auth-method>. Se optar por não utilizar formulários, pode ser informado o método BASIC, mas neste caso não haverá controle dos casos de erros e nem personalização da página de login. Estas páginas deverão ficar no diretório /src/main/webapp/ ou em um subdiretório a partir deste (que deverá ser especificado na tag), as paginas estão listadas abaixo:

login.xhtml:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
	<h:head>
    	<title>Tela de Login</title>
	</h:head>
	    <h:body>
		    <h2>Digite as informações de Login: </h2>
	        <form method="post" action="j_security_check">

	        <table style="width: 80%">
				<tr>
				<td align="right"><label for="username">Usuário</label></td>
				<td align="left"><input type="text" id="username" name="j_username"/></td>
				</tr>
				<tr>
				<td align="right"><label for="password">Senha</label></td>
				<td align="left"><input type="password" id="password" name="j_password"/></td>
				</tr>
				<tr>
				<td></td>
				 <td > <input type="submit" value="Entrar"/> </td>
				 </tr>
			</table>
			</form>
	    </h:body>
</html>

access.xhtml:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
	<h:head>
    	<title>Erro de Login</title>
	</h:head>
	    <h:body>
		    <h2>Erro de Permissão</h2>
	        <form method="post" action="j_security_check">
	            <h:messages />

	            <table style="width: 80%">
				<tr>
				<td align="right"><label for="username">Usuário</label></td>
				<td align="left"><input type="text" id="username" name="j_username"/></td>
				</tr>
				<tr>
				<td align="right"><label for="password">Senha</label></td>
				<td align="left"><input type="password" id="password" name="j_password"/></td>
				</tr>
				<tr>
				<td></td>
				 <td > <input type="submit" value="Tentar Novamente"/> </td>
				 </tr>
			</table>
			</form>
	    </h:body>
</html>

Com estas alterações, a aplicação já está preparada para fazer a chamada ao JAAS para efetuar a autenticação (login) na aplicação, ainda sem considerar as questões de autorização. Mas se executarmos a aplicação desta forma, apesar de não haver regras de acesso, não temos ainda as informações dos usuários e papéis.

O próximo passo é configurar o servidor Jboss 6.0 com as informações de usuários e papéis.

Identifique o diretório de instalação do servidor Jboss 6.0 (para usuários do pacote infra é /opt/demoiselle/server/jboss-6.0) e dentro dele encontre a pasta de configurações /server/default/conf. Nesta pasta inclua dois arquivos:

 

  • defaultRoles.properties, é onde se define quais roles estarão definidas para cada usuário, exempo:
aluno=role_aluno
professor=role_professor
secretaria=role_secretaria, role_professor

 

  • defaultUsers.properties, é a definição dos usuários e senhas, no exemplo abaixo criamos três usuários cujas senhas para cada um é o mesmo nome do usuário:
aluno=aluno
professor=professor
secretaria=secretaria

Até aqui, é o suficiente apenas para autenticação pelo servidor, mas é importante já testar a aplicação, fazendo o “deploy” e iniciando o servidor. De acordo com o exemplo acima informe o usuário e senha para acessar a aplicação. Esse teste é necessário para garantir que o que foi feito até o momento está correto e as demais alterações dependem desta parte. Veja o exemplo da aplicação executando:

 

Exemplo de Tela de Login

Agora que já temos uma tela de Autenticação, podemos partir para as modificações que irão garantir o controle de permissões por papel, e para isso utilizaremos as funcionalidades do EJB 3.1.

Para utilização do EJB 3.1, é necessária a inclusão das dependências no arquivo de configuração do Maven, o POM.XML, incluindo as seguintes linhas dentro da tag <dependecies>:


<dependency>
         	<groupId>org.jboss.ejb3</groupId>
         	<artifactId>jboss-ejb3-api</artifactId>
         	<version>3.1.0</version>
         	<scope>provided</scope>
	</dependency>
	<dependency>
         	<groupId>org.jboss.ejb3</groupId>
         	<artifactId>jboss-ejb3-ext-api</artifactId>
         	<version>1.1.1</version>
         	<scope>provided</scope>
       </dependency>

Note que as dependências estão com o escopo “provided” que nos informa que as bibliotecas já estão disponíveis no Jboss 6.0, mas precisamos apontá-las no projeto para conseguir fazer a compilação dentro do Eclipse.

Por definição, para usarmos as funcionalidades do EJB 3.1, precisamos obviamente de EJBs na nossa aplicação. E começaremos criando um novo pacote que conterá estas implementações, conforme exemplificado na figura abaixo:

 

Pacote para EJB

Uma das peculiaridades do Jboss é que devemos informar ao EJB qual é o domínio do login de segurança utilizaremos, que é uma referência a um

<application-policy>

configurado no arquivo login-config.xml (…/server/default/conf/).

No nosso exemplo não faremos nenhum personalizado e utilizaremos o “other” que vem por padrão. Faremos isso criando uma interface chamada Domain.java para armazenar essa informação e assim não ficar replicando constantes, ficando assim:

package contactlist.ejb;

public interface Domain {

	String OTHER = "other";
}

Seguindo o mesmo padrão, vamos definir outra interface chamada Roles.java para os papéis (role names) que foram configurados no Jboss, da seguinte forma:

package contactlist.ejb;

public interface Roles {

	  /**
	    * Apenas consultas
	    */
	   String ALUNO = "role_aluno";

	   /**
	    * Não executará exclusões.
	    */
	   String PROFESSOR = "role_professor";

	   /**
	    * Acesso à todas as funções
	    */
	   String SECRETARIA = "role_secretaria";
}

A estratégia para criação dos EJBs neste exemplo, será de criar um para cada BusinessController da aplicação. Da forma como ainda se encontra, o ManagedBean (@ViewController) aciona os métodos do BusinessController, mas para utilizarmos a estratégia do EJB, criaremos os EJBs como fachadas (@FacadeController) que farão esta interface entre os Managed Beans e os BusinessController. Se desejasse poderia criar apenas um EJB. Usaremos a anotação @Stateless para criar os EJBs, que é o mais óbvio devido à simplicidade da aplicação. Desta forma o nosso código será o seguinte:

ContactEJB.java:

package contactlist.ejb;

import java.util.List;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ejb.Stateless;
import javax.inject.Inject;
import org.jboss.ejb3.annotation.SecurityDomain;
import contactlist.business.ContactBC;
import contactlist.domain.Contact;
import br.gov.frameworkdemoiselle.stereotype.FacadeController;;

@Stateless
@SecurityDomain(Domain.OTHER)
@FacadeController
public class ContactEJB {

	@Inject
	private ContactBC contactBC;

	@RolesAllowed({Roles.SECRETARIA})
	public void delete(Long id) {
		contactBC.delete(id);
	}

	@RolesAllowed({Roles.PROFESSOR, Roles.SECRETARIA})
	public void insert(final Contact contact) {
		contactBC.insert(contact);
	}

	@RolesAllowed({Roles.PROFESSOR, Roles.SECRETARIA})
	public void update(Contact contact) {
		contactBC.update(contact);
	}

	@PermitAll
	public Contact load(Long id) {
		return contactBC.load(id);
	}

	@PermitAll
	public List<Contact> findAll() {
		return contactBC.findAll();
	}

}

AuditEJB.java:

package contactlist.ejb;

import java.util.List;
import javax.annotation.security.RolesAllowed;
import javax.ejb.Stateless;
import javax.inject.Inject;
import org.jboss.ejb3.annotation.SecurityDomain;
import br.gov.frameworkdemoiselle.stereotype.FacadeController;
import contactlist.business.AuditBC;
import contactlist.domain.Audit;

@Stateless
@SecurityDomain(Domain.OTHER)
@FacadeController
public class AuditEJB {

	@Inject
	private AuditBC auditBC;

	// Apenas os usuários da secretaria
	@RolesAllowed({Roles.SECRETARIA})
	public List<Audit> findAll() {
		return auditBC.findAll();
	}
}

Agora temos que alterar os Managed Beans, que estão no pacote contactlist.view, para utilizar os EJBs. Que ficarão respectivamente assim:

AuditMB.java:

package contactlist.view;

import java.util.List;
import javax.inject.Inject;
import br.gov.frameworkdemoiselle.stereotype.ViewController;
import contactlist.domain.Audit;
import contactlist.ejb.AuditEJB;
import contactlist.template.PagedListPageBean;

@ViewController
public class AuditMB extends PagedListPageBean<Audit, Long> {

	private static final long serialVersionUID = 1L;

	@Inject
	private AuditEJB auditEJB;

	@Override
	protected List<Audit> handleResultList() {
		return auditEJB.findAll();
	}

	@Override
	protected void handleDelete(Long id) {
		// Does nothing
	}
}

ContactListMB.java

package contactlist.view;

import static contactlist.view.ViewId.CONTACT_EDIT;
import java.util.List;
import javax.inject.Inject;
import br.gov.frameworkdemoiselle.annotation.NextView;
import br.gov.frameworkdemoiselle.stereotype.ViewController;
import contactlist.domain.Contact;
import contactlist.ejb.ContactEJB;
import contactlist.template.PagedListPageBean;

@ViewController
@NextView(CONTACT_EDIT)
public class ContactListMB extends PagedListPageBean<Contact, Long> {

	private static final long serialVersionUID = 1L;

	@Inject
	private ContactEJB contactEJB;

	protected List<Contact> handleResultList() {
		List<Contact> result = null;

		result = contactEJB.findAll();

		return result;
	}

	@Override
	protected void handleDelete(Long id) {
		contactEJB.delete(id);
	}
}

ContactEditMB.java:

package contactlist.view;

import static contactlist.view.ViewId.CONTACT_LIST;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.SelectItem;
import javax.inject.Inject;
import br.gov.frameworkdemoiselle.annotation.Name;
import br.gov.frameworkdemoiselle.annotation.PreviousView;
import br.gov.frameworkdemoiselle.exception.ExceptionHandler;
import br.gov.frameworkdemoiselle.stereotype.ViewController;
import br.gov.frameworkdemoiselle.template.AbstractEditPageBean;
import br.gov.frameworkdemoiselle.transaction.Transactional;
import br.gov.frameworkdemoiselle.util.Faces;
import contactlist.domain.Contact;
import contactlist.domain.Phone;
import contactlist.domain.PhoneType;
import contactlist.ejb.ContactEJB;
import contactlist.exception.DuplicatedCpfException;

@ViewController
@PreviousView(CONTACT_LIST)
public class ContactEditMB extends AbstractEditPageBean<Contact, Long>  {

	private static final long serialVersionUID = 1L;

	private DataModel<Phone> phones;

	@Inject
	private ContactEJB contactEJB;

	@Inject
	@Name("messages")
	private ResourceBundle bundle;

	public void addPhone() {
		getBean().addPhone(new Phone());
	}

	public void deletePhone() {
		getBean().getPhones().remove(getPhones().getRowData());
	}

	public DataModel<Phone> getPhones() {
		if (phones == null) {
			phones = new ListDataModel<Phone>(getBean().getPhones());
		}

		return phones;
	}

	@ExceptionHandler
	public void handleException(final DuplicatedCpfException cause) {
		Faces.addMessage("cpf", cause);
	}

	public List<SelectItem> getPhoneTypes() {
		List<SelectItem> items = new ArrayList<SelectItem>();

		for (PhoneType phoneType : PhoneType.values()) {
			items.add(new SelectItem(phoneType, bundle.getString(phoneType.toString())));
		}

		return items;
	}

	@Override
	@Transactional
	public String delete() {
		this.contactEJB.delete(getId());
		return getPreviousView();
	}

	@Override
	@Transactional
	public String insert() {
		this.contactEJB.insert(getBean());
		return getPreviousView();
	}

	@Override
	@Transactional
	public String update() {
		this.contactEJB.update(getBean());
		return getPreviousView();
	}

	@Override
	protected void handleLoad() {
		setBean(this.contactEJB.load(getId()));
	}
}

Este último código é o que mais sofreu alterações, devido ao fato de não usarmos o Template de Edição da aplicação que atualmente não está preparada para trabalhar com o EJB.

 

Agora basta republicar a aplicação e iniciar novamente.

Experimente logar com o usuário aluno por exemplo e tentar fazer alguma inclusão, alteração ou acessar a log de auditoria. O sistema apresentará o erro:

javax.servlet.ServletException: javax.ejb.EJBAccessException: Caller unauthorized

Como o objetivo do post é demonstrar o uso do EJB 3.1  para controle de autorização, não exemplificaremos como tratar estas exceções. Para melhorar o código ou preparar sua própria aplicação consulte a documentação neste link: http://demoiselle.sourceforge.net/docs/

22/02/2011

Arquivado em: Demoiselle,Release — Robson Ximenes @ 10:06
Tags: , ,

O time do Demoiselle está feliz em anunciar a liberação da versão estável do framework: 2.0.1.

Confira o que foi tratado nesta versão:

demoiselle - 2.0.1

  • 0000538: [Bug] Possível loop infinito em ResourceBundleFactory
  • 0000523: [Improvement] Possibilitar injeção com jUnit
  • 0000522: [Improvement] Renomear a extensão Swing para SE
  • 0000521: [Bug] Dependencia indevida do componente demoiselle-validation-1.0.0-SNAPSHOT
  • 0000517: [Bug] Tratamento de exceções não funciona com o Weld 1.1.0-CR4
  • 0000518: [Improvement] Possibilitar o deploy de aplicações JSF em servidores Tomcat 6 e 7
  • 0000534: [Task] Disponibilizar o site do framework

Além destas correções foi criado um estilo visual que identifica a linha 2.x do framework e componentes compatíveis, veja o site em: http://demoiselle.sf.net/framework

11/02/2011

Criando um componente para o demoiselle

Arquivado em: Demoiselle Components — Robson Ximenes @ 11:29
Tags: , , ,

Olá pessoal! Muitas vezes o usuários do demoiselle perguntam como fazer para disponibilizar um componente que exista em seu projeto.

O caminho não é complicado! Primeiro é necessário colocar o projeto na estrutura do demoiselle. Para facilitar isto criamos um arquétipo maven para a criação de projetos do tipo componente.

Este exemplo foi feito no eclipse, mas é adaptável para qualquer IDE com suporte ao Maven2+.

Crie um novo projeto do tipo maven:

Criando um novo projeto maven

Criando um novo projeto maven

Aproxima dela você deve prosseguir(Next) para a seleção do arquétipo. Se você já tiver configurado o catalogo do Demoiselle basta seleciona-lo e escolher o arquétipo demoiselle-archetype-component

Selecionando o arquétipo de componente

Selecionando o arquétipo de componente

Obs.: Caso não possua o catalogo configurado basta adicionar um catalogo remoto com o seguinte endereço: http://demoiselle.sourceforge.net/repository/archetype-catalog.xml

Em seguida você deve informar o groupid = br.gov.frameworkdemoiselle e o artifactid com o nome do seu componente.

Configurando o projeto

Configurando o projeto

Então será criado um projeto com a estrutura do demoiselle, com uma classe de exemplo e sua classe de testes. O pom já estará configurado com detalhes do ambiente do demoiselle, bem como um parent contendo dependências de outras bibliotecas(são opcionais, você deve adiciona-las sem versão para usa-las).

Estrutura do projeto de componentes

Estrutura do projeto de componentes

Observem que a estrutura já disponibiliza uma classe de exemplo App.java e sua respectiva classe de testes no mesmo pacote, porém no folder de testes.

É disponibilizado também uma documentação de exemplo para o site maven em src/site.

Você deve explicar todo o funcionamento do componente.

A pasta portal é utilizada apenas no momento de deploy do site do componente para que o mesmo seja categorizado no portal de componentes.

http://demoiselle.sf.net/component

Pronto agora você já pode iniciar seu componente ou apenas mover as classes de um previamente implementado. Vale lembrar que todo novo componente deve ser submetido no http://tracker.frameworkdemoiselle.gov.br na forma de uma feature request, para que o mesmo seja avaliado.

Tema: Rubric. Blog no WordPress.com.

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.

Join 461 other followers

%d bloggers like this: