out 24 2010

Spring MVC 3 na Prática com Bean Validation

Category: Spring,Spring MVC 3.0Edson Gonçalves @ 20:56

Olá Pessoal, tudo bom? Como vão vocês?

Este é o terceiro artigo da série Spring MVC 3.0. Se vocês não tiveram um contato inicial com o framework, recomendo ver este artigo primeiro.

Para acompanhar esta terceira parte, recomendo ter criado o projeto do segundo artigo.

Como sempre, dúvidas e críticas são bem vindas.

Alterando o CRUD criado com o Spring MVC

O projeto neste artigo é o mesmo do segundo artigo da série. Entretanto, faremos algumas alterações para que passe a utilizar validações do Bean Validation.

Adicionando as bibliotecas ao projeto utilizando o Maven

Mais uma vez, recorreremos ao Maven para adicionar as bibliotecas que necessitamos. Neste caso, teremos que baixar o Hibernate Validator. Para utilizar o Bean Validation, utilizaremos dois JARs: hibernate-validator-4.0.2.GA.jar e validation-api-1.0.0.GA.jar.

Abram o arquivo pom.xml , encontrado na view Package Explorer para que possamos adicionar as configurações que necessitamos.

Criando a propriedade

Na aba Overview, em Properties, cliquem no botão Create. Na caixa de diálogo Add property, preencham como na Figura 1.

Figura 1 – Criação da property da versão do hibernate validator

Criando a dependência

Com a propriedade definida para informar qual versão desejamos utilizar, no qual o Maven deverá baixar, resta configurar as dependências.

Na aba Dependencies, cliquem no botão Create e preencham conforme a Figura 2 ilustra.

Figura 2 – A dependência do Hibernate Validator

Alterando a entidade Contato

A entidade do artigo, chamada de Contato, será a primeira coisa que iremos modificar no projeto.

Graças a JSR 303, chamada de Bean Validation, podemos anotar as entidades com validações. Com as anotações de Bean Validation na entidade, concentramos a validação em um único local, de forma padronizada, tornando possível portar estas validações para as classes controladoras do Spring MVC.

A Listagem 1 exibe  a entidade Contato modificada. Note as anotações de validação onde colocamos as mensagens de erro embutidas. Mais adiante iremos capturá-las para exibir o problema ao usuário.

Listagem 1. A entidade Contato com anotações Bean Validation.

package br.com.integrator;

import javax.persistence.*;
import javax.validation.constraints.*;

@Entity
@Table(name = "contato")
public class Contato {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id")
	private Long id;
	@NotNull
	@Size(min=5, message="O nome não pode ter menos que 5 caracteres!")
	private String nome;
	@NotNull
	@Pattern(regexp = "^[\\w\\-]+(\\.[\\w\\-]+)*@([A-Za-z0-9-]+\\.)+[A-Za-z]{2,4}$", message="E-mail com formato incorreto.")
	private String email;
	@Pattern(regexp = "\\(?\\b([0-9]{2})\\)?[-. ]?([0-9]{4})[-. ]?([0-9]{4})\\b", message="Telefone em formato incorreto")
	private String telefone; 

     //getters e setters
 }

A infra-estrutura definida pela JSR 303 nos permite descrever as restrições, usando anotações no modelo de classes de persistência, como definir se um campo aceitará uma quantidade mínima de caracteres ou se não aceitará nulo, por exemplo.

Cada anotação é associada a uma validação, verificando se a instância da entidade anotada obedece à regra ou não.

A Tabela 1 apresenta todas as anotações possíveis de serem usadas e suas funcionalidades.

Annotation O que faz?
@AssertFalse Checa se a propriedade anotada é falsa.
@AssertTrue Checa se a propriedade anotada é verdadeira.
@DecimalMax(value=) A propriedade anotada precisa ser um número, cujo valor deve estar menor ou igual ao valor máximo previsto. O parâmetro value é a representação em string do valor máximo aceito de acordo com o formato representado em BigDecimal. Suporta tipos como BigDecimal, BigInteger, String, byte, short, int, long e os respectivos wrappers de tipos primitivos.
@DecimalMin(value=) A propriedade anotada precisa ser um número, cujo valor deve estar maior ou igual ao valor mínimo previsto. O parâmetro value é a representação em string do valor mínimo de acordo com a representação de sequência de BigDecimal. Suporta tipos como BigDecimal, BigInteger, String, byte, short, int, long e os respectivos wrappers de tipos primitivos.
@Digits(integer=, fraction=) Verifica se a propriedade possui a quantidade de dígitos antes e depois do separador de casa decimal. Por exemplo: @Digits(integer=9, fraction=2) significa que espera-se 9 dígitos inteiros e 2 dígitos fracionários. Suporta os tipos: BigDecimal, BigInteger, String, byte, short, int, long e os respectivos wrappers de tipos primitivos.
@Future Checa se a data está no futuro. Suporta os tipos java.util.Date e java.util.Calendar.
@Max(value=) Verifica se o valor é menor ou igual ao valor anotado. Suporta os tipos: BigDecimal, BigInteger, String, byte, short, int, long e os respectivos wrappers de tipos primitivos.
@Min(value=) Verifica se o valor é maior ou igual ao valor anotado. Suporta os tipos: BigDecimal, BigInteger, String, byte, short, int, long e os respectivos wrappers de tipos primitivos.
@NotNull Checa se o valor anotado não é nulo (null). Uma String cujo valor seja vazio (“”) vai passar.
@Null Checa se o valor anotado é nulo (null).
@Past Checa se uma data está no passado. Suporta os tipos java.util.Date e java.util.Calendar.
@Pattern(regex=, flag=) Checa se a propriedade obedece à expressão regular.
@Size(min=, max=) Confere se a quantidade de elementos está entre o mínimo e o máximo, suportando tipos como: Strings, Collections, Maps e arrays.
@Valid Impõe uma validação recursiva aos objetos associados. Digamos que, no bean Post, houvesse a anotação @Valid no atributo comments. Já na classe Comment, temos um atributo de validação anotado. Se um valor transmitido a comments, de Post, não estiver de acordo com a validação existente na classe Comment, de acordo com o atributo anotado, um erro será gerado em tempo de execução. A notação @Valid está sendo usada no PostController para validar os erros existentes em Post ou Comment.

Tabela 1. Anotações e regras para criar restrições (Constraints).

Alterando o controlador

Alteraremos a classe ContatoController, criada no pacote br.com.integrator.web, de acordo com o mostrado na Listagem 2.

Listagem 2. A classe ContatoController alterada.

package br.com.integrator.web;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

import br.com.integrator.dao.ContatoDAO;
import br.com.integrator.Contato;

@Controller
@RequestMapping("/contato/**")
public class ContatoController {
@Autowired
private ContatoDAO contatoDao;

@RequestMapping(value = "/contato/{id}", method = RequestMethod.GET)
public String show(@PathVariable("id") Long id, ModelMap modelMap) {
modelMap.addAttribute("contato", contatoDao.find(id));
return "contato/show";
}

@RequestMapping(value = "/contato", method = RequestMethod.GET)
public String list(ModelMap modelMap) {
modelMap.addAttribute("contatos", contatoDao.findAll());
return "contato/list";
}

@RequestMapping(value = "/contato/{id}", method = RequestMethod.DELETE)
public String delete(@PathVariable("id") Long id) {
contatoDao.remove(contatoDao.find(id));
return "redirect:/contato";
}

@RequestMapping(value = "/contato/form", method = RequestMethod.GET)
public String form(ModelMap modelMap) {
modelMap.addAttribute("contato", new Contato());
return "contato/create";
}

@RequestMapping(value = "/contato", method = RequestMethod.POST)
public String create(@Valid Contato contato, BindingResult result) {
if (result.hasErrors())
return "contato/create";

contatoDao.persist(contato);
return "redirect:/contato";
}

@RequestMapping(value = "/contato/{id}/form", method = RequestMethod.GET)
public String updateForm(@PathVariable("id") Long id, ModelMap modelMap) {
modelMap.addAttribute("contato", contatoDao.find(id));
return "contato/update";
}

@RequestMapping(method = RequestMethod.PUT)
public String update(@Valid Contato contato, BindingResult result) {
if (result.hasErrors())
return "contato/update";

contatoDao.merge(contato);
return "redirect:/contato";
}

}

Como visto na Listagem 1, as anotações @Size e @Pattern possuem o atributo message, que capturamos pelo Spring MVC na classe ContatoController (Listagem 2)  – através da classe javax.validation.Valid (anotação @Valid). Assim como a anotação @Controller, as anotações pertencentes a Bean Validation são obtidas graças a adição do elemento <mvc:annotation-driven />. Isto permite que as informações anotadas sejam injetadas onde a validação é necessária na sua aplicação.Caso haja um erro, o método hasErrors(), de org.springframework.validation.BindingResult, retorna true, nos possibilitando conduzir a navegação da página, com seus respectivos erros, inclusive paralisando a ação ao qual se encontra. A utilização de hasErrors() ocorre em ContatoController, nos métodos create() e update() com o seguinte if:

if (result.hasErrors())

//executa uma ação para conduzir a página com os erros ao usuário

As informações recebidas da validação pela classe Controller são transmitidas para o formulário e capturadas. Veja como isto ocorre na descrição da Listagem 3.

Alterando as views

Temos três páginas que representam nosso CRUD feitas. Estas páginas foram criadas dentro do diretório chamado WEB-INF/jsp/contato(fisicamente na ferramenta: src/main/webapp/WEB-INF/jsp/contato). Teremos que alterá-las para permitir que os erros sejam exibidos.

O formulário de cadastro

Abra o arquivo create.jsp e altere como na Listagem 3, adicionando as tags  <form:errors />.

Listagem 3. A página create.jsp.

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
 <style type="text/css" media="screen">
 @import url("<c:url value="/static/styles/style.css"/>");
 </style>
<title>Cadastrar</title>

</head>
<body>
 <div id="wrap">

 <div id="menu">
 <%@ include file="/menu.jsp" %>
 </div>
 <div id="main">
 <div id="body">
 <c:url var="url" value="/contato" />
 <form:form action="${url}" method="POST" modelAttribute="contato">
 <div>
 <label for="nome">Nome:</label>
 <form:errors path="nome" cssClass="errors"/><br />
 <form:input cssStyle="width:250px" maxlength="30" path="nome" size="30"/>

 </div>
 <br/>
 <div>
 <label for="email">Email:</label>
 <form:errors path="email" cssClass="errors"/><br />
 <form:input cssStyle="width:250px" maxlength="30" path="email" size="30"/>

 </div>
 <br/>
 <div>
 <label for="telefone">Telefone:</label>
 <form:errors path="telefone" cssClass="errors"/><br />
 <form:input cssStyle="width:250px" maxlength="30" path="telefone" size="20"/>
</div>
 <br/><div>
 <input id="criar" type="submit" value="Criar Contato"/>
 </div>
 </form:form>
 </div>
 </div>
 </div>
</body>
</html>
A tag  <form:errors /> foi utilizada na Listagem 3 com dois parâmetros: path e cssClass. No parâmetro path transmitimos o  atributo da classe Contato, validada pelas anotações de Bean Validation, onde receberá a mensagem.

Figura 3 – Processo de validação partindo da anotação no JavaBean Contato

Podemos ter uma idéia de como ficará a página create.jsp depois das alterações visualizando a Figura 4.

Figura 4 – Validação transmitida para a página de JSP

Figura 4 – Validação transmitida para a página de JSP

Alterando a página de atualização de dados

Como feito na página create.jsp, a página update.jsp também receberá as tags <form:errors />. A Listagem 4 exibe a página com as mudanças, na íntegra.

Listagem 4. A página update.jsp.

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
 <style type="text/css" media="screen">
 @import url("<c:url value="/static/styles/style.css"/>");
 </style>
<title>Atualizar</title>
</head>
<body>
 <div id="wrap">
 <div id="menu">
 <%@ include file="/menu.jsp" %>
 </div>
 <div id="main">
 <div id="body">
 <c:url var="url" value="/contato/${contato.id}" />
 <form:form action="${url}" method="PUT" modelAttribute="contato">
 <div>
 <label for="nome">Nome:</label>
 <form:errors path="nome" cssClass="errors"/><br />
 <form:input cssStyle="width:250px" maxlength="30" path="nome" size="30"/>
 </div>
 <br/>
 <div>
 <label for="email">Email:</label>
 <form:errors path="email" cssClass="errors"/><br />
 <form:input cssStyle="width:250px" maxlength="30" path="email" size="30"/>
 </div>
 <br/>
 <div>
 <label for="telefone">Telefone:</label>
 <form:errors path="telefone" cssClass="errors"/><br />
 <form:input cssStyle="width:250px" maxlength="30" path="telefone" size="20"/>
 </div>
 <br/>
 <div>
 <input id="atualizar" type="submit" value="Atualizar Contato"/>
 </div>
 <form:hidden path="id"/>
 </form:form>
 </div>
 </div>
 </div>
</body>
</html>

O projeto para download

Clique aqui para baixar o projeto e alterá-lo como desejar.

Considerações finais

Com as validações, boa parte dos problemas iniciais de um desenvolvimento usando o Spring MVC foram resolvidos. Mas vejam só: é o começo. Caso haja interesse do leitor em aprender um exemplo mais complexo, a revista JavaMagazine #78 publicou, um artigo meu com o Spring MVC 3 na criação de um blog, do começo ao fim. É um bom início para se desenvolver um projeto mais completo e complexo.
Para os que acompanham o blog, o assunto Spring MVC não para por aqui. Veremos em breve um site completo, feito com o framework, unindo várias características do Spring, de seu framework MVC e a parte de segurança com Spring Security.

Tags: , , , , , , , ,


maio 04 2010

Segurança com Spring Security 3.0 utilizando banco de dados em apenas alguns minutos

Category: SpringEdson Gonçalves @ 5:13

Olá, tudo bom? Como vão vocês?
Este artigo é o segundo, de uma série, que falaremos sobre segurança em aplicações Java, continuando com o Spring Security.  Dúvidas e críticas são bem vindas.

Spring Security 3.0 com banco de dados

Aplicações comerciais sempre necessitam de acesso ao banco de dados então, porque seria diferente criar uma segurança sem estar com as informações contidas no banco de dados.

No artigo anterior,   tivemos uma introdução de como usar o Spring Security utilizando as configurações somente no XML. Neste artigo veremos como criar a segurança utilizando o Spring Security e o banco de dados.

O banco de dados

O banco de dados utilizado será o MySQL. Você pode baixar a versão 5.1, utilizada no artigo, aqui.

Preparando o banco de dados do exemplo

Abra o terminal do MySQL com seu usuário e senha ROOT (aquela que você configurou na instalação).

Crie o banco de dados e a tabela executando os seguintes comandos:

create database security;

use security;

CREATE TABLE users

(

username VARCHAR(15) NOT NULL,

password VARCHAR(40),

authority VARCHAR(15),

PRIMARY KEY (username)

);

Insira um usuário assim:

INSERT INTO users  VALUES(‘edson’,’integrator’,’ROLE_ADMIN’);

Figura 1 – A tabela criada no banco de dados security

Figura 1 – A tabela criada no banco de dados security

Baixando e descompactando o driver JDBC

Podemos baixar o MySQL clicando aqui, além da versão atual do driver JDBC, chamado de Connector/J 5.1. Baixe os arquivos, instale o MySQL, caso não o tenha em sua máquina e descompacte o arquivo JAR, do qual iremos precisar, que será:

mysql-connector-java-5.1.10-bin.jar

O projeto

Pegue o projeto criado no artigo anterior, adicione a biblioteca JDBC do MySQL e também a biblioteca JAR do Spring Framework:

org.springframework.jdbc-3.0.0.RELEASE.jar

Figura 2 – Bibliotecas adicionadas ao projeto nas properties

Figura 2 – Bibliotecas adicionadas ao projeto nas properties

O arquivo de configurações do Spring

No arquivo de configurações do Spring, altere como mostrado na Listagem 1 a seguir:

Listagem 1 – O arquivo applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:beans="http://www.springframework.org/schema/beans"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/security

http://www.springframework.org/schema/security/spring-security-3.0.xsd">

<http auto-config="true" >

<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?error=invalido"/>

<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />

</http>

<authentication-manager>

<authentication-provider>

<jdbc-user-service data-source-ref="dataSource"

users-by-username-query="SELECT username, password, 'true' as enable FROM users WHERE username=?"

authorities-by-username-query="SELECT username, authority FROM users WHERE username=?"

/>

</authentication-provider>

</authentication-manager>

<beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >

<beans:property name="url" value="jdbc:mysql://localhost:3306/security" />

<beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />

<beans:property name="username" value="root" />

<beans:property name="password" value="integrator" />

</beans:bean>

</beans:beans>

A mudança feita no arquivo applicationContext.xml, visto na Listagem 1, com relação ao artigo anterior, está na forma como vamos acessar o usuário, senha e seu role. Alteramos o antigo elemento <user-service /> por <jdbc-user-service />.

Em <jdbc-user-service />, temos TRÊS atributos importantes: users-by-username-query e authorities-by-username-query e data-source-ref. O primeiro atributo possui uma query que deve buscar três informações: username, password e um valor booleano chamado enable. A tabela criada no banco de dados, vista na Figura 1, possui três campos (username, password e authority), mas não possui enable. Portanto, a query deve simular este campo, informando que todos os cadastrados na tabela estão habilitados:

SELECT username, password, ‘true’ as enable FROM users WHERE username=?

Observe também que há uma condição WHERE na cláusula SQL, para que o usuário seja filtrado no login e para que o Spring Security se encarregue do resto, analisando se o usuário transmitido e sua senha são similares ao existente no banco de dados.

Além de verificar se o usuário existe, o Spring Security precisa saber se ele possui autorização para acessar determinado local. O role, no caso, foi adicionado no terceiro campo da tabela users, chamado de authority. O atributo authorities-by-username-query, portanto, recebe uma query onde existe o usuário e seu role, sempre filtrando pelo usuário:

SELECT username, authority FROM users WHERE username=?

Atenção: A query utilizada para o spring security precisa retornar os seguintes nomes de colunas: username, password, enable e authority.

No atributo data-source-ref indicamos o data source necessário para se conectar ao banco de dados.

A conexão é feita pelo elemento <beans/> no qual a classe utilizada é a org.springframework.jdbc.datasource.DriverManagerDataSource, responsável por fazer a conexão com o banco de dados.

Logout, acesso negado e criptografia da senha

Depois de concluído o acesso à área restrita, temos que efetuar o logout quando não interessa mais estar naquela área.

Também precisamos definir o que será apresentado aos usuários cujo não possuem acesso a uma determinada área.

Logout

Para fazer o logout de uma área restrita, utilizamos o padrão para fazer isto é: /j_spring_security_logout. Sua utilização será no arquivo /admin/index.jsp, como por exemplo a adição de um link, como na Listagem 2.

Listagem 2 – Adição do link para efetuar logout da área restrita

<html>

...

<body>

<h2>Parabéns, você está logado!</h2>

<hr />

Faça logout <a href="../j_spring_security_logout">clicando aqui</a>.

</body>

</html>

Página com a mensagem de acesso negado

Caso tenhamos um usuário cadastrado válido, que não possui permissão de acesso a uma determinada área, vemos uma página padrão, como de costume, exibida pelo servidor.

Figura 3 – Acesso negado a um usuário que não tem permissão a esta área

Figura 3 – Acesso negado a um usuário que não tem permissão a esta área

Para modificar esta página, podemos criar uma página que será exibida em seu lugar, informando o usuário da falta de permissão de acesso.

Sua configuração depois será no elemento <http />, do arquivo applicationContext.xml, com o atributo access-denied-page. Abaixo vemos a página negado.jsp como sendo a responsável por exibir uma mensagem personalizada caso o usuário entre em uma área não permitida.

<http auto-config="true" access-denied-page="/negado.jsp">

...

</http>

Criptografia de senha

O padrão para as senhas é de texto plano, como já sabem. Entretanto, o Spring Security permite ler informações criptografadas. Digamos que, na tabela de usuários, tenhamos as senhas criptografadas em MD5. Teríamos que informar isso nas configurações do Spring Security da seguinte forma:

<authentication-manager>
<authentication-provider>
<password-encoder hash="md5" />
…
</authentication-provider>
</authentication-manager>

Nota: No MySQL podemos adicionar uma string em MD5 simplesmente utilizando a função de mesmo nome:

INSERT INTO users VALUES(‘usuario’, MD5(‘senha’),’ROLE_…’)

Tags: , , , ,


abr 25 2010

Segurança passo a passo com Spring Security 3.0

Category: SpringEdson Gonçalves @ 6:48

Olá, tudo bom? Como vão vocês?

Este artigo é o primeiro de uma série que falaremos sobre segurança em aplicações Java, começando com a primeira parte do artigo sobre Spring Security.  Dúvidas e críticas são bem vindas.

Segurança de dados através do Spring Security

A segurança de áreas restritas em aplicações Web escritas em Java não é uma tarefa das mais triviais.

Sabendo que não era simples criar áreas de segurança, em 2003 surge o Acegi Security System for Spring, um framework extremamente configurável  e complexo. Comum na época, suas configurações eram baseadas em XML e demorava um tempo para que o desenvolvedor o dominasse completamente.

O projeto Acegi evoluiu e, em 2007, foi incorporado aos projetos do Spring Framework, sendo renomeado para Spring Security.

A versão 2.0 foi lançada em 2008 e em 2010 a versão 3.0, a que iremos utilizar neste artigo.

Download do Framework

Para trabalhar com o Spring Security, você deve realizar o download no endereço http://www.springsource.org/download. No momento em que este artigo é escrito, a versão utilizada é a Spring Security 3.0.2. Clique em Download.

Figura 1 – Local de download do Spring Security 3.0.2

Figura 1 – Local de download do Spring Security 3.0.2

Baixe a versão spring-security-3.0.2.RELEASE.zip. Ao baixar, descompacte o arquivo extraindo todos os JARs existentes no diretório lib.

Figura 2 – Download do Spring Security

Figura 2 – Download do Spring Security

Também será preciso baixar o Spring Framework. No momento em que este artigo é escrito, a versão utilizada é a Spring Framework 3.0.2.

Figura 3 – Download do Spring Framework

Figura 3 – Download do Spring Framework

Como o Spring Security trabalha

Da mesma forma que faríamos se estivéssemos utilizando JAAS, o Spring Security trabalha a segurança através de declarações baseadas em papéis (roles). Seja em XML ou Anotações, o Spring Security  não necessita chamar método algum para realizar uma autenticação ou autorização.

Através dos roles definidos, podemos informar ao aplicativo em questão, ao qual está sendo assegurada  uma área, quais recursos podem ser acessados ou restringidos a uma determinada pessoa que acessou a área restrita.

Preparando o ambiente de trabalho

Para este artigo, iremos utilizar a IDE da Spring Source, divisão da VMware,  criada sobre a plataforma Eclipse, chamada de SpringSource Tools Suite.

Para baixar o SpringSource Tools Suite, clique aqui, preencha o formulário e faça o Download. Como a ferramenta possui uma opção de instalador, use-a como facilitador se desejar. Na própria página onde baixar o arquivo, haverá a explicação da instalação em cada plataforma, em Installation Instructions.

Criando um projeto

No SpringSource Tools Suite, clique no menu File>New>Dynamic Web Project. Na caixa de diálogo New Dynamic Web Project, digite ProjSpringSecurity (ou o nome que desejar) em Project name.

O SpringSource Tools Suite possui embutido um servidor de aplicações Java Web baseado no Apache Tomcat 6, só que com algumas modificações. Entretanto, vamos utilizar o Tomcat, que pode ser adicionado como mostro neste artigo.

Confirme a criação do projeto no botão Finish.

Adicionando as bibliotecas ao Projeto

Com o direito do mouse sobre o projeto, na view Project Explorer, vá até Properties. Na  caixa de diálogo das propriedades do projeto, vá até Java EE Module Dependencies. Clique em Add External JARs e adicione os seguintes arquivos:

  1. org.springframework.aop-3.0.2.RELEASE.jar
  2. org.springframework.asm-3.0.2.RELEASE.jar
  3. org.springframework.beans-3.0.2.RELEASE.jar
  4. org.springframework.context-3.0.2.RELEASE.jar
  5. org.springframework.core-3.0.2.RELEASE.jar
  6. org.springframework.expression-3.0.2.RELEASE.jar
  7. org.springframework.transaction-3.0.2.RELEASE.jar
  8. org.springframework.web-3.0.2.RELEASE.jar
  9. spring-security-config-3.0.2.RELEASE.jar
  10. spring-security-core-3.0.2.RELEASE.jar
  11. spring-security-taglibs-3.0.2.RELEASE.jar
  12. spring-security-web-3.0.2.RELEASE.jar
  13. commons-logging-1.1.1.jar

Note que o 13º item é um JAR que não pertence a família do Spring Framework. Você pode baixar o arquivo compactado, contendo a biblioteca commons-logging-1.1.1.jar, clicando aqui.

Figura 4 – Arquivos JARs adicionados ao projeto

Figura 4 – Arquivos JARs adicionados ao projeto

Uma aplicação simples com Spring Security

Para exemplificar como funciona o Spring Security, vamos criar uma aplicação simples com apenas uma área segura. Esta área segura será representada dentro de um diretório, chamado admin.

Teremos duas páginas index.jsp: uma na raiz do aplicativo e outra dentro do diretório admin, como mostra a Figura 5.

Figura 5 – As páginas do Projeto

Figura 5 – As páginas do Projeto

As páginas JSP

A página index.jsp, existente dentro do diretório admin exibe apenas uma mensagem simples, como mostra a Figura 6, com apenas HTML.

O conteúdo é mostrado na Listagem 1.

Figura 6 – Página index.jsp exibida após logar na área admin

Figura 6 – Página index.jsp exibida após logar na área admin

Listagem 1 – O conteúdo HTML da página /admin/index.jsp

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01

Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<title>Usuário Logado</title>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

</head>

<body>

<h2>Parabéns, você está logado!</h2>

</body>

</html>

A página index.jsp encontrada na raiz exibe apenas um link que o leva até a área administrativa. Sua aparência é idêntica a Figura 7.

O conteúdo da página index.jsp encontrada na raiz da aplicação é mostrado na Listagem 2.

Figura 7 – Página index.jsp exibida quando acessada a aplicação

Figura 7 – Página index.jsp exibida quando acessada a aplicação

Listagem 2 – O conteúdo HTML da página /index.jsp

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<title>Página Inicial</title>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

</head>

<body>

<h2>Uma aplicação simples com Spring Security</h2>

<hr />

<a href="admin">Clique aqui para acessar a área administrativa</a>

</body>

</html>

Configurando o web.xml

Para que o Spring Security, assim como o Spring, precisamos configurar o web.xml. O Spring Security utiliza um filtro HTTP para interceptar as URLs acessadas e verificar as permissões de acesso.

A Listagem 3 exibe a configuração do arquivo web.xml.

Listagem 3 – O arquivo web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

id="WebApp_ID" version="2.5">

<display-name>ProjSpringSecurity</display-name>

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

<filter>

<filter-name>springSecurityFilterChain</filter-name>

<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

</filter>

<filter-mapping>

<filter-name>springSecurityFilterChain</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

<welcome-file-list>

<welcome-file>index.jsp</welcome-file>

</welcome-file-list>

</web-app>

Para configurar o Spring Security, utilizamos o filtro org.springframework.web.filter.DelegatingFilterProxy, devidamente configurado na Listagem 3. O filtro está sendo aplicado em todo o aplicativo, podendo ser visto no elemento <url-pattern />.

Atenção: Um detalhe importante que precisa ser notado é o nome do filtro, colocado no elemento <filter-name/>.  Não o altere, pois o Spring já espera pelo nome springSecurityFilterChain.

Configurando o applicationContext.xml

O Spring Security será configurado no arquivo applicationContext.xml. Este arquivo deverá ser criado dentro do diretório WEB-INF, com o conteúdo mostrado na Listagem 4.

Listagem 4 – O arquivo applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:beans="http://www.springframework.org/schema/beans"

xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">

<http auto-config="true">

<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />

</http>

<authentication-manager>

<authentication-provider>

<user-service>

<user name="edson" password="integrator" authorities="ROLE_ADMIN" />

</user-service>

</authentication-provider>

</authentication-manager>

</beans:beans>

No Spring Security, as configurações de autenticação e autorização estão sendo feitas no arquivo de contexto padrão do Spring (applicationContext.xml). Para que este arquivo seja lido, adicionamos no web.xml o elemento <listener />, contendo o listener org.springframework.web.context.ContextLoaderListener.

O listener do Spring faz com que as configurações sejam carregadas na inicialização da aplicação Web.

Ao ser carregado pelo listener, o arquivo da Listagem 4 declara os usuários e suas regras de acesso ao aplicativo.

O controle de acesso é feito pelo elemento <http />, do applicationContext.xml. Este controle é definido no sub-elemento <intercept-url />. O atributo pattern, de <intercept-url />, informa, através de uma expressão, em qual local o filtro deve agir, bem como define a sua regra de acesso, através do atributo access.

Para que possamos definir que qualquer elemento dentro do diretório admin fique acessível somente para os usuários do role ROLE_ADMIN, adicionamos uma expressão comum no Apache Ant.

Caso tenhamos mais de um sub-elemento <intercept-url />, teremos sua interpretação sendo feita por ordem de definição, sendo que, a primeira que atender a regra, será chamada. Na prática, isto significa que, se houver /admin/relatorios/** e /admin/**, o primeiro caso deverá ser lido primeiro, portanto será o primeiro a ser adicionado na ordem em  applicationContext.xml.

O atributo auto-config, com o valor true, indica a configuração automática da aplicação para utilizar um formulário de login. O JSP do formulário é gerado automaticamente pelo Spring Security neste caso. A Figura 8 exibe o formulário gerado pelo Spring Security.

Figura 8 – Formulário de login gerado automaticamente pelo Spring Security

Figura 8 – Formulário de login gerado automaticamente pelo Spring Security

Com o atributo <authentication-manager>, gerenciamos os usuários e seus respectivos roles  que darão permissão ao diretório especificado anteriormente, em <intercept-url />, pelo filtro.

Para facilitar a compreensão do exemplo, adicionamos apenas um usuário, através de <user/>, informando o nome de usuário, a senha e o seu papel de acesso.

Ao logar no aplicativo, o Spring analisará qual role é permitido no diretório e quem possui  tal permissão.

Personalizando o formulário de acesso a área restrita

É interessante ter uma geração automática de formulário no Spring Security, ajuda a testar a codificação, com certeza. Mas não é agradável ao aplicativo como um todo, pois sempre precisamos criar o formulário com as características gerais desenvolvidas no layout das páginas.

Criando a página personalizada de login

Para isso, o Spring Security nos fornece a personalização do formulário.  A Listagem 5 exibe o conteúdo da página /login.jsp que ficará na raiz do seu aplicativo, junto com index.jsp.

Listagem 5 – A página login.jsp

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title>Área Restrita</title>

</head>

<body>

<h2>Área Restrita</h2>

<hr />

<%

if(request.getParameter("error") != null){

if (request.getParameter("error").equals("invalido")){

%>

<p>

<span style="color:red">

Usuário ou Senha inválidos

</span>

</p>

<%

} //fim do if equals

}//fim do if null

%>

<form action="j_spring_security_check" method="post">

Usuário: <input name="j_username" type="text" value="${not empty login_error ? SPRING_SECURITY_LAST_USERNAME : ''}" />

<br />

Senha: <input type="password" name="j_password"><br />

<input type="submit" value="Efetuar Login"><br />

<a href="index.jsp">Retornar para a Página Inicial</a>

</form>

</body>

</html>

Para que o formulário funcione com o Spring Security, as regras mais básicas são:

  • O atributo action deve apontar para j_spring_security_check;
  • O atributo name da caixa de entrada de  texto, do nome de usuário, deve ser j_username;
  • O atributo name da caixa de entrada de senha deve ser j_ password.

Estes princípios básicos farão com que seu formulário funcione com o Spring Security. Entretanto, precisamos mostrar uma mensagem de erro, caso o usuário não tenha colocado as informações necessárias ou, as mesmas não sejam compatíveis com o registrado no sistema para permitir a entrada na área restrita.

É exatamente o papel do if(request.getParameter(“error”) e if (request.getParameter(“error”).equals(“invalido”)),  na página login.jsp, personalizado, com um parâmetro que iremos transmitir, através do Spring Security, caso ocorra um erro na permissão.

A página personalizada pode ser vista na Figura 9.

Figura 9 – Formulário de acesso a área restrita personalizado

Figura 9 – Formulário de acesso a área restrita personalizado

Alterando o arquivo applicationContext.xml

Por fim, mas não menos importante, precisamos alterar o arquivo applicationContext.xml para que o Spring Security passe a trabalhar com a página de login personalizada que criamos. A seguir você tem o trecho, contendo o elemento <form-login/>, que deve ser inserido por entre o elemento <http />:

<form-login login-page=“/login.jsp” authentication-failure-url=“/login.jsp?error=invalido”/>

O elemento <form-login/> é de simples compreensão, pois temos atributos que descrevem bem sua função:

login-page: A página personalizada com o formulário de substituição do padrão existente no framework

authentication-failure-url: URL de retorno caso ocorra um erro. Note que adicionamos,  após “?” , error=invalido. Isto demonstra, claramente, que não é uma regra fixa o que pode ser transmitido, caso ocorra um erro, na entrada de uma determinada área restrita.

Figura 10 – Resultado final do projeto com todos os arquivos criados

Figura 10 – Resultado final do projeto com todos os arquivos criados

No próximo artigo sobre Spring Security

Embora tenhamos conhecido os elementos básicos de utilização do Spring Security, restou colocar o acesso ao banco de dados para fazer uma autenticação verdadeira, como ocorre em sistemas.

No próximo artigo veremos como fazer para trabalhar com Spring Security e o acesso ao banco de dados, com um exemplo completo, passo a passo.

Até o próximo artigo pessoALL.

Tags: , , , , , , ,