以线程安全的方式刷新OAuth客户端令牌

时间:2015-06-22 02:04:03

标签: ruby-on-rails concurrency thread-safety

我的应用正在使用OAuth资源,并且有时必须使用其刷新令牌刷新访问令牌。为此,我正在做类似的事情:

record = MyClientModel.find(...)
client = OAuthClient.new(record.access_token)

begin
  tries ||= 2
  client.do_something
rescue ExpiredOAuthToken => e
  new_access_token, new_refresh_token = 
    client.refresh_token(record.refresh_token)
  client.access_token = new_access_token
  record.access_token = new_access_token
  record.refresh_token = new_refresh_token
  record.save
  retry unless (tries -= 1).zero?
  raise e
end

此代码旨在在Web请求和工作进程中同时运行,但显然,它不是线程安全的,例如:

  1. record.access_token到期
  2. 主题A遇到ExpiredOAuthToken
  3. 主题A调用#refresh_token
  4. 主题B遇到ExpiredOAuthToken
  5. 主题B调用#refresh_token但由于record.refresh_token现在无效
  6. 而失败
  7. 主题A持续存在新令牌
  8. 主题A继续
  9. 我之前从未真正考虑过线程安全问题,因此我正在寻找有关如何改进此代码的建议。

1 个答案:

答案 0 :(得分:0)

我最近遇到了这个问题-就我而言,我将访问令牌存储在ActiveRecord模型中,因此可以使用ActiveRecord fi<-list.files("C:/Users/Desktop/DL/Test",full.names=T) dat<-lapply(fi, read.csv, row.names = NULL) lapply(seq_along(dat), function(i) { write.csv(dat[i], paste0('address/to/file', i,'.csv')) }) 方法(与MySQL / PostgreSQL一起使用)。

with_lock

当我的线程遇到过期的令牌时,它们都将调用# ...inside Authorization model instance methods def refresh # do not allow more than one refresh to occur simultaneously with_lock do # if the token was just refreshed, return (there may be threads waiting on this token) return oauth_token if updated_at > 5.seconds.ago oauth = OmniAuth::Strategies::Custom.new(nil) token = OAuth2::AccessToken.new( oauth.client, oauth_token, refresh_token: oauth_refresh_token ) new_token = token.refresh! return unless new_token update!( oauth_token: new_token.token, oauth_refresh_token: new_token.refresh_token, oauth_expires_at: Time.at(new_token.expires_at) ) && new_token.token end end ,但是仅执行第一个refresh,其他线程等待,并在更新时立即获得新令牌。