`
酷的飞上天空
  • 浏览: 522406 次
  • 性别: Icon_minigender_1
  • 来自: 无锡
社区版块
存档分类
最新评论

Oauth简易服务器端ruby实现,仿新浪微博验证的方式

阅读更多

前段时间用ruby实现了新浪微博的简易Oauth的客户端,对aouth协议有了一个大概的了解。

完成服务器端的实现,纯属自己一个的加深学习aouth的想法,嘿嘿.  验证支持basic,oauth,xauth

 

接收下用到的controller

OauthController 负责对用户aouth验证和发放accessToken

Oauth_base_controller  所有需要aouth验证的controller的父类,对子类的所有方法进行权限验证

一个帮助类

OauthUtil 负责字符串的加密和拼接

 

OauthController提供三个对外方法:

request_token

authorize

access_token

具体方法含义,对应oauth验证的每个url。

具体代码如下

 

# coding: utf-8
#      HTTP 400 Bad Request
#          o Unsupported parameter
#          o Unsupported signature method
#          o Missing required parameter
#          o Duplicated OAuth Protocol Parameter
#     HTTP 401 Unauthorized
#          o Invalid Consumer Key
#          o Invalid / expired Token
#          o Invalid signature
#          o Invalid / used nonce

class OauthController < Oauth_base_controller
  TEST_APP_KEY = "123456"
  TEST_APP_SECRET = "654321"
  TEST_OAUTH_TOKEN = "QWERTY"
  TEST_OAUTH_TOKEN_SECRET ="YUIOP"
  TEST_OAUTH_VERIFIER = "ASDFG"
  TEST_ACCESS_TOKEN = "HJKLG"
  TEST_ACCESS_TOKEN_SECRET = "ZXCVB"
  protect_from_forgery :except => [:request_token,:authorize,:access_token]
  skip_before_filter :auth
  
  def request_token
    oauth_signature = params["oauth_signature"]
    puts "=======================params======================",params.inspect
    render :json=>%({"error":400,"detail":"need signature"}),:status => 400,:callback=>params[:callback] and return if oauth_signature.blank?
    oauth_signature = CGI::unescape oauth_signature
    oauth_params = {
        :oauth_consumer_key => params["oauth_consumer_key"],
        :oauth_timestamp => params["oauth_timestamp"],
        :oauth_nonce => params["oauth_nonce"],
        :oauth_version => params["oauth_version"] || "1.0",
        :oauth_signature_method => "HMAC-SHA1"
    }
    oauth_params[:oauth_callback] = params["oauth_callback"] if params["oauth_callback"]
    oauth_params[:oauth_body_hash] = params["oauth_body_hash"] if params["oauth_body_hash"]

    httpmethod = request.method.to_s
    base_uri = "http://#{request.headers["HTTP_HOST"]}/oauth/request_token"
    key = "#{TEST_APP_SECRET}&"
    create_base_string = OauthUtil.create_oauth_signature(httpmethod.upcase, base_uri, oauth_params, key)
    puts "===============oauth_signature,create_base_string",oauth_signature,create_base_string
    render :json=>%({"error":401,"detail":"Invalid signature"}),:status => 401,:callback=>params[:callback] and return if create_base_string != oauth_signature
    render :text => "oauth_token=#{TEST_OAUTH_TOKEN}&oauth_token_secret=#{TEST_OAUTH_TOKEN_SECRET}" if params["oauth_callback"].nil?
    render :text => "oauth_token=#{TEST_OAUTH_TOKEN}&oauth_token_secret=#{TEST_OAUTH_TOKEN_SECRET}&oauth_callback_confirmed=true" if params["oauth_callback"]
  end

  def authorize

    @token = params[:oauth_token]
    @oauth_callback = params[:oauth_callback]
    render :text => "no token error" and return  if @token.blank?
    
    if request.get?
       @name = "测试应用"
       render :action => "authorize"
       return
    end

    if request.post?
       username = params[:username]
       password = params[:password]
       if username.nil? || password.nil? || username != "test" || password !="test"
         flash[:notice] = "用户名或密码错误"
         render :action => "authorize"
         return
       else
         render :text => "授权已完成" and return if params[:oauth_callback].blank?
         callback = OauthUtil.callback_url(CGI::unescape(params[:oauth_callback]), @token, TEST_OAUTH_VERIFIER)
         redirect_to callback
         return
       end
    end
    return :text=>""
  end
  
  def access_token
    oauth_signature = params["oauth_signature"]
    render :json=>%({"error":400,"detail":"need signature"}),:status => 400,:callback=>params[:callback] and return if oauth_signature.blank?
    oauth_signature = CGI::unescape oauth_signature
    puts "=======================params======================",params.inspect

    ## for oauth
    oauth_params = {
        :oauth_consumer_key => params["oauth_consumer_key"],
        :oauth_token => params["oauth_token"],
        :oauth_timestamp => params["oauth_timestamp"],
        :oauth_nonce => params["oauth_nonce"],
        :oauth_version => params["oauth_version"] || "1.0",
        :oauth_signature_method => "HMAC-SHA1"
    }

    ## for xauth
    oauth_params = {
      	:x_auth_username => params["x_auth_username"],
      	:x_auth_password => params["x_auth_password"],
      	:x_auth_mode => "client_auth",
        :oauth_consumer_key => params["oauth_consumer_key"],
        :oauth_timestamp => params["oauth_timestamp"],
        :oauth_nonce => params["oauth_nonce"],
        :oauth_version => "1.0",
        :oauth_signature_method => "HMAC-SHA1"
    } if xauth?

    render :json=>%({"error":403,"detail":"unsupport XAuth"}),:status => 403,:callback=>params[:callback] and return if xauth?
    render :json=>%({"error":401,"detail":"Invalid signature"}),:status => 401,:callback=>params[:callback] and return if params["x_auth_username"] != "test" || params["x_auth_password"] != "test"

    oauth_params[:oauth_body_hash] = params["oauth_body_hash"] if params["oauth_body_hash"]
    oauth_params[:oauth_verifier] = params["oauth_verifier"] if params["oauth_verifier"]

    httpmethod = request.method.to_s
    base_uri = "http://#{request.headers["HTTP_HOST"]}/oauth/access_token"
    key = "#{TEST_APP_SECRET}&#{TEST_OAUTH_TOKEN_SECRET}"
    key = "#{TEST_APP_SECRET}&" if xauth? ## for xauth
    create_base_string = OauthUtil.create_oauth_signature(httpmethod.upcase, base_uri, oauth_params, key)
    puts "===============oauth_signature,create_base_string",oauth_signature,create_base_string
    render :json=>%({"error":401,"detail":"Invalid signature "}),:status => 401,:callback=>params[:callback] and return if create_base_string != oauth_signature
    render :text => "oauth_token=#{TEST_ACCESS_TOKEN}&oauth_token_secret=#{TEST_ACCESS_TOKEN_SECRET}"
  end

  def xauth?
    params["x_auth_username"] || params["x_auth_password"]
  end
end

 其中OauthUtil类源码如下

# coding: utf-8
require "uri"
class OauthUtil
  class << self
    ## 生成base_string 字符串
    def base_string(httpmethod,base_uri,request_params)
      base_str  = httpmethod + "&" + CGI::escape(base_uri) + "&"
      base_str += request_params.to_a.sort{|a,b| a[0].to_s <=> b[0].to_s}.map{|param| CGI::escape(param[0].to_s) + "%3D"+ CGI::escape(param[1].to_s)}.join("%26")
      puts "===========base_string===========",base_str
      base_str
    end

    ## see http://stackoverflow.com/questions/1959486/digest-hmac-is-part-of-ruby-standard-lib
    def digest(value,key)
      puts "===========digest_key===========",key  
      signature = Base64.encode64 OpenSSL::HMAC.digest("SHA1", key, value)
      puts "===========signature===========",signature.strip  
      signature.strip
    end

    def create_oauth_signature(httpmethod,base_uri,request_params,key)
      create_base_string =  base_string httpmethod,base_uri,request_params
      digest create_base_string,key
    end

    def callback_url(url,oauth_token,oauth_verifier)
      begin
      uri = URI::parse(url)
      rescue Exception
        return ""
      end
      query_string = uri.query
      query_string = "&#{query_string}" if query_string
      "#{uri.scheme}://#{uri.host}:#{uri.port}#{uri.path}?oauth_token=#{oauth_token}&oauth_verifier=#{oauth_verifier}#{query_string}"
    end
  end
end

 涉及到的erb页面authorize.html.erb页面代码如下

是否要授权<%=@name%>应用,使用你在本网站的部分功能?
<form action="/oauth/authorize" method="post">
  <input type="hidden" value="<%=@token%>" name="oauth_token"/>
  <input type="hidden" value="<%=@oauth_callback%>" name="oauth_callback"/>
  账号:<input type="text" name="username"></input><br />
  密码:<input type="password" name="password"></input><br />
  <input type="submit" value="授权"/>
</form>

如果不想授权,请关闭此页面。

<p><%=flash[:notice]%></p>

 

Oauth_base_controller 主要方法为auth,为子类提供验证,具体源码如下:

# coding: utf-8 
class Oauth_base_controller < ApplicationController
  
  before_filter :handle_headers_oauth_string
  before_filter :auth

  ## 进行验证
  def auth
    authenticate = request.headers["AUTHORIZATION"] || request.headers["HTTP_AUTHORIZATION"]
    if authenticate.blank?
      auth_oauth
    else
      authenticate_method = authenticate[0,5]
      authenticate_body = authenticate[5,authenticate.size - 5]
      case authenticate_method
      when "Basic"
        @name_pwd = Base64.decode64(authenticate_body.strip)
        puts "name_pwd",@name_pwd
        render :json=>%({"error":401,"detail":"authenticate format error"}),:status => "401",:callback=>params[:callback] and return false if @name_pwd.split(":").size != 2
        auth_basic
      else "OAuth"
        auth_oauth
      end
    end
  end

  ## 要移除的相关非oauth计算签名参数
  def except_other_params
    except_params "realm",params["realm"]
    except_params "oauth_signature",params["oauth_signature"]
    except_params "action",params["action"]
    except_params "controller",params["controller"]
  end

  ## 将非oauth计算签名所需参数移动到except_params中去
  def except_params(key,value)
    @except_params = {} if @except_params.nil?
    @except_params[key] = value
    params.delete key.to_s
    params.delete key.to_sym
  end

  private

  ## 进行 Basic 验证
  def auth_basic
    username = @name_pwd.split(":")[0]
    password = @name_pwd.split(":")[1]
    render :json=>%({"error":401,"detail":"authenticate fail"}),:status => "401",:callback=>params[:callback] and return false if username != "test" || password != "test"
  end

  ## 进行oauth 验证
  def auth_oauth
    params["oauth_version"] ||= "1.0"
    puts "=======================params======================",params.inspect
    oauth_signature = params["oauth_signature"]
    render :json=>%({"error":400,"detail":"need signature"}),:status => "400",:callback=>params[:callback] and return if oauth_signature.blank?
    oauth_signature = CGI::unescape oauth_signature
    
    except_other_params
    
    httpmethod = request.method.to_s
    base_uri = "http://#{request.headers["HTTP_HOST"]}#{request.path}"
    key = "#{app_secret_by_oauth_consumer_key params["oauth_consumer_key"]}&#{token_secret_by_access_token params["oauth_token"]}"
    create_base_string = OauthUtil.create_oauth_signature(httpmethod.upcase, base_uri, params, key)
    puts "===============oauth_signature,create_base_string",oauth_signature,create_base_string
    render :json=>%({"error":401,"detail":"Invalid signature"}),:status => "401",:callback=>params[:callback] and return if create_base_string != oauth_signature
  end

  ## 查找app_key 对应的secret
  def app_secret_by_oauth_consumer_key(app_key)
    "654321"
  end

  ## 查找access_token 对应的secret
  def token_secret_by_access_token(access_token)
    "ZXCVB"
  end

  ## 整理header里面的oauth 的参数到params里面去
  def handle_headers_oauth_string
    authenticate = request.headers["AUTHORIZATION"] || request.headers["HTTP_AUTHORIZATION"]
    return true if authenticate.blank?

    oauth_method = authenticate[0,5]

    return true if oauth_method == "Basic"
    render :json=>%({"error":401,"detail":"http_headers content error"}),:status => "401",:callback=>params[:callback] and return false if oauth_method != "OAuth"

    oauth_body = authenticate[5,authenticate.size - 5]
    oauth_body.split(",").each do |header_param|
      next if header_param.split("=").size != 2
      k = header_param.split("=")[0].strip
      v = header_param.split("=")[1].strip.gsub(/\"/,"")
      params[k] = v
    end
  end
end

 

使用方式为,继承Oauth_base_controller,然后子类中的所有方法则都要进行验证后才能访问,如:

# coding: utf-8
class OauthTestController < Oauth_base_controller

  def index
    puts "=======================",request.headers.inspect
    render :text => request.headers.inspect
  end

end

 

当访问这个index方法的时候,会进行oauth或basic验证,如果通过则返回客户端的请求头字符串,否则返回相应的验证失败代码

 

分享到:
评论
2 楼 酷的飞上天空 2013-01-13  
zhangyuxiu 写道
请教下:
1. 获取APP_KEY及APP_SECRET属于服务器端的设计范围么?
2. 上述实现的服务器端应该等同于新浪微博开放平台吧?那么测试用的APP,APP_SECRET是如何获得的啊?


回答一:
APP_KEY及APP_SECRET的发放和验证是分开的。发放端一般是一个特定的应用管理平台,可以让用户自己提交应用然后平台自动生成这两个,也可能是管理员手动建立。
回答而:
上述实现只是自己研究新浪oauth客户端验证时,兴趣上来想自己实现下服务器,只能说简易实现,实际情况肯定要复杂的多的。

另:现在新浪已经换成oauth2了。
1 楼 zhangyuxiu 2012-12-25  
请教下:
1. 获取APP_KEY及APP_SECRET属于服务器端的设计范围么?
2. 上述实现的服务器端应该等同于新浪微博开放平台吧?那么测试用的APP,APP_SECRET是如何获得的啊?

相关推荐

    仿新浪微博php程序xweibo

    新浪微博采用OAuth协议进行第三方应用授权,XWeibo在实现用户授权登录功能时,也需要理解并应用OAuth协议,确保安全地获取和使用用户的微博账户权限。 6. **模板引擎** 为了提高视图层的复用性和可维护性,XWeibo...

    仿新浪微博的网站源码

    该压缩包文件包含的是一个仿新浪微博的网站源代码,它是一个基于Web开发的项目,用于构建类似新浪微博的社交网络平台。下面将详细解释这个项目可能涉及的一些关键知识点和技术。 1. **网站架构**:仿新浪微博的网站...

    android仿新浪微博应用

    7. **登录与授权**: 实现微博登录功能,需要集成新浪的OAuth2.0授权协议,通过SDK进行用户验证和授权,获取访问令牌。 8. **推送通知**: 对于实时性要求高的社交应用,可以集成Firebase Cloud Messaging (FCM) 来...

    新浪微博OAuth 验证

    微博作为中国主要的社交媒体平台之一,其开放API接口允许开发者通过OAuth验证机制来获取用户的授权,以便在第三方应用中实现微博的功能,如发布微博、读取用户信息等。OAuth验证是一种广泛使用的授权协议,它使得...

    swift-仿新浪微博截图分享

    5. 社交媒体集成:为了实现微博分享,需要使用新浪开放平台提供的SDK。首先,开发者需要注册并获取App Key和App Secret。然后,将SDK集成到项目中,通过OAuth2.0授权协议获取用户授权,调用SDK提供的接口发送分享...

    C#新浪微博验证程序

    C#新浪微博验证程序是一款基于C#编程语言开发的应用,主要用于实现微博用户在第三方网站上的身份验证。这个程序的核心功能是利用OAuth授权协议,允许用户使用其新浪微博账号登录到非新浪的网站,从而提高用户体验,...

    OAuth2.0新浪微博简单示例

    这个“OAuth2.0新浪微博简单示例”是为初学者设计的,旨在帮助理解OAuth2.0的工作原理及其在实际应用中的实现方式,特别是与新浪微博的集成。 首先,我们来深入了解一下OAuth2.0的核心概念: 1. **客户端(Client...

    新浪微博OAuth授权的Java实现.pdf

    新浪微博OAuth授权的Java实现 一、 OAuth协议简介 OAuth协议是一种广泛使用的授权协议,使用户不需要直接向第三方应用提供用户名及密码,且使一个账户在多个网站中使用成为可能。OAuth协议的细节描述可参考其官方...

    Android 仿新浪微博

    【Android 仿新浪微博】项目是基于Android平台的一个开源项目,其目标是实现一个类似新浪微博的应用。这个项目对于学习Android开发,尤其是社交应用的构建,提供了丰富的实践参考。 首先,项目的核心在于用户界面...

    Android仿新浪微博客户端源代码

    在Android平台上,开发一款仿新浪微博客户端是一项挑战性的任务,它涉及到多个关键的技术点,包括UI设计、网络请求、数据解析、缓存策略、用户登录授权、动态加载与刷新、社交功能实现等。以下是对这个项目中涉及的...

    OAUTH1.0腾讯登录与新浪微博登录

    OAuth 1.0的核心概念包括三个主要角色:资源所有者(用户)、客户端(第三方应用)和认证服务器(腾讯或新浪微博)。当用户想要使用第三方应用访问其在社交媒体平台上的数据时,OAuth 1.0提供了一种授权机制。 1. *...

    新浪微博 api Oauth认证.

    【标题】:“新浪微博API OAuth认证”是一个关于使用OAuth授权机制与新浪微博API进行交互的技术主题。OAuth是一种开放标准,允许用户提供一个令牌,而不是用户名和密码来访问他们存储在特定服务提供者的数据。在微博...

    使用Oauth2.0实现新浪微博客户端(C#)

    在本例中,我们将讨论如何使用C#来实现一个基于OAuth2.0的新浪微博客户端。 **C#** 是一种面向对象的编程语言,由微软开发,主要用于.NET Framework。在C#中,我们可以利用HTTP请求库如HttpClient或Flurl来发送网络...

    OAuth2认证之新浪微博

    在实现微博的OAuth2认证时,我们通常会经历以下步骤: 1. **注册应用**:首先,开发者需要在微博开放平台注册自己的应用,获取到应用的ID(Client ID)和密钥(Client Secret)。这两个值在后续的认证过程中至关...

    新浪微博登录 sina

    总之,这篇关于“新浪微博登录 sina”的博文和提供的源码文件“sina_oauth”,对于想要在自己的应用中实现微博登录功能的开发者来说,是一个宝贵的资源,可以帮助他们快速理解和实践OAuth授权过程,从而提高开发效率...

    新浪微博OAuth2.0认证实现登陆

    这篇文档将详细介绍如何使用OAuth2.0协议实现新浪微博的登录功能。OAuth2.0是目前广泛采用的授权框架,它允许第三方应用在用户许可的情况下访问其存储在特定服务提供商(如新浪微博)上的数据,而无需获取用户的...

    新浪微博OAuth2.0登录

    本教程将详细阐述如何使用C#语言和MVC框架实现新浪微博的OAuth2.0登录流程。 首先,我们需要理解OAuth2.0的基本流程。OAuth2.0的核心是四类角色:资源所有者(用户)、资源服务器(如新浪微博)、客户端(你的应用...

    仿新浪微博客户端

    【标题】"仿新浪微博客户端"涉及的是一款针对Android平台开发的应用程序,旨在模仿新浪微博的功能和用户体验。这样的项目对于初学者来说是一个很好的实践案例,因为它涵盖了移动应用开发中的多个关键技术和设计原则...

    基于ThinkPHP框架下的新浪微博用户同步登陆代码

    在这个场景下,我们关注的是一个基于ThinkPHP框架实现的新浪微博用户同步登录功能的代码实例。这个功能允许用户使用他们的新浪微博账号直接登录到你的网站,为用户提供便利,同时也可以帮助开发者吸引并保留更多用户...

    仿新浪微博

    【标题】:“仿新浪微博”项目实现详解 在移动应用开发领域,模仿知名社交网络平台如新浪微博的功能和用户体验是一种常见的学习和实践方式。本项目“仿新浪微博”旨在通过模仿微博的关键特性,帮助开发者深入理解...

Global site tag (gtag.js) - Google Analytics