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

新浪微博oauth简易客户端ruby实现

阅读更多

前后花了近一个星期,中间就sha1的加密就纠结了几天。。。

还有一些莫名奇妙的问题,也都是自己的马虎,和对oauth认证的一知半解的原因。

 

废话不多说,代码如下

require "cgi"
require "uri"
require "net/http"
require "openssl"
require "base64"
module Oauth
  #注:暂不支持发布图片微博功能。
  #
  #使用方法介绍
  #首先获取用户的授权,如果已获取授权则直接看第三步
  #1.首先取得未授权token,使用get_request_token方法,然后跳转到新浪用户授权页面
  #  def request_token
  #    client = Oauth::Client.new({
  #        :app_key => "3224168886",
  #        :app_secret => "3739fb8b93b6c1737bd3052460a125e3"
  #    })
  #
  #    token = client.get_request_token
  #    session[:oauth_token_secret] = token[1]
  #    # puts "=======================oauth_token_secret",session[:oauth_token_secret]
  #    callback_url = "http://localhost:5000/oauth_test/access_token"
  #
  #    redirect_to client.authorize token[0],callback_url
  #  end
  #
  #2.用户授权以后,通过callback_url返回自己的接收页面,接收已授权的token和oauth_verifier。使用已授权的token和其他参数获取用户的access_token和token_secret。
  #这两个参数可以保存到数据库,以后就可以直接使用而不用再次走1和2的授权流程了,access_token和token_secret的有效期为直到用户登陆微博在设置里面取消授权为止
  #    client = Oauth::Client.new({
  #        :app_key => "3224168886",
  #        :app_secret => "3739fb8b93b6c1737bd3052460a125e3"
  #    })
  #    render :text => client.get_access_token(params[:oauth_token], session[:oauth_token_secret],params[:oauth_verifier]).inspect
  #
  #3.使用授权过的token访问新浪的api接口,例子如下
  #
  #    client = Oauth::Client.new({
  #        :app_key => "3224168886",
  #        :app_secret => "3739fb8b93b6c1737bd3052460a125e3",
  #        :oauth_token => "you's oauth_token  ",
  #        :oauth_token_secret => "you's oauth_token_secret",
  #        :debug => true
  #    })
  #
  #    client.send_request_get "http://api.t.sina.com.cn/direct_messages.json"  ## 获取当前用户最新私信列表
  #    client.send_request_post "http://api.t.sina.com.cn/statuses/update.json",{:status=>CGI::escape("今天很暖和!!!")} ## 发送一条微博信息
  #    client.send_upload_request_post "http://api.t.sina.com.cn/statuses/upload.json",{:status=>CGI::escape("vim功能大全")},{:pic=>File.new("d:\\vi.jpg","rb")}
  #    client.send_upload_request_post "http://api.t.sina.com.cn/account/update_profile_image.json",{},{:image=>File.new("d:\\013.jpg","rb")}
  #    注:File.new("d:\\vi.jpg","rb")的打开方式mode一定要是rb即二进制方式打开,否则无法读取完整的图片信息
  #
  class Client
    attr_accessor :request_params
    
    ## :app_key 新浪的app_key
    ## :app_secret 新浪的app_secret
    ## :oauth_token 用户授权的access_token
    ## :oauth_token_secret 用户授权的oauth_token_secret
    ## :debug 是否输出具体的oauth参数,供查看调试
    def initialize(params)
      @request_params = {
        :request_token_url => "http://api.t.sina.com.cn/oauth/request_token",
        :access_token_url => "http://api.t.sina.com.cn/oauth/access_token",
        :authorize_url => "http://api.t.sina.com.cn/oauth/authorize",
        :oauth_consumer_key => params[:app_key],
        :oauth_consumer_secret => params[:app_secret],
        :oauth_token => params[:oauth_token],
        :oauth_token_secret => params[:oauth_token_secret],
        :debug => params[:debug]
      }
    end
    
    ## 此方法生成要跳出的页面url
    ## token 未授权的request_token
    ## callback_url 接受已授权token的url
    def authorize(token,callback_url)
      "#{@request_params[:authorize_url]}?oauth_token=#{token}&oauth_callback=#{CGI::escape(callback_url)}"
    end
    
    ## 获取未授权的token
    ## 返回参数为 [oauth_token,oauth_token_secret]
    ## 建议把oauth_token_secret存储到session或cookie,后面获取access_token的时候会使用
    ## 如果验证失败,则抛出异常。异常信息为新浪返回的错误字符串
    def get_request_token
      
      params = {
        :oauth_consumer_key => "#{@request_params[:oauth_consumer_key]}",
        :oauth_timestamp => Time.new.to_i.to_s,
        :oauth_nonce => (Time.new.to_i + 100).to_s,
        :oauth_version => "1.0a",
        :oauth_signature_method => "HMAC-SHA1"
      }
      
      oauth_signature = digest base_string("GET",@request_params[:request_token_url],params)
      
      params_string = "#{query_string(params)}&oauth_signature=#{CGI::escape(oauth_signature)}"
      
      request_result = get_request_result(@request_params[:request_token_url],params_string)
      raise request_result if request_result.split("&").size != 2
      return [request_result.split("&")[0].split("=")[1],request_result.split("&")[1].split("=")[1]]
    end
    
    ## oauth_token 用后授权后返回的oauth_token
    ## oauth_token_secret 用后授权后返回的oauth_token_secret
    ## oauth_verifier 第一步中返回的oauth_verifier
    ## 返回为参数为[oauth_token,oauth_token_secret,user_id]
    ## 如果验证失败,则抛出异常。异常信息为新浪返回的错误字符串
    def get_access_token(oauth_token,oauth_token_secret,oauth_verifier)
      httpmethod = "GET"
      params = {
        :oauth_consumer_key => "#{@request_params[:oauth_consumer_key]}",
        :oauth_token => oauth_token,
        :oauth_timestamp => Time.new.to_i.to_s,
        :oauth_nonce => (Time.new.to_i + 100).to_s,
        :oauth_version => "1.0a",
        :oauth_signature_method => "HMAC-SHA1",
        :oauth_verifier => oauth_verifier
      }
      
      oauth_signature = digest base_string(httpmethod,@request_params[:access_token_url],params),@request_params[:oauth_consumer_secret]+"&"+oauth_token_secret
      params_string = "#{query_string(params)}&oauth_signature=#{CGI::escape(oauth_signature)}"
      request_result = get_request_result(@request_params[:access_token_url],params_string)
      result_array = request_result.split("&")
      raise request_result if result_array.size != 3
      return [result_array[0].split("=")[1],result_array[1].split("=")[1],result_array[2].split("=")[1]]
    end
    
    ## 发送GET请求,访问新浪api接口
    ## address 新浪api接口url,不含参数
    def send_request_get(address,data={})
      request_handle(address,data,"GET")
    end
    
    ## 发送POST请求,访问新浪api接口,不能上传文件,如果要上传文件请用send_upload_request_post方法
    ## address 新浪api接口url,不含参数
    ## data 一个hash对象,里面的值如果包含特殊字符如请使用CGI::escape方法进行编码。
    def send_request_post(address,data={})
      request_handle(address,data,"POST")
    end
    
    ## 发送POST请求,访问新浪api接口
    ## address 新浪api接口url,不含参数
    ## data 一个hash对象,里面的值如果包含特殊字符如请使用CGI::escape方法进行编码。
    ## upload_file 一个hash对象,里面的key对应的值为File 对象,即要上传的文件对象
    ## upload_file不能为空,否则会抛出upload_file is empty 异常
    def send_upload_request_post(address,data={},upload_file={})
      raise "upload_file is empty" if upload_file.nil? || upload_file.size == 0
      request_handle(address,data,"POST",upload_file)
    end
    
    private
    
    ## 用户访问新浪api接口
    ## address 新浪api接口url,不含参数
    ## data 一个hash对象,里面的值如果包含特殊字符如请使用CGI::escape方法进行编码。
    ## method 发出request请求的方式
    def request_handle(address,data,method="GET",upload_files={})
      
      params = {
        :oauth_consumer_key => "#{@request_params[:oauth_consumer_key]}",
        :oauth_nonce => (Time.new.to_i + 100).to_s,
        :oauth_signature_method => "HMAC-SHA1",
        :oauth_token => "#{@request_params[:oauth_token]}",
        :oauth_timestamp => Time.new.to_i.to_s,
        :oauth_version => "1.0"
      }
      
			if upload_files.size > 0
				escape_data = {}
				data.each do |k,v|
					escape_data[k.to_s] = CGI::escape(data[k])
				end
				base_string_params = params.merge(escape_data)   # data from send_upload_request_post
			else
				base_string_params = params.merge(data)   # data from send_request_post
			end
      
      
      oauth_signature = digest base_string(method,address,base_string_params),@request_params[:oauth_consumer_secret]+"&"+@request_params[:oauth_token_secret]
      
      params[:oauth_signature] = oauth_signature
      
      #生成http header 字符串
      header = {
        "Authorization" => "OAuth #{header_string(params)}"
      }
      
      uri = URI.parse(address)
      case method.to_s.upcase
      when "GET"
        req = Net::HTTP::Get.new(uri.path)
        req = Net::HTTP::Get.new(uri.path + "?#{query_string(data)}") if data.size != 0
      when "POST"
        req = Net::HTTP::Post.new(uri.path)
        set_form_data(data,req,false) if upload_files.size == 0
        set_upload_form_data(data, req, false, upload_files) if upload_files.size == 0
        
        puts "===========post_body===========",req.body  if @request_params[:debug] && upload_files.size == 0
      else
        raise "no support request method!"
      end
      
      req["Authorization"] = header["Authorization"]  #设置http header
      
      res = Net::HTTP.start(uri.host,uri.port)do |http|
        http.request(req)
      end
      puts "===========request_reslult_body===========",res.body  if @request_params[:debug]
      return res.body if res.code == "200"   #如果返回状态码不为200,则表示访问失败。抛出返回失败的字符串
      raise res.body
    end
    
    ## 设置POST 方法的数据
    ## params post的参数, 一个hash对象
    ## request 要发出的Net::HTTP::Post对象
    ## escape 是否对post的内容进行再转义
    def set_form_data(params,request,escape=true)
      params_string = ""
      params.each do |k,v|
        params_string << "#{k.to_s}=#{CGI::escape(v.to_s)}&" if escape
        params_string << "#{k.to_s}=#{v.to_s}&" unless escape
      end
      params_string.chomp!("&")
      request["content_type"] = "application/x-www-form-urlencoded"
      request.body = params_string
    end
    
    ## 设置上传文件的post body
    ## params 普通参数
    ## request httppost的request对象
    ## escape 是否对post的内容进行再转义
    ## upload_files 上传的文件
    def set_upload_form_data(params,request,escape=true,upload_files={})
      boundary = "----rubyoauth"
      params_string = ""
      params.each do |k,v|
        params_string << "--#{boundary}\r\n"
        params_string << "Content-Disposition: form-data; name=\"#{k.to_s}\"\r\n\r\n"
        params_string << "#{CGI::escape(v)}\r\n" if escape
        params_string << "#{v}\r\n" unless escape
      end
      
      upload_files.each do |k,v|
        params_string << "--#{boundary}\r\n"
        params_string << "Content-Disposition: form-data; name=\"#{k.to_s}\"; filename=\"#{File.basename(v.path)}\"\r\n"
        params_string << "Content-Type: #{get_file_content_type v}\r\n\r\n"
        params_string << "#{v.read}\r\n"
      end
      params_string << "--#{boundary}--\r\n"
      request["content-type"] = "multipart/form-data; boundary=#{ boundary }"
      request.body = params_string
    end
    
    ## 通过文件名后缀判断文件的类型,jpg为image/jpeg,png为image/x-png,gif为image/gif,其他的一律为application/octet-stream
    def get_file_content_type(file)
      case File.extname(file.path).downcase
      when ".jpg"
        "image/jpeg"
      when ".png"
        "image/x-png"
      when ".gif"
        "image/gif"
      else
        "application/octet-stream"
      end
    end
    
    ## 发出请求,只有获取用户验证的时候用到这个方法
    def get_request_result(request_token_url,params_string)
      url = URI.parse(request_token_url)
      res = Net::HTTP.start(url.host, url.port) {|http|
        http.get(url.path+"?#{params_string}")
      }
      puts "===========request_reslult_body===========",res.body  if @request_params[:debug]
      return res.body if res.code == "200"
      raise res.body
    end
    
    ## 使用HMAC-SHA1进行加密
    ## see http://stackoverflow.com/questions/1959486/digest-hmac-is-part-of-ruby-standard-lib
    def digest(value,key="#{@request_params[:oauth_consumer_secret]}&")
      puts "===========digest_key===========",key  if @request_params[:debug]
      signature = Base64.encode64 OpenSSL::HMAC.digest("SHA1", key, value)
      puts "===========signature===========",signature.strip  if @request_params[:debug]
      signature.strip
    end
    
    ## 生成http header字符串
    def header_string(header_params)
      result_string = ""
      header_params.to_a.sort{|a,b| a[0].to_s <=> b[0].to_s}.each do|param|
        result_string << "#{param[0]}=" << '"' << "#{CGI::escape(param[1])}"<< '",'
      end
      puts "===========header_string===========",result_string.chomp(",")  if @request_params[:debug]
      result_string.chomp(",")
    end
    
    ## 生成query_string 字符串
    def query_string(query_params)
      result_string = ""
      query_params.to_a.sort{|a,b| a[0].to_s <=> b[0].to_s}.each do|param|
        result_string << "#{param[0]}=#{CGI::escape(param[1])}&"
      end
      puts "===========query_string===========",result_string.chomp("&")  if @request_params[:debug]
      result_string.chomp("&")
    end
    
    ## 生成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  if @request_params[:debug]
      base_str
    end
    
  end
end
 

在做图片上传的时候遇到图片只能读取一部分的问题,后来发现是打开方式不对,

把File.new("d:\\vi.jpg") 改为File.new("d:\\vi.jpg","rb") 就可以读取完整的图片数据了。。。 这个问题纠结了几个小时,郁闷。

 

同时发布到新浪微博论坛

http://forum.open.t.sina.com.cn/read.php?tid=558

分享到:
评论
4 楼 酷的飞上天空 2011-08-12  
leiyulyl 写道
我client.send_upload_request_post "http://api.t.sina.com.cn/statuses/upload.json",{:status=>CGI::escape("vim功能大全")},{:pic=>File.new("d:\\vi.jpg","rb")}  这样发微博怎么发不出啊 ...也没报错


你应该是用的早期验证获得的oauth_token和oauth_token_secret  重新验证下获取新的再测试下应该就没问题了。

3 楼 leiyulyl 2011-07-26  
我client.send_upload_request_post "http://api.t.sina.com.cn/statuses/upload.json",{:status=>CGI::escape("vim功能大全")},{:pic=>File.new("d:\\vi.jpg","rb")}  这样发微博怎么发不出啊 ...也没报错
2 楼 404714 2011-07-19  
似乎不行, 我错了。
1 楼 404714 2011-07-19  
base_string方法写的太纠结
base_str = "POST&" + CGI::escape("http://api.t.sina.com.cn/oauth/access_token") + "&" + request_params.map{|k,v| "#{k}%3D#{URI.escape(v.to_s, /[^a-zA-Z0-9\-\.\_\~]/)}"}.sort * "%26"

或者
base_str = "POST&" + CGI::escape("http://api.t.sina.com.cn/oauth/access_token") + "&" + request_params.map{|k,v| "#{k}%3D#{CGI::escape(v.to_s)}"}.sort * "%26"

相关推荐

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

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

    新浪微博OAuth 验证

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

    新浪微博OAuth2.0登录

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

    新浪和腾讯微博Oauth认证Demo(php版)

    3./class/下,文件名中含有If的文件,定义的是接口.总计3个接口文件:各个网站的api访问路径...5.实例中2个网站(新浪微博和腾讯微博)的OAuth已经实现,并且通过验证。本例中的OAuth客户端来源于新浪微博OAuth认证SDK。

    新浪微博OAuth2.0 SDK+ Demo C#

    本文将深入探讨如何使用C#语言结合新浪微博OAuth2.0 SDK,实现对微博数据的获取和操作,以及通过提供的Demo来理解和实践这个过程。 首先,OAuth2.0是一种授权框架,它允许第三方应用在用户授权的情况下访问其存储在...

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

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

    新浪微博oauth2 php sdk 下载

    《使用新浪微博OAuth2 PHP SDK实现认证与数据交互》 在当今的互联网开发中,社交平台API的集成成为了不可或缺的一部分,而新浪微博作为中国重要的社交媒体之一,提供了丰富的API接口供开发者使用。其中,OAuth2协议...

    新浪微博Oauth2.0源码

    新浪微博oauth认证源码,只要加入开发者自己的key与secret即可通过认证,进行相应的开发

    新浪微博 oauth2 模拟登录

    在本文中,我们将深入探讨如何使用OAuth2协议模拟登录新浪微博,并了解相关技术细节。OAuth2是一种授权框架,允许第三方应用代表用户与服务提供商进行交互,例如发布微博。在这个例子中,我们将使用Java的Apache ...

    新浪微博OAuth2.0API源码

    新浪微博OAuth2.0API源码 一个更方便,更快速的C#SDK,希望能帮助更多的朋友学习和使用 新浪微博OAuth2.0API使用流程: 1、根据需要求修改配置文件(Wbm.SinaV2.config)。 2、注册ApplicationKey。(参考...

    新浪微博OAuth2认证android

    本文将深入探讨“新浪微博OAuth2认证在Android客户端中的实现”这一主题,旨在帮助开发者理解和实施这一过程。 OAuth2的核心目的是为第三方应用提供安全、有限的访问用户资源的权限,而无需获取用户的账号密码。在...

    新浪微博Windows Phone 7客户端

    对于“新浪微博”客户端,开发者需要实现以下功能模块: 1. **登录注册**:用户需要能够安全地登录自己的微博账号,这通常涉及OAuth认证,通过API与微博服务器进行交互,获取访问令牌。 2. **消息显示**:展示微博...

    新浪微博源码PC客户端

    《深入解析:新浪微博源码PC客户端》 新浪微博,作为中国最具影响力的社交媒体平台之一,其PC客户端的源码对于开发者来说具有极高的学习价值。这份源码不仅揭示了客户端软件的设计理念,还展示了复杂网络应用的架构...

    新浪微博OAuth授权

    ### 新浪微博OAuth授权 #### OAuth协议简介 OAuth是一种开放标准协议,用于授权应用程序访问受保护资源(如用户的个人信息)而无需用户提供凭据(如用户名和密码)。它为客户端应用程序提供了一种安全的方法来访问...

    IOS新浪微博Oauth2.0授权demo

    在iOS平台上实现新浪微博的OAuth2.0授权是一个常见的需求,特别是在开发社交应用或者需要与微博进行数据交互的应用中。OAuth2.0是一种授权框架,它允许第三方应用在用户许可的情况下,安全地访问用户在特定服务(如...

    android新浪微博OAuth2认证demo

    在Android平台上实现微博服务,尤其是新浪微薄,通常需要通过OAuth2.0授权协议来确保安全的用户身份验证。OAuth2.0是一种广泛使用的开放标准,允许第三方应用以用户的名义安全地访问其存储在另一应用上的资源,而...

    OAuth2.0新浪微博简单示例

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

    新浪微博OAuth2.0分享

    【标题】:“新浪微博OAuth2.0分享” 在互联网社交领域,新浪微博是中国极具影响力的社交媒体平台之一。为了方便开发者集成微博功能到自己的应用中,新浪提供了OAuth2.0授权协议的SDK,即“weiboSDK2.1_130806.jar...

    使用WebView实现新浪微博Oauth2.0认证(android)

    本文将详细介绍如何使用WebView组件实现新浪微博的OAuth2.0认证过程,这是一个安全、便捷的方法,避免了直接操作HTML代码获取验证码带来的潜在风险。 OAuth2.0是一种授权框架,它允许第三方应用在用户的授权下访问...

    ios5新浪微博Oauth的使用

    总的来说,iOS 5.0以上版本中集成新浪微博OAuth涉及到的步骤包括注册应用、配置URL Schemes、发起OAuth授权、处理授权回调、发布微博以及监听登录状态。理解并熟练掌握这些步骤,将有助于你成功地在应用中集成新浪...

Global site tag (gtag.js) - Google Analytics