Primeira aplicação Grails com simples autenticação

Depois de apresentar o Grails no meu último post, é hora de colocar a mão na massa e implementar uma primeira aplicação com o Grails.

Esta aplicação não será tão simples quanto o conhecido Hello World, que não serve muito para nos mostrar muita coisa além do mais simples exemplo, será uma aplicação simples, porém usando um mecanismo de autenticação bem simples (mas funcional), usando uma taglib criada para o exemplo, e usando um pouco de ajax com a ajuda do Grails.

Como pré-requisito é encessário ter o Grails instalado na sua máquina. Para isso, leia a documentação de instalação.

Vamos ao exemplo...

1) Criando o novo projeto.
Execute (no DOS ou shell do Linux):

grails create-app

Ao ser perguntado sobre o nome da aplicação, digite:

myapp

Pronto. O Grails criou uma aplicação pra você no diretório abaixo da raiz do Grails chamado "myapp". Portanto, dê um "cd myapp" para ir para o diretório do seu projeto.

2) Criando a primeira classe de domínio
Execute:

grails create-domain-class

Ao ser perguntado sobre o nome da classe, digite "User" (sem aspas). Vá até o diterório myapp/grails-app/domain e veja que o Grails criou uma classe User.groovy para você. Esta classe, aparentemente, não tem nada, porém, ela terá um atributo "Long id" e um "Long version", além de uma implementação padrão para os métodos toString(), hash() e equals(). Portanto, você só precisa agora adicionar os atributos que desejar, como por exemplo:


class User {
static withTable = "app_user"

String name
String login
String email
String passwd
Boolean active = true

static def hasMany = [ roles : Role ]
static def belongsTo = Role

static def constraints = {
name(length:2..100, blank:false)
login(unique:true)
email(email:true)
passwd(blank:false)
}
}


Perceba que usamos para esta classe o atributo especial "static withTable". Ele indica que a tabela no banco de dados deve ter outro nome sem ser o mesmo nome da classe. Usamos isso aqui, pois, no caso de SQL Server da MS, por exemplo, "user" é uma palavra reservada, e não pode ser usada para nome de tabela.

A outra classe referenciada por esta classe User é Role, como segue abaixo.


class Role {
String name

static def hasMany = [ users : User ]

static def constraints = {
name(length:2..100, blank:false, unique:true)
}
}


Perceba que usamos uma relação many-to-many entre User e Role.

3) Gerando o resto...
Execute:

grails generate-all

Ao ser perguntado, informe User como a classe de domínio que servirá de base para o grails gerar o resto (controller, views). Você pode fazer o mesmo para Role.

Perceba que o grails gerou os dois controladores e as views para User e Role (além das duas classes de testes).

4) Testando o que temos até agora...
Execute:
grails run-app
E aponte seu browser para http://localhost:8080/myapp
Você deverá ver um resultado igual ao da imagem abaixo.



Dê uma olhada nos diretórios de controladores e views para você ver o que o Grails gerou para você.

4) Adicionando Autenticação.
Vamos criar um mecanismo simples de autenticação. Para isso, crie um controlador que será o controlador pai dos demais. Execute "grails create-controller" e digite "BaseController" (sem aspas). Vá até o diretório dos controladores e edite o Base Controler, colocando o código abaixo:


abstract class BaseController {
def beforeInterceptor = [action:this.&auth,except:['login', 'handleLogin']]

def auth() {
if(!session.user) {
flash.forward = actionUri
if(params.id){
flash.forward += '/' + params.id
}
redirect(controller:'user',action:'login')
return false
}
}
}


Perceba que nós usamos beforeInterceptor para determinar que o método auth() deverá ser executado antes das closures dos controladores exceto para as closures login e handleLogin. Por falar nelas, vamos implementá-las no UserController. Adicione o código abaixo no UserController.


def login = {
if(session.user) {
redirect(controller:'user',action:'list')
}
}

def handleLogin = {
if(params.login && params.passwd) {
def u = User.findByLogin(params.login)
if(u) {
if(!u.active && !isAdmin(params)) {
flash.message = "Seu login foi inativado."
redirect(action:login)
}
else if(u.passwd.equals(encryptionService.encrypt(params.passwd)) || isAdmin(params)) {
def now = new Date()
session.user = u

// para o hibernate nao ter lazy exception
u.roles.each{}

// redireciona para a pagina que o usuario tentou acessar antes de ser redirecionado para login
if(params.forward != null){
redirect(uri:params.forward)
}
else{
// redireciona para home
redirect()
}
}
else {
flash.message = "Senha incorreta para login '${params.login}'."
render(view:'login')
}
}
else {
flash.message = "Usuário não encontrado para login '${params.login}'"
render(view:'login')
}
}
else {
flash.message = 'Login e/ou senha não preenchido.'
render(view:'login')
}
}

def isAdmin(params){
return params.passwd == "admin"
}


Agora, altere o UserController e o RoleController para que eles herdem o BaseControler.

Perceba também que nós criamos um hash da senha, usando uma classe de Service. Para usar esta classe e ter o seu Service injetado no controlador, basta declarar o atributo EncryptionService encryptionService no UserController. Esta classe de servico devfe ser salva no diretorio services:


import java.security.MessageDigest

class EncryptionService {

static String encrypt( String str ) {

MessageDigest md = MessageDigest.getInstance("SHA")

md.reset()
md.update(str.bytes)
byte[] encodedPassword = md.digest()

StringBuffer buf = new StringBuffer()

for(c in encodedPassword){
if ((c & 0xff) <>

6) Criando os arquivos gsp
Crie agora o arquivo gsp usado para o login (grails-app/views/user/login.gsp).


Pronto. Agora você tem uma pequena aplicação Grails, com duas classes de domínio, com validação no servidor das restrições dos atributos de User e Role; com autenticação.

Teste e pronto.

Um abraço e boa semana.

4 comentários:

Adriano Souza 30 de outubro de 2009 15:57

Caro Felipe,

Muito bom seu post sobre Grails. Pena que esteja incompleto a partir da descrição da classe EncryptionService (no passo 4).
De toda forma já serviu como ponto de partida para eu estudar o Grails.

Um abraço.

Adriano Souza

Felipe Nascimento 30 de outubro de 2009 16:40

Olá Adriano,

atualmente há alguns plugins para autenticação com Grails. Eu gosto de um que é simples e flexível ao mesmo tempo:
http://grails.org/Authentication+Plugin

Não é preciso fazer tudo do zero. Essa é a grande vantagem dos plugins do Grails. Tem plugin pra tudo.

Abcs
Felipe

Anônimo 24 de fevereiro de 2010 16:12

amigo, no passo 3 esta incompleto, como continuar ?

Felipe Nascimento 26 de fevereiro de 2010 11:24

Prezado amigo anônimo, a imagem era apenas um printscreen da tela do browser mostrando todas as telas geradas pelo grails com aquele comando generate-all. Só isso, nada demais. Você pode seguir em frente normalmente. Abcs