Olá Galera! Decidi criar esse passo a passo, juntando desde o básico ao necessário do componente authlogic.
Vou estar abordando os seguintes tópicos:
- Instalação
- Criação de usuários
- Criação de sessões de usuários
- Login
- Ativação de usuário
- Recuperar senha
- Tradução para portugûes
Então vamos lá? Espero que gostem!!
Para quem quiser irei disponibilizar o projeto em http://github.com/diegonogueira/Usando-Authlogic/tree/master
Criando um projeto rails
Vamos criar um projeto chamado usando_authlogic:
rails usando_authlogic
Instalação
Para instalar como gem:
sudo gem install authlogic
Para instalar como plugin:
script/plugin install git://github.com/binarylogic/authlogic.git
Antes de configurarmos o environment.rb, exclua o arquivo index.html localizado em usando_authlogic/public
Abra seu arquivo usando_authlogic/config/environment.rb, e adicione a gem ao seu projeto:
...
Rails::Initializer.run do |config|
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Add additional load paths for your own custom dirs
# config.load_paths += %W( #{RAILS_ROOT}/extras )
# Specify gems that this application depends on and have them installed with rake gems:install
# config.gem "bj"
# config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
# config.gem "sqlite3-ruby", :lib => "sqlite3"
# config.gem "aws-s3", :lib =>"aws/s3"
config.gem "authlogic"
# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Skip frameworks you're not going to use. To use Rails without a database,
# you must remove the Active Record framework.
# config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
# Activate observers that should always be running
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names.
config.time_zone = 'Brasilia'
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')]
config.i18n.default_locale = "pt-BR"
end
...
Criando o modelo de Usuários
No console, digite:
script/generate scaffold User login:string email:string password:string password_confirmation:string
*OBS:Lembrando que criei o password e password_confirmation somente para gerar na view, dado que o mesmo não será um campo da nossa tabela users.
Gerado nosso scaffold, vamos abrir o migration gerado para user e alterar o conteúdo para:
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :login, :null=>false
t.string :email, :null=>false
t.string :crypted_password, :null=>false
t.string :password_salt, :null=>false
t.string :persistence_token, :null=>false
t.string :perishable_token, :null=>false
t.boolean :status, :default => false
t.timestamps
# # Campos adicionais
# t.integer :login_count, :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns
# t.integer :failed_login_count, :null => false, :default => 0 # optional, see Authlogic::Session::MagicColumns
# t.datetime :last_request_at # optional, see Authlogic::Session::MagicColumns
# t.datetime :current_login_at # optional, see Authlogic::Session::MagicColumns
# t.datetime :last_login_at # optional, see Authlogic::Session::MagicColumns
# t.string :current_login_ip # optional, see Authlogic::Session::MagicColumns
# t.string :last_login_ip
end
end
def self.down
drop_table :users
end
end
*OBS.: Reparem que o código comentado é opcional.
Vamos definir os principais métodos do authlogic, para isso abra usando_authlogic/controller/application_controller.rb e altere para:
class ApplicationController < ActionController::Base
helper :all
helper_method :current_user_session, :current_user #esses metodos estarão acessíveis no helper também
filter_parameter_logging :password, :password_confirmation #filtramos os campos de senha, para que o mesmo não aparece legivelmente nos logs.
#Não use esse método como privado, para que possa interagir com outros plugins(Ex.: o plugin rails_authorization)
def current_user #retorna o usuário atual
return @current_user if defined?(@current_user)
@current_user = current_user_session && current_user_session.record
end
private
def current_user_session #retorna a sessão do usuário atual
return @current_user_session if defined?(@current_user_session)
@current_user_session = UserSession.find
end
def require_user #informa o que precisa estar logado
unless current_user
store_location
redirect_to new_user_session_url
return false
end
end
def require_no_user #informa o que não precisa estar logado
if current_user
store_location
redirect_to account_url
return false
end
end
def store_location #retorna a ultima url que não pode ser acessada, pois o usuário não estava logado
session[:return_to] = request.request_uri
end
def redirect_back_or_default(default) #retorna para a ultima url que não pode ser acessada ou a definida como default
redirect_to(session[:return_to] || default)
session[:return_to] = nil
end
end
Feito isto, abre o arquivo usando_authlogic/controllers/users_controller.rb, e deixe-o assim:
class UsersController < ApplicationController
before_filter :require_no_user, :only => [:new, :create]
before_filter :require_user, :only => [:show, :edit, :update]
# GET /users/1
# GET /users/1.xml
def show
@user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @user }
end
end
# GET /users/new
# GET /users/new.xml
def new
@user = User.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @user }
end
end
# GET /users/1/edit
def edit
@user = User.find(params[:id])
end
# POST /users
# POST /users.xml
def create
@user = User.new(params[:user])
respond_to do |format|
if @user.save
send_email_active_user
flash[:notice] = 'Usuário criado com sucesso. Você receberá um email para ativação do mesmo.'
format.html { redirect_to logout_path }
format.xml { render :xml => @user, :status => :created, :location => @user }
else
format.html { render :action => "new" }
format.xml { render :xml => @user.errors, :status => :unprocessable_entity }
end
end
end
# PUT /users/1
# PUT /users/1.xml
def update
@user = User.find(params[:id])
respond_to do |format|
if @user.update_attributes(params[:user])
flash[:notice] = 'User was successfully updated.'
format.html { redirect_to(@user) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @user.errors, :status => :unprocessable_entity }
end
end
end
private
#Envia email (usuário ativar usuário)
def send_email_active_user
corpo = <<-CODE
<b>Seu cadastro precisa ser confirmado<br></b>
<b>Data do cadastro: </b>#{@user.created_at}<br>
<b>Login: </b>#{@user.login}<br>
<b>E-mail: </b>#{@user.email}<br>
<b>Para ativar </b><a href='#{edit_active_user_url(@user.perishable_token)}'>clique aqui.</a>
CODE
Email.deliver_padrao(:corpo => corpo, :assunto => "Cadastro Aceito", :para => @user.email)
end
end
Se repararem no inicio do arquivo, já estamos usando dois métodos do authlogic, o require_user e require_no_user, que tem por sua finalidade ser chamado antes de carregar a página com o método before_filter, e nele estamos dizendo que:
-Para acessar as actions new e create não é necessário estar logado.
-Para acessar as actions show, edit e update é necessário estar logado.
E será assim que iremos definir quais páginas precisam ou não precisam estar logado, bem simples né?
Como não teremos a action index, então remova a view index.html.erb, localizada em usando_authlogic/app/views/users/
Crie um arquivo chamado _form.html.erb em usando_authlogic/app/views/users/, com o seguinte conteúdo.
<h1>Usuário</h1>
<% form_for(@user) do |f| %>
<%= f.error_messages %>
<p>
Login<br />
<%= f.text_field :login %>
</p>
<p>
E-mail<br />
<%= f.text_field :email %>
</p>
<p>
Senha<br />
<%= f.password_field :password %>
</p>
<p>
Confirmar senha<br />
<%= f.password_field :password_confirmation %>
</p>
<p>
<%= f.submit 'Registrar' %>
</p>
<% end %>
<%= link_to 'Voltar', users_path %>
Abra também as views new.html.erb e edit.html.erb e deixa as duas com o seguinte conteúdo apenas, que será responsável por renderizar a partial que acabamos de criar.
<%= render :partial=>"form" %>
Altere também a view show.html.erb para:
<strong>Login:</strong>
<%=h @user.login %>
<strong>Email:</strong>
<%=h @user.email %>
<%= link_to 'Edit', edit_user_path(@user) %> |
<%= link_to 'Back', users_path %>
Vamos abrir também o model do usuário, localizado em usando_authlogic/app/models/user.rb/, e vamos dizer que este modelo será responsável pela autenticação do sistema:
class User < ActiveRecord::Base
acts_as_authentic
def deliver_password_reset_instructions!
reset_perishable_token!
end
end
Por fim, vamos criar mais uma rota para users, abra usando_authlogic/config/routes.rb, e adicione:
...
map.resource :account, :controller => "users"
...
Criando a sessão do usuário
Abra o console, e digite:
script/generate session user_session
Logo em seguida, digite também:
script/generate controller user_sessions
Feito isto, abra o controller user_sessions, e altere para:
class UserSessionsController < ApplicationController
def new
@user_session = UserSession.new
end
def create
@user_session = UserSession.new(params[:user_session])
if @user_session.save
if current_user.status #verifica se o usuário esta ativo
redirect_back_or_default login_path
else
flash[:notice] = "Seu usuário ainda não está ativo."
destroy
end
else
render :action => :new
end
end
def destroy
current_user_session.destroy
redirect_back_or_default login_path
end
end
Crie também uma view chamada new.html.erb em usando_authlogic/app/views/user_sessions/, e coloque o conteúdo a seguir:
<% form_for @user_session, :url => user_sessions_path do |f| %>
<h1>Login</h1>
<p>
Login<br>
<%= f.text_field :login %>
</p>
<p>
Senha<br>
<%= f.password_field :password %><br />
</p>
<p>
<%= f.submit "Entrar" %> |
<%= link_to "esqueci a senha", new_password_reset_path %>
</p>
<% end %>
Vamos definir as rotas para user_sessions, então abra seu arquivo usando_authlogic/config/routers.rb, e adicione as seguintes linhas:
...
map.login "login", :controller => "user_sessions", :action => "new"
map.logout "logout", :controller => "user_sessions", :action => "destroy"
map.root :controller=>"user_sessions",:action=>"new"
map.resources :user_sessions
...
Ativar de Usuário
Vá para o console, e gere o seguinte controller:
script/generate controller active_users
Agora abra o controler active_users_controller.rb, e altere para:
class ActiveUsersController < ApplicationController
def edit
@user = User.find_using_perishable_token(params[:id])
if @user
if @user.update_attributes(:status => true)
send_email_actived_user
flash[:notice] = "Usuário ativado com sucesso!"
redirect_to edit_user_path(@user)
end
else
flash[:notice] = "Token inválido"
render :action => :edit
end
end
private
#Envia email (usuário ativado)
def send_email_actived_user
corpo = <<-CODE
<b>Seu cadastro foi aceito!<br></b>
<b>Data do cadastro: </b>#{@user.created_at}<br>
<b>Login: </b>#{@user.login}<br>
<b>E-mail: </b>#{@user.email}<br>
CODE
Email.deliver_padrao(:corpo => corpo, :assunto => "Cadastro Aceito", :para => @user.email)
end
end
Crie também uma view edit.html.erb para nosso controler em : usando_authlogic/app/views/active_users/, e insira o seguinte conteúdo:
<h1>Ativar Usuário</h1>
Temos que criar as rotas também para active_user, então abra usando_authlogic/config/routes.rb, e adicione a linha:
...
map.resources :active_users
...
Esqueci a senha
Vamos gerar um controler para o esqueci a senha, então digite no console:
script/generate controller password_resets
Vamos então editar o controller password_resets_controller.rb, adicionando o seguinte conteúdo:
class PasswordResetsController < ApplicationController
before_filter :load_user_using_perishable_token, :only => [:edit, :update]
def new
end
def create
@user = User.find_by_email(params[:email])
if @user && @user.deliver_password_reset_instructions!
send_email_password_reset
flash[:notice] = "Foi enviado por e-mail a instrução de como alterar a sua senha."
redirect_to login_path
else
flash[:notice] = "Nenhum usuário com o e-mail informado!"
render :action => :new
end
end
def edit
render
end
def update
@user.password = params[:user][:password]
@user.password_confirmation = params[:user][:password_confirmation]
if @user.save
flash[:notice] = "Senha alterada com sucesso!"
redirect_to edit_user_path(@user)
else
render :action => :edit
end
end
private
def load_user_using_perishable_token
@user = User.find_using_perishable_token(params[:id])
if !@user
flash[:notice] = "Link inválido!"
redirect_to :controller => "users", :action => "new"
end
end
#Envia email (instruções para recuperar a senha)
def send_email_password_reset
corpo = <<-CODE
<b>Instruções para trocar a senha a senha<br></b>
<b>Login: </b>#{@user.login}<br>
<b>E-mail: </b>#{@user.email}<br>
<b>Para trocar a senha <b>Link: </b><a href='#{edit_password_reset_url(@user.perishable_token)}'>clique aqui.</a>
CODE
Email.deliver_padrao(:corpo => corpo, :assunto => "Instruções para trocar a senha", :para => @user.email)
end
end
Agora vamos cria a view new.html.erb em usando_authlogic/app/views/password_resets:
<h1>Esqueci a senha</h1>
Digite o e-mail para o qual será enviado um novo link de acesso.
<% form_tag password_resets_path do %>
<p>
Email<br>
<%= text_field_tag "email" %>
</p>
<p>
<%= submit_tag "Enviar", :id=>"botao" %> |
<%= link_to "Voltar", login_path %>
</p>
<% end %>
Crie também uma view edit.html.erb em usando_authlogic/app/views/password_resets:
<h1>Alteração da senha</h1>
<% form_for @user, :url => password_reset_path, :method => :put do |f| %>
<p>
Senha<br>
<%= f.password_field :password %>
</p>
<p>
Confirmação de senha<br>
<%= f.password_field :password_confirmation %>
</p>
<p>
<%= f.submit "Alterar senha", :id => "botao" %>
</p>
<% end %>
Abra também usando_authlogic/config/routes.rb, a crie mais um rota:
...
map.resources :password_resets
...
Enviando email
Vamos configurar agora o envio de e-mail para que nosso sistema possa enviar um e-mail para ativação e recuperação da senha.
Primeiro adicione as seguintes linhas no seu arquivo usando_authlogic/config/environment.rb. Deve ser inserido no final do arquivo logo após o end.
...
end
require 'tls_smtp'
#Configuração para envio de e-mail
ActionMailer::Base.default_content_type = "text/html"
ActionMailer::Base.delivery_method = :smtp
ActionMailer::Base.raise_delivery_errors = true
ActionMailer::Base.smtp_settings = {
:address => "smtp.seudominio.com",
:port => 587,
:domain => "www.seudominio.com.br",
:user_name => "seuemail@seudominio.com.br",
:password => "suasenha",
:authentication => :login
}
...
Repare a linha require ‘tls_smtp’, precisamos criar este arquivo em usando_authlogic/lib/tls_smtp.rb:
require "openssl"
require "net/smtp"
Net::SMTP.class_eval do
private
def do_start(helodomain, user, secret, authtype)
raise IOError, 'SMTP session already started' if @started
########## PROBLEWA DO ERRO (wrong number of arguments (3 for 2))
no_args = method(:check_auth_args).arity
if no_args == 2
check_auth_args user, secret if user or secret
else
check_auth_args user, secret, authtype if user or secret
end
#check_auth_args user, secret, authtype if user or secret
########## PROBLEWA DO ERRO (wrong number of arguments (3 for 2))
sock = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
@socket = Net::InternetMessageIO.new(sock)
@socket.read_timeout = 60 #@read_timeout
check_response(critical { recv_response() })
do_helo(helodomain)
if starttls
raise 'openssl library not installed' unless defined?(OpenSSL)
ssl = OpenSSL::SSL::SSLSocket.new(sock)
ssl.sync_close = true
ssl.connect
@socket = Net::InternetMessageIO.new(ssl)
@socket.read_timeout = 60 #@read_timeout
do_helo(helodomain)
end
authenticate user, secret, authtype if user
@started = true
ensure
unless @started
# authentication failed, cancel connection.
@socket.close if not @started and @socket and not @socket.closed?
@socket = nil
end
end
def do_helo(helodomain)
begin
if @esmtp
ehlo helodomain
else
helo helodomain
end
rescue Net::ProtocolError
if @esmtp
@esmtp = false
@error_occured = false
retry
end
raise
end
end
def starttls
getok('STARTTLS') rescue return false
return true
end
def quit
begin
getok('QUIT')
rescue EOFError
end
end
end
Agora abra seu console, e execute:
script/generate mailer email
Feito isto, abra o modelo do email, localizado em usando_authlogic/app/models/email.rb:
class Email < ActionMailer::Base
#Email padrao
def padrao(options = {})
email = "seuemail@seudominio.com.br"
recipients options[:para] || ""
from options[:from] || email
subject options[:assunto] || ""
reply_to options[:responder] || email
body :corpo => options[:corpo] || email
end
end
E por fim, crie uma template para o email em usando_authlogic/app/views/email/padrao.html.erb:
<%= @corpo %>
Tradução
Vamo lá, primeiro temos que abrir o arquivo usando_authlogic/config/environment.rb e adicionar a seguinte linha:
...
config.time_zone = 'Brasilia'
config.i18n.default_locale = "pt-BR"
...
Agora crie um arquivo chamado pt-BR.yml em usando_authlogic/config/locales/:
pt-BR:
pagination:
next: "Próximo >>"
prev: "<< Anterior"
authlogic:
error_messages:
login_blank: "não pode ficar em branco"
login_not_found: "não é valido"
login_invalid: "não é valido" #should use osadfsdfnly letters, numbers, spaces, and .-_@ please.
consecutive_failed_logins_limit_exceeded: "limite de tentativas de login excedido"
email_invalid: "não é valido" #should look like an email address.
password_blank: "não pode ficar em branco"
password_invalid: "não é valida"
not_active: "sua conta não está ativa"
not_confirmed: "sua conta não foi confirmada"
not_approved: "sua conta não foi aprovada"
no_authentication_details: "Você não forneceu nenhum detalhe para autenticação"
models:
user_session: UserSession (or whatever name you are using)
attributes:
user_session: (or whatever name you are using)
login: Login
email: E-mail
password: Senha
remember_me: Lembrar-me
activerecord:
attributes:
_all:
created_at: "Data de criação"
updated_at: "Data de atualização"
history:
title: "Título"
content: "Conteúdo"
errors:
template:
header:
one: "model não pôde ser salvo: 1 erro"
other: "model não pôde ser salvo: {{count}} erros."
body: "Por favor, cheque os seguintes campos:"
messages:
inclusion: "não está incluso na lista"
exclusion: "não está disponível"
invalid: "não é válido"
confirmation: "não bate com a confirmação"
accepted: "precisa ser aceito"
empty: "não pode ser vazio"
blank: "não pode ser vazio"
too_long: "é muito longo (não mais do que {{count}} caracteres)"
too_short: "é muito curto (não menos do que {{count}} caracteres)"
wrong_length: "não é do tamanho correto (precisa ter {{count}} caracteres)"
taken: "não está disponível"
not_a_number: "não é um número"
greater_than: "precisa ser maior do que {{count}}"
greater_than_or_equal_to: "precisa ser maior ou igual a {{count}}"
equal_to: "precisa ser igual a {{count}}"
less_than: "precisa ser menor do que {{count}}"
less_than_or_equal_to: "precisa ser menor ou igual a {{count}}"
odd: "precisa ser ímpar"
even: "precisa ser par"
Acabamentos
Vamos definir um template para o sistema, remova todos os arquivos em usando_authlogic/app/views/layouts/, e crie um arquivo chamado application.html.erb, como seguinte conteúdo:
<!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" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<title>Users: <%= controller.action_name %></title>
<%= stylesheet_link_tag 'scaffold' %>
</head>
<body>
<p style="color: green"><%= flash[:notice] %></p>
<% if current_user %>
Logado como <%= current_user.login %> | <%= link_to "Sair", logout_path %>
<% else %>
Você não está logado | <%= link_to "Login", login_path %>
<% end %>
<%= yield %>
</body>
</html>
OK. Agora abre o console e execute o seguinte comando:
rake db:migrate
É isso ai galera! Espero que tenha ajudado de alguma forma. Comentários, criticas ou sugestões sempre serão bem-vindas!!
Lembrando que para quem quiser irei disponibilizar o projeto em http://github.com/diegonogueira/Usando-Authlogic/tree/master
Abraços a todos.
Fiquem com Deus.