Sorcery + Grape on Rails
最近手头有一个需要快速开发的新Web项目, 于是想了想还是不用sinatra, 直接用RoR来写. 因为需要为手机提供API, 感觉在Rails上面架设sinatra有点自欺欺人的感觉, 于是第一次尝试在Rails上面加一层Grape... 于是问题就来了.
Sorcery在Session/Cookies方面的操作完全依赖Rails的API, 以前用sinatra的时候, 基本上没有感觉到什么不适, 这次在Grape上使用Sorcery就爆炸了OTL. Grape::Endpoint.send(:include, Sorcery::Controller)
之后就开始各种undefined. 看了看大概都集中在ActionDispatch和ActionController里. 下午大概的做了个hotfix, 可惜因为Grape自用的Cookies和ActionDispatch::Cookies区别实在太大, Grape的remember_me功能在grape上暂时用不了.
module API
module SorceryAdapter
AUTHENTICITY_TOKEN_LENGTH = 32
def self.included(mod)
mod.instance_eval {
helpers do
### Adapt for Sorcery (some directly taken from rails codes)
# Get session
def session
env[Rack::Session::Abstract::ENV_SESSION_KEY]
end
# Disable remember_me because of the cookies type conflict
def current_user
unless defined?(@current_user)
@current_user = login_from_session || nil
end
@current_user
end
## ActionDispatch::Request
def reset_session
if session && session.respond_to?(:destroy)
session.destroy
else
self.session = {}
end
@env['action_dispatch.request.flash_hash'] = nil
end
## ActionController::RequestForgeryProtection::ProtectionMethods::NullSession
# Sets the token value for the current session.
def form_authenticity_token
masked_authenticity_token(session)
end
def real_csrf_token(session)
session[:_csrf_token] ||= SecureRandom.base64(AUTHENTICITY_TOKEN_LENGTH)
Base64.strict_decode64(session[:_csrf_token])
end
# Creates a masked version of the authenticity token that varies
# on each request. The masking is used to mitigate SSL attacks
# like BREACH.
def masked_authenticity_token(session)
one_time_pad = SecureRandom.random_bytes(AUTHENTICITY_TOKEN_LENGTH)
encrypted_csrf_token = xor_byte_strings(one_time_pad, real_csrf_token(session))
masked_token = one_time_pad + encrypted_csrf_token
Base64.strict_encode64(masked_token)
end
def xor_byte_strings(s1, s2)
s1.bytes.zip(s2.bytes).map { |(c1,c2)| c1 ^ c2 }.pack('c*')
end
end
}
end
end
end