我會用到 Devise + Omniauth-facebook + facebook api
好不容易才搞懂... 趕快做個筆記
Devise 的部分可以參考 iHower 實戰聖經 - 使用者認證
為了版面的完整度... 我還是貼code一下好了
- Devise -
1. 在 Gemfile 新增:
gem 'devise'
2. bundle install
3. rails g devise:install產生devise設定檔
4. config/environments/development.rb 和 production.rb 加入寄信時預設的網站網址:
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
5. 確認 app/views/layouts/application.html.erb layout 中可以顯示 flash 訊息
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
6.確認 routes.rb 中有設定網站首頁位置
root :to => "welcome#index"
7.輸入rails g devise user產生 User model 及 Migration
8.輸入rails generate devise:views產生樣板
9. 輸入bin/rake db:migrate建立資料表
10. 在需要登入的 controller 加上before_action :authenticate_user!
before_action :authenticate_user!, :except => [:index]
註:index 頁面不需要權限就可以瀏覽, 要在後方補上 :except => [:index]
11. 在Layout 中加上 登入 / 登出選單
<% if current_user %>
<%= link_to('登出', destroy_user_session_path, :method => :delete) %> |
<%= link_to('修改密碼', edit_registration_path(:user)) %>
<% else %>
<%= link_to('註冊', new_registration_path(:user)) %> |
<%= link_to('登入', new_session_path(:user)) %>
<% end %>
以上內容出自於 iHower 實戰聖經 - 使用者認證 若有不懂請入內參考!
- Omniauth -
Omniauth 是一套使用者認證系統, 針對不同網站的使用者有不統的gem可以使用, 可以透過該平台使用者帳號登入, 使用者與站方, 也不用擔心儲存密碼的安全性問題
首先要先到 developers.facebook 取得 facebook 的 APP_ID + APP_SECRET
1. 點選右上角, 新增應用程式
2. 選擇網頁
3. 新增名稱
5. 我把以下的code 貼到 app/views/layouts/application.html.erb
7. 開發用的 Site URL 請填 http://localhost:3000
8. 取得 APP_ID + APP_SECRET
接下來要使用的是 omniauth-facebook gem <=內有詳細步驟
我這邊整理一下我的做法:
1. 在 Gemfile 新增:
gem 'omniauth-facebook'
2. bundle
3. 新增 migration : rails g migration AddOmniauthToUsers
class AddOmniauthToUsers < ActiveRecord::Migration
def change
add_column :users, :fb_uid, :string
add_column :users, :fb_token, :string
add_index :users, :fb_uid
end
end
4. rake db:migrate
5. 在config/initializers/devise.rb 底下
config.omniauth :facebook, "輸入剛剛得到的APP_ID", "輸入剛剛得到的APP_SECRET", :scope => 'public_profile,email', :info_fields => 'email,name'
6. 編輯 app/models/user.rb,加上:omniauthable, :omniauth_providers => [:facebook]如下:
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers => [:facebook]
7. 編輯 config/routes.rb
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
8. 編輯 app/views/layout/application.html.erb,加上 Facebook 登入的超連結:
<% if current_user %>
<%= link_to('登出', destroy_user_session_path, :method => :delete) %>
....
<% else %>
....
<%= link_to "登入 Facebook", user_omniauth_authorize_path(:facebook) %>
<% end %>
9. 新增 app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
@user = User.from_omniauth(request.env["omniauth.auth"])
if @user.persisted?
sign_in_and_redirect @user, :event => :authentication #this will throw if @user is not activated
set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def failure
redirect_to root_path
end
end
10. 在 app/models/user.rb,新增一個類別方法self.from_omniauth(auth)
def self.from_omniauth(auth) # Case 1: Find existing user by facebook uid user = User.find_by_fb_uid( auth.uid ) if user user.fb_token = auth.credentials.token #user.fb_raw_data = auth user.save! return user end # Case 2: Find existing user by email existing_user = User.find_by_email( auth.info.email ) if existing_user existing_user.fb_uid = auth.uid existing_user.fb_token = auth.credentials.token #existing_user.fb_raw_data = auth existing_user.save! return existing_user end # Case 3: Create new password user = User.new user.fb_uid = auth.uid user.fb_token = auth.credentials.token user.email = auth.info.email user.password = Devise.friendly_token[0,20] #user.fb_raw_data = auth user.save! return user end
- Facebook API -
OK~ 如果貼得順利 , 目前你已經可以用FB 登入你的web
接下來才會是今天的重點 facebook api !
貼code先提到觀念
API 也是採用 MVC概念
Model:
部分主要是儲存我們要傳送的資料
部分主要是儲存我們要傳送的資料
View:
在API中的view 不會真的被看到, 我們會透過 xxx.json.jbuilder ,填寫要傳送的資料,
可以想像一下, 當手機按下那個按鍵時, 其實是發了一個request 給後端, 後端傳json檔案格式給前端, 裡面資料就包含該 action內的資料.
在API中的view 不會真的被看到, 我們會透過 xxx.json.jbuilder ,填寫要傳送的資料,
可以想像一下, 當手機按下那個按鍵時, 其實是發了一個request 給後端, 後端傳json檔案格式給前端, 裡面資料就包含該 action內的資料.
Controller:
會把對應的controller 寫在 api/v1底下.
會把對應的controller 寫在 api/v1底下.
再來講使用者這部分的觀念
a.一開始,使用者註冊, 會得到一組access_token, 之後這組 access_token 就存放在APP內
(可以說用 email+password 去換一組 token, 後台認可的token)
(可以說用 email+password 去換一組 token, 後台認可的token)
b.之後APP連接後台時, 就會拿這組 access_token 來確認使用者
c. 登出, 並不會刪除access_token 而是在後台把這組email+password 一組新的access_token, 此時手機上的access_token 不等於後台的access_token, 所以就登出了
實作:
實作:
1. 新增 app/models/user.rb
before_create :generate_authentication_token
def generate_authentication_token
self.authentication_token = Devise.friendly_token
end
2. rails g migration add_token_to_users.rb
class AddTokenToUsers < ActiveRecord::Migration def change add_column :users, :authentication_token, :string add_index :users, :authentication_token, :unique => true User.find_each do |u| puts "generate user #{u.id} token" u.generate_authentication_token u.save! end end end
3. 執行 rake db:migrate
4. 修改 app/controllers/api_controller.rb
4. 修改 app/controllers/api_controller.rb
class ApiController < ActionController::Base
before_action :authenticate_user_from_token!
def authenticate_user_from_token!
if params[:auth_token].present?
user = User.find_by_authentication_token( params[:auth_token] )
sign_in(user, store: false) if user
end
end
end
5. 新增 app/controllers/api_v1/auth_controller.rb
class ApiV1::AuthController < ApiController
before_action :authenticate_user!, :only => [:logout]
def login
user = User.find_by_email( params[:email] )
if user && user.valid_password?( params[:password] )
render :json => { :user_id => user.id,
:auth_token => user.authentication_token }
else
render :json => { :message => "email or password is not correct" }, :status => 401
end
end
def logout
user = current_user
user.generate_authentication_token
user.save!
render :json => { :message => "ok" }
end
end
6. 在 config/routes.rb 的 scope :path => '/api/v1/', :module => "api_v1", :as => 'v1', :defaults => { :format => :json } do 下方新增
詳細code 參考 ihower github (以下)
post "/login" => "auth#login"
post "/logout" => "auth#logout"
7. 修改在 app/controllers/api_v1/auth_controller.rb 中的 def login 把 user = User.find_by_email( params[:email] ) 替換成 success = false
if params[:email] && params[:password]
user = User.find_by_email( params[:email] )
success = user && user.valid_password?( params[:password] )
elsif params[:access_token]
fb_data = User.get_fb_data( params[:access_token] )
if fb_data
auth_hash = OmniAuth::AuthHash.new({
uid: fb_data["id"],
info: {
email: fb_data["email"]
},
credentials: {
token: params[:access_token]
}
})
user = User.from_omniauth(auth_hash)
end
success = fb_data && user.persisted?
end
user = User.find_by_email( params[:email] )
下方 if user && user.valid_password?( params[:password] )
render :json => { :user_id => user.id,
:auth_token => user.authentication_token }
替換成: if success
render :json => { :auth_token => user.authentication_token,
:user_id => user.id}
8. 最後在 app/models/user.rb 新增 (若原本有請修改成以下)
def self.get_fb_data(access_token)
res = RestClient.get "https://graph.facebook.com/v2.4/me", { :params => { :access_token => access_token } }
if res.code == 200
JSON.parse( res.to_str )
else
Rails.logger.warn(res.body)
nil
end
end
2. Add auth API
1. 測試登入 =>有得到 auth_token
2. 測試 logout =>得到回覆 ok
讚讚!!!
回覆刪除好說好說~
刪除厲害厲害
回覆刪除