하고자 하는 것들
restful authentication plugin과 open_id_authentication plugin을 이용해서 openid를 이용한 login과정을 구현한다.
restful authentication을 이용해서 회원 가입 과정은 구현하지 않을 것이다.
첫번째로 openid를 이용해서 로그인하는 가입자는 user table에 저장한다. 이 때 name과 email도 같이 저장한다.
로그인 아이디 기억하기(자동 로그인) 기능을 구현한다.
로그인 입력 창에 openid 로고를 넣는다.
gem과 plugin의 설치
ruby-openid gem 설치한다.
- gem install ruby-openid
open id authentication plugin 설치힌다.
- ruby script\plugin install open_id_authentication
restful authentication plugin 을 설치한다.
- ruby script\plugin install http://svn.techno-weenie.net/projects/plugins/restful_authentication/
migration 을 이용한 db table 생성
restful authentication의 generate를 이용해서 user와 sessions를 생성한다.
- ruby script/generate authenticated user sessions
위 명령은 user model 과 sessions controller, users controller 를 생성한다. users controller가 필요없다면 삭제해도 좋다.
user table에 identity_url을 추가한다.
- # db/migrate/001_create_users.rb
- t.column :identity_url, :string
- t.column :name, :string
rake를 이용해서 open_id_authentication migration을 추가한다.
- rake open_id_authentication:db:create
db/migrate/002_add_open_id_authentication_tables.rb 파일이 생성된다.
migrate를 수행해 db table을 생성한다.
- rake db:migrate
기타 설정
application controller에 다음을 추가한다. 기본적으로 모든 controller들의 요청이 login_required에 의해 login을 요구하도록 하고 있다.
- include AuthenticatedSystem
before_filter :login_required
login이 필요없는 controller 혹은 action에 대해서는 skip_before_filter를 이용할 수 있다.
sessions controller는 login_required가 필요없으니 다음을 추가한다.
- skip_before_filter :login_required
routes.rb를 다음과 같이 한다.
- map.resources :users
map.open_id_complete 'session', :controller => "sessions", :action => "create", :conditions => { :method => :get }
map.resource :session
map.open_id_complete는 openid provider로 부터 인증이 완료되었음을 알리는 request를 sessions controller의 create action으로 보내기 위함이다. 이 요청은 open_id_authentication plugin에서는 GET "/session?각종_파라미터들" 이다. 그래서 /session에 대한 get 요청을 create action으로 보낸다. 이것이 마음에 들지 않는다면 return_to option을 조절해서 원하는 값으로 바꿀 수 있다. return_to option에 대해서는 밑에 다시 설명한다.
view의 구현
views/sessions/new.html.erb 를 다음과 같이 한다.
- <%= stylesheet_link_tag "login" %>
<div class="message">
<%= logged_in? ? "로그인되었습니다." : "로그아웃 상태입니다." %>
<%= link_to "로그아웃", session_url, :method=>:delete if logged_in? %>
</div>
<% form_tag(session_url) do %>
<label for="openid_url">OpenID:</label>
<%= text_field_tag "openid_identifier", "", :class=>"openid" %>
<br/>
<%= check_box_tag 'remember_me' %>
<label for="remember_me" class="rememberMe">아이디 기억하기</label>
<% end %>
- login.css를 포함하고,
- 로그인 여부를 보여주고,
- openid를 입력할 수 있는 form을 보여주고, openid 권장 표준 스타일 가이드에 따르면 openid input의 이름을 openid_indentifier로 하라고 한다.
- 아이디 기억하기 checkbox를 보여준다.
login.css를 다음과 같이 작성해서 style을 준다.
- body {
margin: 50px 0px 0px 0px;
text-align: center;
font-size: 14px;
}
div.message {
margin: 10px 0px;
}
input.openid {
background:transparent url(/images/openid_bg.gif) no-repeat left center;
padding-left: 18px;
border: 1px solid;
}
openid logo(openid_bg.gif)는 http://openid.net/logos/ 에서 적당한 것으로 다운 받자.
model의 구현
user model은 restful authentication에 의해 이미 생성되어 있다. openid 인증을 적용하기 위해서는 약간의 수정이 필요한데, 그 내용은 다음과 같다.( 변경된 내용만 적는다. )
- validates_presence_of :email
validates_presence_of :login, :if => :not_openid? - validates_length_of :login, :within => 3..40, :if => :not_openid?
- validates_uniqueness_of :login, :email, :case_sensitive => false, :allow_nil => true
- attr_accessible :login, :email, :password, :password_confirmation, :identity_url
- def password_required?
not_openid? && (crypted_password.blank? or not password.blank?)
end
def not_openid?
identity_url.blank?
end
주된 내용은 "openid를 이용하는 사용자에게는 login, password같이 필요없는 것들에 대한 validation check을 하는 않는다." 이다.
Controller 의 구현
sessions controller의 내용은 꽤 길다.
- class SessionsController < ApplicationController
skip_before_filter :login_required
def create
if using_open_id? params[:openid_identifier]
open_id_authentication params[:openid_identifier]
else
password_authentication(params[:login], params[:password])
end
end
def destroy
self.current_user.forget_me if logged_in?
cookies.delete :auth_token
reset_session
flash[:notice] = "로그아웃 되었습니다."
redirect_back_or_default('/')
end
protected
def password_authentication(login, password)
self.current_user = User.authenticate(login, password)
if logged_in?
successful_login
else
failed_login "Sorry, that username/password doesn't work"
end
end
def open_id_authentication(openid_url)
authenticate_with_open_id(openid_url, :required=>[:nickname, :email],
:return_to=>openid_return_url) do |result, identity_url, registration|
if result.successful?
@user = User.find_or_initialize_by_identity_url(identity_url)
if @user.new_record?
@user.name = registration['nickname']
@user.email = registration['email']
@user.save #TODO : need to check validation
end
self.current_user = @user
successful_login
else
failed_login result.message
end
end
end
private
def openid_return_url
"#{request.protocol + request.host_with_port + request.relative_url_root + request.path}" +
"?remember_me=#{params[:remember_me]}"
end
def successful_login
if params[:remember_me] == "1"
self.current_user.remember_me
cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
end
redirect_to(root_url)
end
def failed_login(message)
flash[:error] = message
redirect_to(new_session_url)
end
end
openid_identifier가 있다면 이 사용자는 openid를 이용해서 로그인을 하는 것이다. 만약 그렇지 않다면 일반적인 login/password로 로그인을 하려는 것이다. login/password 인증이 필요없다면 password_authentication 를 빼도 된다.
로그아웃 과정은 평범하다.
open_id_authentication 를 살펴보면,
인증을 하면서 nickname과 email을 요청한다.
- authenticate_with_open_id(openid_url, :required=>[:nickname, :email],
:return_to=>openid_return_url) do |result, identity_url, registration|
만약 처음으로 로그인하는 사용자라면 user 를 하나 생성하고 name과 email을 저장한다.
- @user = User.find_or_initialize_by_identity_url(identity_url)
if @user.new_record?
@user.name = registration['nickname']
@user.email = registration['email']
@user.save #TODO : need to check validation
end
인증이 잘 되었다면 current_user를 user로 해 주자.
로그인 기억하기 구현
openid provider가 인증을 마치고 응답을 줄때는 :return_to 의 값으로 응답한다.(꼭 :return_to의 값은 아니다. 여기에 다른 파라미터들도 여러개 붙는다.) (진이가 오늘 이쁜짓을 많이하네.. 이제 정말 고양이 스럽다. 난 그 깊은 속을 알 길이 없구나.) 여기서 return_to의 값으로 정의한 openid_return_url은 remember_me 가 parameter로 포함되어 있다. 그래서 "http://site.co.kr/sessions?remember_me=1" 같은 모양이다. 만약 응답 url이 "/sessions 인 것이 마음에 들지 않는다면 원하는 값으로 수정할 수도 있다. 수정 후에는 routes.rb에 그 내용을 써주는 것을 잊지 말자.
- authenticate_with_open_id(openid_url, :required=>[:nickname, :email],
:return_to=>openid_return_url) do |result, identity_url, registration| - ...
- def openid_return_url
"#{request.protocol + request.host_with_port + request.relative_url_root + request.path}" +
"?remember_me=#{params[:remember_me]}"
end
:return_to options을 굳이 적어주는 이유는 remember_me 정보를 보전하기 위함이다. 이 정보는 인증이 잘 이루어진 후 로그인 정보를 기억할지를 결정하는 데 사용된다.
- def successful_login
if params[:remember_me] == "1"
self.current_user.remember_me
cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
end
redirect_to(root_url)
end
써보니
와.. 생각보다 훨씬 간단하다.
난 아직 openid 스펙도 잘 모르는데, 이 기능을 구현할 수 있다는 사실이 놀랍고, 두렵다.
(진이는 커가면서 어리광이 느는 것 같다.)
참조
acts as authentication plugin과 openid 를 같이 사용하기
http://shinji.springnote.com/pages/475676
이 글은 스프링노트에서 작성되었습니다.
'Rails' 카테고리의 다른 글
| [Rails] RailRoad를 사용해서 model과 controller를 graph로 보기 (0) | 2008/05/05 |
|---|---|
| Integration Testing in Ruby with RSpec's Story Automation Framework (0) | 2008/04/16 |
| [Rails] Openid 사용하기 (0) | 2008/04/06 |
| [Rails] HAML 사용하기 (0) | 2008/04/06 |
| [Rails] netbeans에서 haml highlighting 지원 (0) | 2008/04/06 |
| [Rails]controller에 파라미터로 들어온 민감한 데이터를 로그에 남기지 않기 (0) | 2008/04/06 |