Commit bee472cf by lanrion

重构access_token store

添加jsticket store
parent 6d329c47
--format documentation --color spec --drb --format documentation --color spec --drb
--require spec_helper
require "rest-client" require "rest-client"
require "carrierwave" require "carrierwave"
require "weixin_authorize/carrierwave/weixin_uploader"
require 'yajl/json_gem' require 'yajl/json_gem'
require "weixin_authorize/carrierwave/weixin_uploader"
require "weixin_authorize/config" require "weixin_authorize/config"
require "weixin_authorize/handler" require "weixin_authorize/handler"
require "weixin_authorize/api" require "weixin_authorize/api"
...@@ -12,10 +10,18 @@ require "weixin_authorize/client" ...@@ -12,10 +10,18 @@ require "weixin_authorize/client"
module WeixinAuthorize module WeixinAuthorize
# Storage # token store
autoload(:Storage, "weixin_authorize/adapter/storage") module Token
autoload(:ClientStorage, "weixin_authorize/adapter/client_storage") autoload(:Store, "weixin_authorize/token/store")
autoload(:RedisStorage, "weixin_authorize/adapter/redis_storage") autoload(:ObjectStore, "weixin_authorize/token/object_store")
autoload(:RedisStore, "weixin_authorize/token/redis_store")
end
module JsTicket
autoload(:Store, "weixin_authorize/js_ticket/store")
autoload(:ObjectStore, "weixin_authorize/js_ticket/object_store")
autoload(:RedisStore, "weixin_authorize/js_ticket/redis_store")
end
OK_MSG = "ok" OK_MSG = "ok"
OK_CODE = 0 OK_CODE = 0
......
# encoding: utf-8
module WeixinAuthorize
module Api
module DataCube
end
end
end
...@@ -15,71 +15,68 @@ module WeixinAuthorize ...@@ -15,71 +15,68 @@ module WeixinAuthorize
attr_accessor :app_id, :app_secret, :expired_at # Time.now + expires_in attr_accessor :app_id, :app_secret, :expired_at # Time.now + expires_in
attr_accessor :access_token, :redis_key attr_accessor :access_token, :redis_key
attr_accessor :storage, :jsticket attr_accessor :jsticket, :jsticket_expired_at, :jsticket_redis_key
def initialize(app_id, app_secret, redis_key=nil) def initialize(app_id, app_secret, redis_key=nil)
@app_id = app_id @app_id = app_id
@app_secret = app_secret @app_secret = app_secret
@expired_at = Time.now.to_i @jsticket_expired_at = @expired_at = Time.now.to_i
@redis_key = security_redis_key((redis_key || "weixin_" + app_id)) @redis_key = security_redis_key(redis_key || "weixin_#{app_id}")
@storage = Storage.init_with(self) @jsticket_redis_key = security_redis_key("js_sdk_#{app_id}")
end end
# return token # return token
def get_access_token def get_access_token
@storage.access_token token_store.access_token
end end
# 检查appid和app_secret是否有效。 # 检查appid和app_secret是否有效。
def is_valid? def is_valid?
@storage.valid? token_store.valid?
end end
# TODO: refactor with difference storage def token_store
def js_ticket Token::Store.init_with(self)
raise ValidAccessTokenException if !is_valid? end
if @jsticket.nil? || @jsticket.result["expired_at"] <= Time.now.to_i
@jsticket ||= get_jsticket def jsticket_store
end JsTicket::Store.init_with(self)
@jsticket.result["ticket"] end
def get_jsticket
jsticket_store.jsticket
end end
# 获取js sdk 签名包 # 获取js sdk 签名包
def get_jssign_package(url) def get_jssign_package(url)
timestamp = Time.now.to_i timestamp = Time.now.to_i
noncestr = SecureRandom.hex(16) noncestr = SecureRandom.hex(16)
string = "jsapi_ticket=#{js_ticket}&noncestr=#{noncestr}&timestamp=#{timestamp}&url=#{url}"; str = "jsapi_ticket=#{get_jsticket}&noncestr=#{noncestr}&timestamp=#{timestamp}&url=#{url}";
signature = Digest::SHA1.hexdigest(string) signature = Digest::SHA1.hexdigest(str)
{ {
"appId" => app_id, "nonceStr" => noncestr, "appId" => app_id, "nonceStr" => noncestr,
"timestamp" => timestamp, "url" => url, "timestamp" => timestamp, "url" => url,
"signature" => signature, "rawString" => string "signature" => signature, "rawString" => str
} }
end end
private # 暴露出:http_get,http_post两个方法,方便第三方开发者扩展未开发的微信API。
def http_get(url, headers={}, endpoint="plain")
headers = headers.merge(access_token_param)
WeixinAuthorize.http_get_without_token(url, headers, endpoint)
end
def get_jsticket def http_post(url, payload={}, headers={}, endpoint="plain")
ticket = http_get("/ticket/getticket", {type: 1}) headers = access_token_param.merge(headers)
ticket.result["expired_at"] = ticket.result["expires_in"] + Time.now.to_i WeixinAuthorize.http_post_without_token(url, payload, headers, endpoint)
ticket end
end
private
def access_token_param def access_token_param
{access_token: get_access_token} {access_token: get_access_token}
end end
def http_get(url, headers={}, endpoint="plain")
puts "get jsticket" if url.include?("getticket")
headers = headers.merge(access_token_param)
WeixinAuthorize.http_get_without_token(url, headers, endpoint)
end
def http_post(url, payload={}, headers={}, endpoint="plain")
headers = access_token_param.merge(headers)
WeixinAuthorize.http_post_without_token(url, payload, headers, endpoint)
end
def security_redis_key(key) def security_redis_key(key)
Digest::MD5.hexdigest(key.to_s).upcase Digest::MD5.hexdigest(key.to_s).upcase
end end
......
module WeixinAuthorize
module JsTicket
class ObjectStore < Store
def jsticket_expired?
# 如果当前token过期时间小于现在的时间,则重新获取一次
client.jsticket_expired_at <= Time.now.to_i
end
def jsticket
super
client.jsticket
end
def refresh_jsticket
super
end
end
end
end
module WeixinAuthorize
module JsTicket
class RedisStore < Store
JSTICKET = "jsticket"
EXPIRED_AT = "expired_at"
def jsticket_expired?
weixin_redis.hvals(client.jsticket_redis_key).empty?
end
def refresh_jsticket
super
weixin_redis.hmset(
client.jsticket_redis_key,
JSTICKET,
client.jsticket,
EXPIRED_AT,
client.jsticket_expired_at
)
weixin_redis.expireat(
client.jsticket_redis_key,
client.jsticket_expired_at.to_i
)
end
def jsticket
super
client.jsticket = weixin_redis.hget(client.jsticket_redis_key, JSTICKET)
client.jsticket_expired_at = weixin_redis.hget(
client.jsticket_redis_key,
EXPIRED_AT
)
client.jsticket
end
def weixin_redis
WeixinAuthorize.weixin_redis
end
end
end
end
# encoding: utf-8
module WeixinAuthorize
module JsTicket
class Store
attr_accessor :client
def initialize(client)
@client = client
end
def self.init_with(client)
if WeixinAuthorize.weixin_redis.nil?
ObjectStore.new(client)
else
RedisStore.new(client)
end
end
def jsticket_expired?
raise NotImplementedError, "Subclasses must implement a jsticket_expired? method"
end
def refresh_jsticket
set_jsticket
end
def jsticket
refresh_jsticket if jsticket_expired?
end
def set_jsticket
result = client.http_get("/ticket/getticket", {type: 1}).result
client.jsticket = result["ticket"]
client.jsticket_expired_at = result["expires_in"] + Time.now.to_i
end
end
end
end
# encoding: utf-8 # encoding: utf-8
module WeixinAuthorize module WeixinAuthorize
class ClientStorage < Storage module Token
class ObjectStore < Store
def valid? def valid?
super super
end end
def token_expired? def token_expired?
# 如果当前token过期时间小于现在的时间,则重新获取一次 # 如果当前token过期时间小于现在的时间,则重新获取一次
client.expired_at <= Time.now.to_i client.expired_at <= Time.now.to_i
end end
def refresh_token def refresh_token
super super
end end
def access_token def access_token
super super
client.access_token client.access_token
end
end end
end end
end end
# encoding: utf-8 # encoding: utf-8
module WeixinAuthorize module WeixinAuthorize
module Token
class RedisStore < Store
class RedisStorage < Storage def valid?
weixin_redis.del(client.redis_key)
super
end
def valid? def token_expired?
weixin_redis.del(client.redis_key) weixin_redis.hvals(client.redis_key).empty?
super end
end
def token_expired? def refresh_token
weixin_redis.hvals(client.redis_key).empty? super
end weixin_redis.hmset(client.redis_key, "access_token", client.access_token,
"expired_at", client.expired_at)
weixin_redis.expireat(client.redis_key, client.expired_at.to_i-10) # 提前10秒超时
end
def refresh_token def access_token
super super
weixin_redis.hmset(client.redis_key, "access_token", client.access_token, client.access_token = weixin_redis.hget(client.redis_key, "access_token")
"expired_at", client.expired_at) client.expired_at = weixin_redis.hget(client.redis_key, "expired_at")
weixin_redis.expireat(client.redis_key, client.expired_at.to_i-10) # 提前10秒超时 client.access_token
end end
def access_token def weixin_redis
super WeixinAuthorize.weixin_redis
client.access_token = weixin_redis.hget(client.redis_key, "access_token") end
client.expired_at = weixin_redis.hget(client.redis_key, "expired_at")
client.access_token
end end
def weixin_redis
WeixinAuthorize.weixin_redis
end
end end
end end
# encoding: utf-8 # encoding: utf-8
module WeixinAuthorize module WeixinAuthorize
module Token
class Store
class Storage attr_accessor :client
attr_accessor :client def initialize(client)
@client = client
def initialize(client) end
@client = client
end
def self.init_with(client) def self.init_with(client)
if WeixinAuthorize.weixin_redis.nil? if WeixinAuthorize.weixin_redis.nil?
ClientStorage.new(client) ObjectStore.new(client)
else else
RedisStorage.new(client) RedisStore.new(client)
end
end end
end
def valid? def valid?
authenticate["valid"] authenticate["valid"]
end end
def authenticate def authenticate
auth_result = http_get_access_token auth_result = http_get_access_token
auth = false auth = false
if auth_result.is_ok? if auth_result.is_ok?
set_access_token_for_client(auth_result.result) set_access_token(auth_result.result)
auth = true auth = true
end
{"valid" => auth, "handler" => auth_result}
end end
{"valid" => auth, "handler" => auth_result}
end
def refresh_token def refresh_token
handle_valid_exception handle_valid_exception
set_access_token_for_client set_access_token
end end
def access_token def access_token
refresh_token if token_expired? refresh_token if token_expired?
end end
def token_expired? def token_expired?
raise NotImplementedError, "Subclasses must implement a token_expired? method" raise NotImplementedError, "Subclasses must implement a token_expired? method"
end end
def set_access_token_for_client(access_token_infos=nil) def set_access_token(access_token_infos=nil)
token_infos = access_token_infos || http_get_access_token.result token_infos = access_token_infos || http_get_access_token.result
client.access_token = token_infos["access_token"] client.access_token = token_infos["access_token"]
client.expired_at = Time.now.to_i + token_infos["expires_in"].to_i client.expired_at = Time.now.to_i + token_infos["expires_in"].to_i
end end
def http_get_access_token def http_get_access_token
WeixinAuthorize.http_get_without_token("/token", authenticate_headers) WeixinAuthorize.http_get_without_token("/token", authenticate_headers)
end end
def authenticate_headers def authenticate_headers
{grant_type: GRANT_TYPE, appid: client.app_id, secret: client.app_secret} {grant_type: GRANT_TYPE, appid: client.app_id, secret: client.app_secret}
end end
private private
def handle_valid_exception def handle_valid_exception
auth_result = authenticate auth_result = authenticate
if !auth_result["valid"] if !auth_result["valid"]
result_handler = auth_result["handler"] result_handler = auth_result["handler"]
raise ValidAccessTokenException, result_handler.full_error_message raise ValidAccessTokenException, result_handler.full_error_message
end
end end
end end
end end
end end
describe WeixinAuthorize::Client do
describe "#get jsticket" do
it "return the same jsticket in the same thing twice" do
js_ticket_1 = $client.get_jsticket
sleep 5
js_ticket_2 = $client.get_jsticket
expect(js_ticket_1).to eq(js_ticket_2)
end
end
end
...@@ -14,7 +14,7 @@ describe WeixinAuthorize::Api::Qrcode do ...@@ -14,7 +14,7 @@ describe WeixinAuthorize::Api::Qrcode do
expect(response.code).to eq(WeixinAuthorize::OK_CODE) expect(response.code).to eq(WeixinAuthorize::OK_CODE)
expect(response.result.keys).to eq(["ticket"]) expect(response.result.keys).to eq(["ticket"])
end end
it "#return_qr_url" do it "#return_qr_url" do
response = $client.create_qr_limit_scene("1234") response = $client.create_qr_limit_scene("1234")
qr_url = $client.qr_code_url(response.result["ticket"]) qr_url = $client.qr_code_url(response.result["ticket"])
......
...@@ -43,7 +43,7 @@ ENV["APPSECRET"]="1a941cd88cb4579ba98ec06b6813af03" ...@@ -43,7 +43,7 @@ ENV["APPSECRET"]="1a941cd88cb4579ba98ec06b6813af03"
ENV["OPENID"]="o9k6BuB0kydAcPTc7sPxppB1GQqA" ENV["OPENID"]="o9k6BuB0kydAcPTc7sPxppB1GQqA"
# Comment to test for ClientStorage # Comment to test for ClientStorage
redis = Redis.new(:host => "127.0.0.1",:port => "6379") redis = Redis.new(host: "127.0.0.1", port: "6379", db: 15)
namespace = "weixin_test:weixin_authorize" namespace = "weixin_test:weixin_authorize"
...@@ -58,6 +58,7 @@ WeixinAuthorize.configure do |config| ...@@ -58,6 +58,7 @@ WeixinAuthorize.configure do |config|
end end
$client = WeixinAuthorize::Client.new(ENV["APPID"], ENV["APPSECRET"]) $client = WeixinAuthorize::Client.new(ENV["APPID"], ENV["APPSECRET"])
RSpec.configure do |config| RSpec.configure do |config|
# The settings below are suggested to provide a good initial experience # The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content. # with RSpec, but feel free to customize to your heart's content.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment