sábado, 1 de novembro de 2008

ADF BC: Usando History Columns sem JAAS

Olá pessoal!

Neste post vou chamar a atenção para um artifício extremamente útil durante a programação ADF BC. Encontrei esta solução em um artigo neste link abaixo, que depois apliquei e testei em projetos: http://my.opera.com/dominionspy/blog/show.dml/575983

Em resumo: o ADF BC possui um mecanismo muito prático de rastreamento de registro por usuário, que é chamado de History Columns. Imaginem que temos um campo, CREATED_BY, na nossa tabela no qual queremos gravar o nome do usuário que criou o registro. O ADF BC permite que marquemos este campo como History Column ao editarmos o Entity Object e visualizarmos as propriedades do atributo CreatedBy, mapeado para essa coluna. Ao marcarmos este campo como History Column, também selecionamos o tipo de histórico para "Created By" para que automaticamente o BC pegue o usuário logado e grave-o nessa coluna durante a criação, sem nenhuma intervenção do programador. Simples, não?

Essa funcionalidade tem um porém: o BC só consegue pegar o usuário logado nativamente se você estiver usando a segurança declarativa, via WEB.XML (as tags por exemplo) e JAAS. Sabemos, no entanto, que em muitos dos nossos projetos o uso do JAAS é impossível, devido a limitações do framework (por exemplo, o JAAS só nos permite ter os dados de Usuário e Senha, nada mais) ou então de limitações do projeto, por já haver componentes de segurança feitos em outra tecnologia, por exemplo, Http Filters. Felizmente, uma simples mudança pode permitir que utilizemos esse outro método de autenticação sem perder as History Columns.

Funciona assim: o BC, para buscar o usuário logado, utiliza o método getRemoteUser() da Request HTTP, ou seja, o usuário que é populado pelo JAAS ao autenticar. Porém, existe um objeto chamado HttpServletRequestWrapper que permite que "embalemos" uma request original em um objeto customizado e passemos adiante através de um Filtro por exemplo, sobrescrevendo o método getRemoteUser() original e retornando nosso próprio usuário. Para isso, siga os passos adiante, que estão listados inclusive no link citado no início da página:

1) Crie um objeto que implemente a interface java.security.Principal, é o objeto que vai guardar os dados de usuário. Tem dois métodos: getName() e getRole(). Exemplo:

import java.security.Principal;

public class AuthUserPrincipal implements Principal {
private String username;
private int role;

public AuthUserPrincipal(String _username, int _role) {
this.username = _username;
this.role = _role;
}

public String getName() {
return username;
}

public int getRole() {
return role;
}
}


2) Crie uma classe que estenda javax.servlet.http.HttpServletRequestWrapper. Esta classe deve conter um construtor que permita informar o nome de usuário, e sobrescrever os métodos isUserInRole() e getRemoteUser. Exemplo:

import java.security.Principal;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class AuthRequestWrapper extends HttpServletRequestWrapper {
private AuthUserPrincipal principal;

public AuthRequestWrapper(HttpServletRequest request, String usuario, String role)
super(request);
this.principal = new AuthUserPrincipal(usuario, role);
}

public boolean isUserInRole(String string) {
return String.valueOf(principal.getRole()).equals(string);
}

public String getRemoteUser() {
return principal.getName();
}

public Principal getUserPrincipal() {
return principal;
}
}


3) Crie um filtro Http que "embale" o HttpServletRequest que recebe de parâmetro no seu HttpServletRequestWrapper. Por exemplo, supondo que seu filtro de autenticação colocou o nome do usuário e role na sessão Http:

public class BCAuthFilter extends javax.servlet.Filter {
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
String user = request.getSession().getAttribute("user"); // Usuario
String role = request.getSession().getAttribute("role"); // Role

AuthRequestWrapper wrap = new AuthRequestWrapper(request, user, role);
chain.doFilter(wrap, resp);
}
}


4) Configure este filtro no seu Web.xml. IMPORTANTE: Você deve configurar este filtro antes dos filtros adfBindings e adfFaces, mas DEPOIS do seu filtro de autenticação:


BCAuthFilter
com.custom.BCAuthFilter


BCAuthFilter
/*



Desta maneira o seu aplicativo irá se logar através do filtro de autenticação; em seguida, o seu BCAuthFilter irá sobrescrever esses dados no Request, para que depois o ADFBindingFilter consiga recuperar esse dado em sua inicialização, através do request.getRemoteUser() (que na verdade executará o metodo sobrescrito do seu Wrapper).

Abraços!

Nenhum comentário: