Ruby on Rails使用doorkeeper实现oauth2保护api接口

最近在玩基于rails的api,自然有些api不能够完全开放,所以也就有了认证这一步,api认证现在一般用oauth2,现在经常看到的第三方社交登录其实就是oauth2。rails在oauth2比较受欢迎的方案就是doorkeeper,看了一下很容易使用。这里介绍一下doorkeeper的基本使用和一些需要注意的问题。

个人原创,版权所有,转载请注明出处,并保留原文链接:

https://www.embbnux.com/2016/01/26/ruby_on_rails_use_doorkeeper_for_auth2-0_to_protect_api/

参考:doorkeeper wiki

一、使用doorkeeper

首先在gemfile加入

gem 'doorkeeper'

然后终端下:

rails generate doorkeeper:install
rails generate doorkeeper:migration
rake db:migrate

会生成配置文件和migration

还会在routes.rb里面自动加入  ‘use_doorkeeper’

这样网站就会自动多出以下接口

GET /oauth/authorize/:code
GET /oauth/authorize
POST /oauth/authorize
DELETE /oauth/authorize
POST /oauth/token
POST /oauth/revoke
resources /oauth/applications
GET /oauth/authorized_applications
DELETE /oauth/authorized_applications/:id
GET /oauth/token/info

然后设置api生成权限:

编辑config/initializers/doorkeeper.rb

加入下面的代码:

resource_owner_authenticator do
  User.find_by(id: session[:current_user_id]) || redirect_to(login_url)
end

这样访问/oauth/applications就可以新建应用了,建好应用后会有一个应用id和应用私钥,这是日后调用api要用的

二、绑定创建用户和应用

为了使新建的应用和创建的用户绑定需要做以下操作:

rails generate doorkeeper:application_owner
rake db:migrate

修改配置文件加入下面:
enable_application_owner confirmation: false

新建app/controllers/oauth/applications_controller.rb:


module Oauth
 class ApplicationsController < Doorkeeper::ApplicationsController
   before_action :authenticate_user!
   def create
     @application = Doorkeeper::Application.new(application_params)
     @application.owner = current_user if Doorkeeper.configuration.confirm_application_owner?
     if @application.save
       flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
       redirect_to oauth_application_url(@application)
     else
       render :new
     end
   end
 end
end

route配置:

use_doorkeeper do
  controllers applications: 'oauth/applications'
end

这样再次创建的应用就会有一个所有者,application.owner就是这个人

三、oauth2.0的使用

oauth2.0的认证流程有这几个:

 # "authorization_code" => Authorization Code Grant Flow
 # "implicit" => Implicit Grant Flow
 # "password" => Resource Owner Password Credentials Grant Flow
 # "client_credentials" => Client Credentials Grant Flow

这里我启用authorization_code,password,client_credentials

在配置里面加:

resource_owner_from_credentials do |_routes|
 u = User.find_by(name: params[:name])
 u if u && u.valid_password?(params[:password])
end
grant_flows %w(authorization_code client_credentials)
Doorkeeper.configuration.token_grant_types << 'password' #末尾追加

这样这三个模式就都可以用了

在api里面启用oauth2.0保护

class Api::V1::ProductsController > Api::V1::ApiController
  before_action :doorkeeper_authorize! # Require access token for all actions
  # your actions
end

这样没有access_token是无法访问api的

四、oauth2.0流程测试

1、authorization_code

这个就是经常见到的第三方登录,首先跳到用户网站登录认证,再跳回应用网站。这边测试就直接用脚本实现了:

api_test.rb:

require 'oauth2'
client_id = '...'
client_secret = '...'
redirect_uri = '...'
site = "http://localhost:3000" #server

client = OAuth2::Client.new(client_id, client_secret, :site => site)
authorize_url = client.auth_code.authorize_url(:redirect_uri => redirect_uri)
puts authorize_url

浏览器访问输出的authorize_url,登录后点击认证后会跳转到一个链接,这个链接有一个code参数,存下这个code,继续:

require 'oauth2'

client_id = '...'
client_secret = '...'
redirect_uri = '...'
site = "http://localhost:3000" #server

client = OAuth2::Client.new(client_id, client_secret, :site => site)
#authorize_url = client.auth_code.authorize_url(:redirect_uri => redirect_uri)
puts authorize_url
code = "..." # 从浏览器得到的
access_token = client.auth_code.get_token(code, :redirect_uri => redirect_uri)
puts access_token.token
response = access_token.get('/api/v1/users.json')
JSON.parse(response.body)

输出的access_token.token就是访问api所需要的token了

2. password认证

有时候在app上调用api不可能跳转到原网站去登录,所以就得让app有用户输入密码和用户名登录后获取access_token的功能,这就是password认证。 测试api_test2.rb :


require 'oauth2'

client_id = '...'
client_secret = '...'
redirect_uri = '...'
site = "http://localhost:3000" #server
client = OAuth2::Client.new(client_id, client_secret, :site => site)
access_token = client.password.get_token('username', '12345678')
puts access_token.token
response = access_token.get('/api/v1/users.json')
puts JSON.parse(response.body)

3. client_credentials

有时候api是给指定用户用的,不用登录就可以知道是谁在用,这时候就可以client_credentials,这个获取的access_token是和应用绑定的。

测试api_test3.rb :

require 'rest-client'
require 'json'
client_id = '...'
client_secret = '...'

response = RestClient.post 'http://localhost:3000/oauth/token', {
  grant_type: 'client_credentials',
  client_id: client_id ,
  client_secret: client_secret
}
token = JSON.parse(response)["access_token"]
puts token

data = RestClient.get 'http://localhost:3000/api/v1/me.json', { 'Authorization' => "Bearer #{token}" }
puts data

五、api里面的用户系统

在api里面可以通过当前的access_token之前当前登录用户:


def current_login_owner
  @current_login_owner ||= User.find_by(id: doorkeeper_token.resource_owner_id) if doorkeeper_token
end

如果是用client_credentials方式认证的则变成下面:

@current_login_user ||= doorkeeper_token.application.owner if  doorkeeper_token

结束!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

Time limit is exhausted. Please reload the CAPTCHA.