前后花了近一个星期,中间就sha1的加密就纠结了几天。。。
还有一些莫名奇妙的问题,也都是自己的马虎,和对oauth认证的一知半解的原因。
?
废话不多说,代码如下
# coding: utf-8 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("今天很暖和!!!")} ## 发送一条微博信息 # 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接口 ## address 新浪api接口url,不含参数 ## data 一个hash对象,里面的值如果包含特殊字符如请使用CGI::escape方法进行编码。 def send_request_post(address,data={}) request_handle(address,data,"POST") end private ## 用户访问新浪api接口 ## address 新浪api接口url,不含参数 ## data 一个hash对象,里面的值如果包含特殊字符如请使用CGI::escape方法进行编码。 ## method 发出request请求的方式 def request_handle(address,data,method="GET") 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.0a" } base_string_params = params.merge(data) 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) #req.set_form_data(data) set_form_data(data,req,false) puts "===========post_body===========",req.body if @request_params[:debug] 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 ## 发出请求,只有获取用户验证的时候用到这个方法 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
?
暂时不能发送图片微博,这个功能以后在考虑。。。
?
同时发布到新浪微博论坛
http://forum.open.t.sina.com.cn/read.php?tid=558