Commit 7ebb18e5b7cfe97af48c03e943c5b2e86045d513

Authored by lanrion
1 parent 6cf4eedc
Exists in master

添加token 存储方案

README.md
1   -开发中...
2   -
3   -部门、成员、标签、自定义菜单、Oauth2 接口均可以在开发环境调试
4   -
5 1 **企业号对应多个管理组,请前往 `设置` => `权限管理` 任意创建一个管理组,在管理组最下角即可获取 CorpID Secret**
6 2  
7 3 **有问题请及时提issue**
... ... @@ -10,30 +6,30 @@
10 6 gem "qy_wechat_api", git: "https://github.com/lanrion/qy_wechat_api.git"
11 7 ```
12 8  
13   -**暂未对access_token做缓存处理,为了确保在开发过程不会出现token过期问题,请不要使用全局变量存储group_client。**
14   -
15   -# 配置
16   -此项配置,仅用于你使用redis存储token,否则不需要!
  9 +# token 存储方案
17 10  
18   -在:your_project_name/config/initializers/qy_wechat_api.rb 添加:
  11 +## 对象存储
  12 +如果你是单个企业号,建议使用这个方案,无需任何配置即可使用。
19 13  
  14 +## Redis 存储
20 15 ```ruby
21   -# don't forget change namespace
22   -namespace = "your_project_name:qy_wechat_api"
23   -redis = Redis.new(:host => "127.0.0.1", :port => "6379", :db => 15)
  16 +redis = Redis.new(host: "127.0.0.1", port: "6379")
  17 +
  18 +namespace = "qy_wechat_api:redis_storage"
24 19  
25 20 # cleanup keys in the current namespace when restart server everytime.
26 21 exist_keys = redis.keys("#{namespace}:*")
27 22 exist_keys.each{|key|redis.del(key)}
28 23  
29   -# Give a special namespace as prefix for Redis key, when your have more than one project used qy_wechat_api, this config will make them work fine.
30   -redis = Redis::Namespace.new("#{namespace}", :redis => redis)
  24 +redis_with_ns = Redis::Namespace.new("#{namespace}", redis: redis)
31 25  
32 26 QyWechatApi.configure do |config|
33   - config.redis = redis
  27 + config.redis = redis_with_ns
34 28 end
35 29 ```
36 30  
  31 +## 自定义存储方案
  32 +TODO...
37 33  
38 34 # API基本用法
39 35  
... ... @@ -43,6 +39,8 @@ end
43 39  
44 40 ```ruby
45 41 group_client = QyWechatApi::Client.new(corpid, corpsecret)
  42 +# 为了确保用户输入的corpid, corpsecret是准确的,请务必执行:
  43 +group_client.is_valid?
46 44 ```
47 45  
48 46 ## 部门
... ... @@ -120,4 +118,3 @@ group_client.media.upload(image_jpg_file, "image")
120 118 group_client.media.get_media_by_id(media_id)
121 119 ```
122 120  
123   -
... ...
lib/qy_wechat_api.rb
1 1 # encoding: utf-8
2 2  
3 3 require "rest-client"
4   -
5 4 require "carrierwave"
6   -require "qy_wechat_api/carrierwave/qy_wechat_api_uploader"
7   -
8 5 require 'yajl/json_gem'
9 6  
  7 +require "qy_wechat_api/carrierwave/qy_wechat_api_uploader"
  8 +require "qy_wechat_api/config"
10 9 require "qy_wechat_api/client"
11 10 require "qy_wechat_api/handler"
12 11 require "qy_wechat_api/api"
13 12  
14 13 module QyWechatApi
  14 +
  15 + # Storage
  16 + autoload(:Storage, "qy_wechat_api/storage/storage")
  17 + autoload(:ObjectStorage, "qy_wechat_api/storage/object_storage")
  18 + autoload(:RedisStorage, "qy_wechat_api/storage/redis_storage")
  19 +
15 20 ENDPOINT_URL = "https://qyapi.weixin.qq.com/cgi-bin"
16 21 OK_MSG = "ok".freeze
17 22 OK_CODE = 0.freeze
... ...
lib/qy_wechat_api/client.rb
... ... @@ -6,9 +6,10 @@ module QyWechatApi
6 6 attr_accessor :access_token, :redis_key, :storage
7 7  
8 8 def initialize(corp_id, group_secret, redis_key=nil)
9   - @corp_id = corp_id
  9 + @corp_id = corp_id
10 10 @group_secret = group_secret
11   - @redis_key = security_redis_key((redis_key || "qy_" + group_secret))
  11 + @redis_key = security_redis_key((redis_key || "qy_" + group_secret))
  12 + @storage = Storage.init_with(self)
12 13 end
13 14  
14 15 # return token
... ... @@ -54,15 +55,6 @@ module QyWechatApi
54 55 end
55 56  
56 57 private
57   - def get_access_token
58   - self.access_token ||= get_token.result["access_token"]
59   - end
60   -
61   - # 获取token
62   - def get_token
63   - params = {corpid: corp_id, corpsecret: group_secret}
64   - QyWechatApi.http_get_without_token("/gettoken", params)
65   - end
66 58  
67 59 def security_redis_key(key)
68 60 Digest::MD5.hexdigest(key.to_s).upcase
... ...
lib/qy_wechat_api/handler.rb
  1 +require "qy_wechat_api/handler/errors"
1 2 require "qy_wechat_api/handler/global_code"
2 3 require "qy_wechat_api/handler/result_handler"
... ...
lib/qy_wechat_api/handler/errors.rb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +module QyWechatApi
  2 + module Errors
  3 + class ValidAccessTokenException < RuntimeError;end
  4 + end
  5 +end
... ...
lib/qy_wechat_api/storage/object_storage.rb 0 → 100644
... ... @@ -0,0 +1,23 @@
  1 +# encoding: utf-8
  2 +module QyWechatApi
  3 + class ObjectStorage < Storage
  4 + def valid?
  5 + super
  6 + end
  7 +
  8 + def token_expired?
  9 + # 如果当前token过期时间小于现在的时间,则重新获取一次
  10 + client.expired_at <= Time.now.to_i
  11 + end
  12 +
  13 + def refresh_token
  14 + super
  15 + end
  16 +
  17 + def access_token
  18 + super
  19 + client.access_token
  20 + end
  21 + end
  22 +
  23 +end
... ...
lib/qy_wechat_api/storage/redis_storage.rb 0 → 100644
... ... @@ -0,0 +1,28 @@
  1 +# encoding: utf-8
  2 +module QyWechatApi
  3 + class RedisStorage < Storage
  4 + def valid?
  5 + weixin_redis.del(client.redis_key)
  6 + super
  7 + end
  8 +
  9 + def token_expired?
  10 + weixin_redis.hvals(client.redis_key).empty?
  11 + end
  12 +
  13 + def refresh_token
  14 + super
  15 + weixin_redis.hmset(client.redis_key, "access_token", client.access_token,
  16 + "expired_at", client.expired_at)
  17 + weixin_redis.expireat(client.redis_key, client.expired_at.to_i-10) # 提前10秒超时
  18 + end
  19 +
  20 + def access_token
  21 + super
  22 + client.access_token = weixin_redis.hget(client.redis_key, "access_token")
  23 + client.expired_at = weixin_redis.hget(client.redis_key, "expired_at")
  24 + client.access_token
  25 + end
  26 + end
  27 +
  28 +end
... ...
lib/qy_wechat_api/storage/storage.rb
... ... @@ -3,5 +3,73 @@
3 3 module QyWechatApi
4 4 class Storage
5 5  
  6 + attr_accessor :client
  7 +
  8 + def initialize(client)
  9 + @client = client
  10 + end
  11 +
  12 + def self.init_with(client)
  13 + if QyWechatApi.weixin_redis.nil?
  14 + ObjectStorage.new(client)
  15 + else
  16 + RedisStorage.new(client)
  17 + end
  18 + end
  19 +
  20 + def valid?
  21 + authenticate["valid"]
  22 + end
  23 +
  24 + def authenticate
  25 + auth_result = http_get_access_token
  26 + auth = false
  27 + if auth_result.is_ok?
  28 + set_access_token_for_client(auth_result.result)
  29 + auth = true
  30 + end
  31 + {"valid" => auth, "handler" => auth_result}
  32 + end
  33 +
  34 + def refresh_token
  35 + handle_valid_exception
  36 + set_access_token_for_client
  37 + end
  38 +
  39 + def access_token
  40 + refresh_token if token_expired?
  41 + end
  42 +
  43 + def token_expired?
  44 + raise NotImplementedError, "Subclasses must implement a token_expired? method"
  45 + end
  46 +
  47 + def set_access_token_for_client(access_token_infos=nil)
  48 + token_infos = access_token_infos || http_get_access_token.result
  49 + client.access_token = token_infos["access_token"]
  50 + client.expired_at = Time.now.to_i + token_infos["expires_in"].to_i
  51 + end
  52 +
  53 + def http_get_access_token
  54 + QyWechatApi.http_get_without_token("/gettoken", authenticate_headers)
  55 + end
  56 +
  57 + def authenticate_headers
  58 + {corpid: client.corp_id, corpsecret: client.group_secret}
  59 + end
  60 +
  61 + private
  62 +
  63 + def handle_valid_exception
  64 + auth_result = authenticate
  65 + if !auth_result["valid"]
  66 + result_handler = auth_result["handler"]
  67 + raise Errors::ValidAccessTokenException, result_handler.full_error_message
  68 + end
  69 + end
  70 +
  71 + def weixin_redis
  72 + QyWechatApi.weixin_redis
  73 + end
6 74 end
7 75 end
... ...
spec/spec_helper.rb
... ... @@ -14,13 +14,30 @@
14 14 # users commonly want.
15 15 #
16 16 # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
  17 +require "redis-namespace"
  18 +require "redis"
  19 +
17 20 require "qy_wechat_api"
18 21 require "pry-rails"
19   -
20 22 corpid = "wxb9ce1d023fe6eb69"
21 23 corpsecret = "UOofFIah4PVLmkG8xMH3lpDxj6NTnQSKMrFt-HubiPB4kjB09EmTVcUjgNeermps"
22 24  
23 25  
  26 +# Comment to test for ClientStorage
  27 +redis = Redis.new(:host => "127.0.0.1",:port => "6379")
  28 +
  29 +namespace = "qy_wechat_api:redis_storage"
  30 +
  31 +# cleanup keys in the current namespace when restart server everytime.
  32 +exist_keys = redis.keys("#{namespace}:*")
  33 +exist_keys.each{|key|redis.del(key)}
  34 +
  35 +redis_with_ns = Redis::Namespace.new("#{namespace}", :redis => redis)
  36 +
  37 +QyWechatApi.configure do |config|
  38 + config.redis = redis_with_ns
  39 +end
  40 +
24 41 $client = QyWechatApi::Client.new(corpid, corpsecret)
25 42  
26 43 RSpec.configure do |config|
... ...