6.3 KiB
RuCaptcha
This is a Captcha gem for Rails Applications. It drawing captcha image with C code.
Example
Feature
- No dependencies. No ImageMagick, No RMagick.
- For Rails Application;
- Simple, Easy to use;
- High performance.
Usage
Put rucaptcha in your Gemfile
:
gem 'rucaptcha'
Create config/initializers/rucaptcha.rb
RuCaptcha 没有使用 Rails Session 来存储验证码信息,因为 Rails 的默认 Session 是存储在 Cookie 里面,如果验证码存在里面会存在 Replay attack 漏洞,导致验证码关卡被攻破。
所以我在设计上要求 RuCaptcha 得配置一个可以支持分布式的后端存储方案例如:Memcached 或 Redis 以及其他可以支持分布式的 cache_store 方案。
同时,为了保障易用性,默认会尝试使用 :file_store
的方式,将验证码存在应用程序的 tmp/cache/rucaptcha/session
目录(但请注意,多机器部署这样是无法正常运作的)。
所以,我建议大家使用的时候,配置上 cache_store
(详见 Rails Guides 缓存配置部分的文档)到一个 Memcached 或 Redis,这才是最佳实践。
RuCaptcha.configure do
# Color style, default: :colorful, allows: [:colorful, :black_white]
# self.style = :colorful
# Custom captcha code expire time if you need, default: 2 minutes
# self.expires_in = 120
# [Requirement / 重要]
# Store Captcha code where, this config more like Rails config.cache_store
# default: Read config info from `Rails.application.config.cache_store`
# But RuCaptcha requirements cache_store not in [:null_store, :memory_store, :file_store]
# 默认:会从 Rails 配置的 cache_store 里面读取相同的配置信息,并尝试用可以运行的方式,用于存储验证码字符
# 但如果是 [:null_store, :memory_store, :file_store] 之类的,你可以通过下面的配置项单独给 RuCaptcha 配置 cache_store
self.cache_store = :mem_cache_store
end
Controller app/controller/account_controller.rb
When you called verify_rucaptcha?
, it will uses value from params[:_rucaptcha]
to validation.
class AccountController < ApplicationController
def create
@user = User.new(params[:user])
if verify_rucaptcha?(@user) && @user.save
redirect_to root_path, notice: 'Sign up successed.'
else
render 'account/new'
end
end
end
class ForgotPasswordController < ApplicationController
def create
# without any args
if verify_rucaptcha?
to_send_email
else
redirect_to '/forgot-password', alert: 'Invalid captcha code.'
end
end
end
TIP: Sometime you may need keep last verified captcha code in session on
verify_rucaptcha?
method call, you can usekeep_session: true
. For example:verify_rucaptcha? @user, keep_session: true
.
View app/views/account/new.html.erb
<form method="POST">
...
<div class="form-group">
<%= rucaptcha_input_tag(class: 'form-control', placeholder: 'Input Captcha') %>
<%= rucaptcha_image_tag(alt: 'Captcha') %>
</div>
...
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
And if you are use Devise, you can read this to add validation: RuCaptcha with Devise.
Write your test skip captcha validation
for RSpec
describe 'sign up and login', type: :feature do
before do
allow_any_instance_of(ActionController::Base).to receive(:verify_rucaptcha?).and_return(true)
end
it { ... }
end
for MiniTest
class ActionDispatch::IntegrationTest
def sign_in(user)
ActionController::Base.any_instance.stubs(:verify_rucaptcha?).returns(true)
post user_session_path \
'user[email]' => user.email,
'user[password]' => user.password
end
end
Invalid message without Devise
When you are using this gem without Devise, you may find out that the invalid message is missing. For this case, use the trick below to manually add your i18n invalid message.
if verify_rucaptcha?(@user) && @user.save
do_whatever_you_want
redirect_to someplace_you_want
else
# this is the trick
@user.errors.add(:base, t('rucaptcha.invalid'))
render :new
end