Autenticação e Autorização
Em 90% das aplicações, é utilizado algum tipo de autenticação e autorização.
O NEO disponibiliza um filtro que é responsável por validar o usuário logado,
checar se aquele usuário tem permissão a um determinado recurso no sistema, e também autenticá-lo.
Para que a autenticação funcione são necessários os seguintes componentes:
- Filtro de autenticação: Filtro responsável por gerenciar a autenticação/autorização
- JSP para login: Formulário para envio dos dados para o filtro.
- AuhtorizationModules: Responsável por definir o que pode ser visto em cada controller.
- Página principal do sistema: Necessário para redirecionar a requisição para uma primeira página.
- Entidades para representar as funcionalidades no sistema.
- AuthorizationDAO: Responsável por carregar os dados necessários para autenticação.
Configuração do web.xml
Primeiramente é necessário configurar o web.xml, e dizer a ele para interceptar as requisições, e a partir dela validar a
permissão do usuário.
Conforme explicado no capítulo 2,
Primeira aplicão, é necessário ter ao menos um módulo configurado.
Adicione o seguinte código ao seu web.xml:
[JSP]
<!-- Autenticação / Autorização -->
<filter>
<filter-name>authenticationControlFilter</filter-name>
<filter-class>br.com.linkcom.neo.authorization.AuthenticationControlFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>authenticationControlFilter</filter-name>
<url-pattern>/modulo/*</url-pattern>
</filter-mapping>
Suponhamos que em sua aplicação estivesse configurado mais de um módulo, portanto ficaria assim:
[JSP]
<!-- Autenticação / Autorização -->
<filter>
<filter-name>authenticationControlFilter</filter-name>
<filter-class>br.com.linkcom.neo.authorization.AuthenticationControlFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>authenticationControlFilter</filter-name>
<url-pattern>/modulo/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>authenticationControlFilter</filter-name>
<url-pattern>/modulo2/*</url-pattern>
</filter-mapping>
O filtro deve estar declarado no web.xml antes dos servlets que mapeiam os módulos.
Configuração do applicationConfig.xml
Também é necessário configurar a maneira que o NEO se comporta na autenticação/autorização. Para isso
será utilizado o Spring, para injetar valores na classe DefaultConfig.
No seu arquivo applicationConfig.xml, adicione o seguinte trecho de código:
[JSP]
<bean name="config" class="br.com.linkcom.neo.core.config.DefaultConfig" autowire="autodetect">
<property name="authenticationConfig">
<ref bean="authenticationConfig" />
</property>
</bean>
<bean id="authenticationConfig" class="br.com.linkcom.neo.core.config.AuthenticationConfig">
<property name="loginPage" value="/jsp/login.jsp"/>
<property name="indexPage" value="/modulo/index"/>
<property name="useStaticModuleAccess" value="false"/>
</bean>
-
Definição da página de login
(linha 8)
Define qual jsp que será chamado para apresentar a tela de login.
-
Definição da página principal
(linha 9)
Define qual é o index da sua aplicação.
-
Definição de uso de regras estáticas
(linha 10)
Define se o NEO deverá utilizar as regras em um arquivo, dessa maneira o acesso aos módulos
passará a se comportar de maneira fixa.
Desenvolvimento da tela de login
Em seguida é necessário termos uma tela de login, que possui a função básica de submeter os dados para que o filtro
possa autenticar o usuário, e registrá-lo na aplicação. A url que receberá os dados é /contexto/seumodulo/neo_security_manager.
No diretório Web-Root/jsp coloque o arquivo descrito abaixo com o nome login.jsp:
[JSP]
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<HTML>
<HEAD>
<TITLE>NEO FRAMEWORK</TITLE>
</HEAD>
<BODY leftmargin="0" topmargin="0" rightmargin="0" style="padding:0px; margin:0px;">
<form name="loginForm" action="/${ctx}/modulo/neo_security_manager" method="post">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td width="47%" height="80">
</td>
<td width="51%">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td colspan="2"><c:if test="${!empty login_error}">
<div id="error">Login / senha inválidos.</div>
</c:if>
</td>
</tr>
<tr>
<td><span class="style8"> Login</span></td>
<td><input type="text" name="username" id="username"/></td>
</tr>
<tr>
<td>Senha</td>
<td><input type="password" name="password" size="14"/></td>
</tr>
<tr>
<td align="right" colspan="2"><input type="submit" value="Entrar"/></td>
</tr>
</table>
</form>
</BODY>
</HTML>
Estrutura do banco de dados.
Crie no seu banco de dados a seguinte estrutura:
CREATE TABLE "usuario" (
"id" INTEGER NOT NULL,
"nome" VARCHAR(80) NOT NULL,
"login" VARCHAR(50) NOT NULL,
"password" VARCHAR(50) NOT NULL,
CONSTRAINT "usuario_pkey" PRIMARY KEY("id")
) ;
CREATE TABLE "papel" (
"id" INTEGER NOT NULL,
"description" VARCHAR(80) NOT NULL,
"name" VARCHAR(50),
CONSTRAINT "papel_pkey" PRIMARY KEY("id")
) ;
CREATE TABLE "permissao" (
"id" INTEGER NOT NULL,
"permissionString" VARCHAR(80) NOT NULL,
"path" VARCHAR(100) NOT NULL,
"role_id" INTEGER NOT NULL,
"path" VARCHAR(50),
CONSTRAINT "permissao_pkey" PRIMARY KEY("id"),
CONSTRAINT "fk_permissao_papel" FOREIGN KEY ("id")
REFERENCES "papel"("id")
ON DELETE NO ACTION
ON UPDATE NO ACTION
NOT DEFERRABLE,
);
CREATE TABLE "usuariopapel" (
"id" INTEGER NOT NULL,
"usuario_id" INTEGER NOT NULL,
"papel_id" INTEGER NOT NULL,
CONSTRAINT "usuariopapel_pkey" PRIMARY KEY("id"),
CONSTRAINT "usuariopapel_fk" FOREIGN KEY ("usuario_id")
REFERENCES "public"."usuario"("id")
ON DELETE CASCADE
ON UPDATE NO ACTION
NOT DEFERRABLE,
CONSTRAINT "usuariopapel_fk1" FOREIGN KEY ("papel_id")
REFERENCES "public"."papel"("id")
ON DELETE NO ACTION
ON UPDATE NO ACTION
NOT DEFERRABLE
) ;
Esta ddl foi gerada no PostGreSQL. Caso utilize outro SGBD serão necessárias adaptações.
Estrutura dos beans.
No pacote que é localizado as suas entidades do hibernate, adicione as seguintes classes:
Usuario.java
package entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import br.com.linkcom.neo.authorization.User;
import br.com.linkcom.neo.bean.annotation.DescriptionProperty;
import br.com.linkcom.neo.types.Password;
/**
* Representa um usuário no sistema
*/
@Entity
public class Usuario implements User {
Integer id;
String nome;
String login;
String password;
@Id
@GeneratedValue
public Integer getId() {
return id;
}
//API
public String getLogin() {
return login;
}
@DescriptionProperty
public String getNome() {
return nome;
}
//API
@Password
public String getPassword() {
return password;
}
public void setId(Integer id) {
this.id = id;
}
public void setLogin(String login) {
this.login = login;
}
public void setNome(String nome) {
this.nome = nome;
}
public void setPassword(String password) {
this.password = password;
}
}
Papel.java
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import br.com.linkcom.neo.authorization.Role;
import br.com.linkcom.neo.bean.annotation.DescriptionProperty;
/**
* Representa um papel no sistema, um nível de permissão.
* Ex.: Administrador, Usuário, Financeiro
*/
@Entity
public class Papel implements Role {
Integer id;
String description;
String name;
@Id
@GeneratedValue
public Integer getId() {
return id;
}
//API
@DescriptionProperty
public String getDescription() {
return description;
}
//API
public String getName() {
return name;
}
public void setDescription(String description) {
this.description = description;
}
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
}
UsuarioPapel.java
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
/**
* Faz o relacionamento entre Usuário e Papel
* Relacionamento muitos para muitos entre usuario e papel
*/
@Entity
public class UsuarioPapel {
Integer id;
Usuario usuario;
Papel papel;
@Id
@GeneratedValue
public Integer getId() {
return id;
}
@ManyToOne(fetch=FetchType.LAZY)
public Papel getPapel() {
return papel;
}
@ManyToOne(fetch=FetchType.LAZY)
public Usuario getUsuario() {
return usuario;
}
public void setId(Integer id) {
this.id = id;
}
public void setPapel(Papel papel) {
this.papel = papel;
}
public void setUsuario(Usuario usuario) {
this.usuario = usuario;
}
}
Permissao.java
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import br.com.linkcom.neo.authorization.impl.AbstractPermission;
/**
* Representa a permissão para determinado papel em determinada tela
*/
@Entity
public class Permissao extends AbstractPermission {
Integer id;
Papel role;
String permissionString;
String path;
@Id
@GeneratedValue
public Integer getId() {
return id;
}
public String getPath() {
return path;
}
//API
public String getPermissionString() {
return permissionString;
}
//API
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="role_id")
public Papel getRole() {
return role;
}
public void setId(Integer id) {
this.id = id;
}
public void setPath(String path) {
this.path = path;
}
public void setPermissionString(String permissionString) {
this.permissionString = permissionString;
}
public void setRole(Papel role) {
this.role = role;
}
}
Configuração do AuthorizationDAO.
O NEO dispõe de uma interface, a AuthorizationDAO, que possui os métodos que serão usados tanto
para a autorização quanto autenticação do usuário.
No pacote dos DAO's adicione a seguinte classe:
NeoAuthorizationDAO.java
import java.util.List;
import java.util.Map;
import org.springframework.orm.hibernate3.HibernateTemplate;
import entity.Papel;
import entity.Permissao;
import entity.Usuario;
import entity.UsuarioPapel;
import br.com.linkcom.neo.authorization.AuthorizationDAO;
import br.com.linkcom.neo.authorization.Permission;
import br.com.linkcom.neo.authorization.Role;
import br.com.linkcom.neo.authorization.User;
import br.com.linkcom.neo.persistence.QueryBuilder;
/**
* DAO de autorização, detectado automaticamente pelo NEO.
* Fornece informações sobre autorização para o framework
*/
public class NeoAuthorizationDAO implements AuthorizationDAO {
private HibernateTemplate hibernateTemplate;
public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
//API
/**
* Retorna a lista de todos os papeis do sistema
*/
public Role[] findAllRoles() {
List roles = new QueryBuilder(hibernateTemplate)
.from(Papel.class)
.list();
return roles.toArray(new Role[roles.size()]);
}
//API
/**
* Acha a permissão para determinado nível em determinada tela
* @param role papel que desejamos a permissão
* @param controlName path que representa a tela desejada
*/
public Permission findPermission(Role role, String controlName) {
return new QueryBuilder(hibernateTemplate)
.from(Permissao.class)
.where("permissao.role = ?", role)
.where("permissao.path = ?", controlName)
.unique();
}
//API
/**
* Acha o usuário pelo login
*/
public User findUserByLogin(String login) {
return new QueryBuilder(hibernateTemplate)
.from(Usuario.class)
.where("usuario.login = ?", login)
.unique();
}
//API
/**
* Acha os papeis (níveis) de determinado usuário
*/
public Role[] findUserRoles(User user) {
List list = new QueryBuilder(hibernateTemplate)
.select("papel")
.from(UsuarioPapel.class)
.leftOuterJoin("usuarioPapel.papel papel")
.leftOuterJoin("usuarioPapel.usuario usuario")
.where("usuario = ?", user)
.list();
return list.toArray(new Role[list.size()]);
}
//API
/**
* Salva ou atualiza uma permissão no banco de dados.
*/
public Permission savePermission(String controlName, Role role, Map permissionMap) {
Permissao permissao = (Permissao)findPermission(role, controlName);
if(permissao == null){
permissao = new Permissao();
permissao.setPath(controlName);
permissao.setRole((Papel) role);
}
permissao.setPermissionMap(permissionMap);
hibernateTemplate.saveOrUpdate(permissao);
return permissao;
}
}
Configuração do controller da página principal.
Também é necessário configurar a página principal da nossa aplicação. Para isso crie o seguinte controller:
import org.springframework.web.servlet.ModelAndView;
import br.com.linkcom.neo.controller.Controller;
import br.com.linkcom.neo.controller.DefaultAction;
import br.com.linkcom.neo.controller.MultiActionController;
import br.com.linkcom.neo.core.web.WebRequestContext;
@Controller(path="/modulo/index")
public class IndexController extends MultiActionController {
@DefaultAction
public ModelAndView doPage(WebRequestContext request){
return new ModelAndView("index");
}
}
Em seguinda um arquivo chamado index.jsp na pasta /WEB-ROOT/WEB-INF/jsp/modulo/
<b>Página principal da minha aplicação.</b>
Documentação extra.
Caso precise de recursos extras para autenticação, abra as seguintes classes:
- br.com.linkcom.neo.authorization.AuthorizationChecker
- br.com.linkcom.neo.authorization.AuthenticationControlFilter
- br.com.linkcom.neo.authorization.AuthenticationConfig