如何给网站登录和注册页面加一个验证码?

Published on:

本篇文章的大纲

  • 给本地加上验证码验证
  • 给heroku远程端加上验证码验证

说明:

  • $ xxxx指的是Iterm端输入命令
  • 以下指的是修改文档
    xxx
    

本地验证码登录

1.install gem

gem 'rucaptcha’
gem ‘dalli'

$ bundle install
$ rails s

2.touch config/initializers/rucaptcha.rb

  • 新建文件
    $ touch config/initializers/rucaptcha.rb

  • 修改文件

    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
    

3.edit config/routes.rb
修改routes.rb

Rails.application.routes.draw do
  ...
  + mount RuCaptcha::Engine => "/rucaptcha"
    + devise_for :users, controllers: {
    registrations: 'users/registrations',
    sessions: 'users/sessions',
    passwords: 'users/passwords'
  }
  ...
end

4.see the devise controller and views
$ rails g devise:controllers users
$ rails g devise:views

除此之外也可以用这个命令:$ rails g devise:views users
区别在于,前一个命令在view这个文件夹下建立的子文件夹的名字默认叫devise,而后一个建立的子文件夹名字叫users。
本文用的是前一个命令:$ rails g devise:views

5.add to registration

class RegistrationsController < Devise::RegistrationsController
  def create
    build_resource(sign_up_params)
    if verify_rucaptcha?(resource) && resource.save
      yield resource if block_given?
      if resource.persisted?
        if resource.active_for_authentication?
          set_flash_message! :notice, :signed_up
          sign_up(resource_name, resource)
          respond_with resource, location: after_sign_up_path_for(resource)
        else
          set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
          expire_data_after_sign_in!
          respond_with resource, location: after_inactive_sign_up_path_for(resource)
        end
      else
        clean_up_passwords resource
        set_minimum_password_length
        respond_with resource
      end
    else
      clean_up_passwords resource
      respond_with resource
    end
  end
end
<div class="form-group">
    <div class="cols-sm-10">
        <div class="input-group">
        <%= rucaptcha_image_tag(alt: 'Captcha') %>
            <%= rucaptcha_input_tag(class: 'form-control', placeholder: 'Input Captcha') %>
        </div>
    </div>
</div>

6.add to session

prepend_before_action :valify_captcha!, only: [:create]

  def valify_captcha!
    unless verify_rucaptcha?
      redirect_to new_user_session_path, alert: t('rucaptcha.invalid')
      return
    end
    true
  end
<div class="form-group">
    <div class="cols-sm-10">
        <div class="input-group">
        <%= rucaptcha_image_tag(alt: 'Captcha') %>
            <%= rucaptcha_input_tag(class: 'form-control', placeholder: 'Please input Captcha') %>
        </div>
    </div>
</div>

7.add to passwords

def create
    self.resource = resource_class.find_or_initialize_with_errors(Devise.reset_password_keys, resource_params, :not_found)
    if self.resource.persisted? && verify_rucaptcha?(resource)
      self.resource.send_reset_password_instructions
    end

    yield resource if block_given?

    if successfully_sent?(resource)
      respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name))
    else
      respond_with(resource)
    end
  end
<div class="form-group">
    <div class="cols-sm-10">
        <div class="input-group">
        <%= rucaptcha_image_tag(alt: 'Captcha') %>
            <%= rucaptcha_input_tag(class: 'form-control', placeholder: 'Please input Captcha') %>
        </div>
    </div>
</div>

这个时候,验证码能够看到了,但输入验证码登录始终提示captcha invalid,看rails s的log,出现这样的字样:

8.brew安装memcached
还需要做最后一个步骤:安装memcached

  • brew install memcached
  • brew services start memcached

做完这一步,重启一下rails s就可以成功注册和登录了。

相关资料参考:

  1. GitHub - huacnlee/rucaptcha: This is a Captcha gem for Rails Application. No dependencies. No ImageMagick, No RMagick.

  2. Working with Devise · huacnlee/rucaptcha Wiki · GitHub

  3. 全栈营课程帮助文档


远程heroku验证码登录

本地实现验证码登录功能后,上传heroku,logs显示:localhost:11211 failed

我尝试google,搜到这个stackover flow上面的这个答案

顺藤摸瓜,找到这个heroku官方文档

结果在cache上浪费了太多时间,测试了半天也没有理想的结果。

最后,请教了YY师兄才解决了问题。

这个addons需要填写信用卡信息:

1.填写信用卡信息
填写信用卡信息

2.heroku setting
$ heroku addons:create memcachier:dev
这个命令会在heroku远程端产生三个key,并同步到后台就不要再本地pplication.yml里面设置了。

3.revise production.rb

config.cache_store = :dalli_store,
                       (ENV["MEMCACHIER_SERVERS"] || "").split(","),
                       {:username => ENV["MEMCACHIER_USERNAME"],
                        :password => ENV["MEMCACHIER_PASSWORD"],
                        :failover => true,
                        :socket_timeout => 1.5,
                        :socket_failure_delay => 0.2,
                        :down_retry_delay => 60
                       }

4.commit and deploy
$ git add .
$ git commit -m "change config for rucaptcha/dalli on heroku"

一开始,local和production都可以正常使用。
不过重启rails s之后,报错如下:

/Users/Yammy/.rvm/gems/ruby-2.3.1/gems/rucaptcha-2.0.3/lib/rucaptcha/engine.rb:20:in `block in <class:Engine>':  (RuntimeError)

  RuCaptcha's cache_store requirements are stored across processes and machines,
  such as :mem_cache_store, :redis_store, or other distributed storage.
  But your current set is :null_store.

  Please make config file `config/initializes/rucaptcha.rb` to setup `cache_store`.
  More infomation please read GitHub RuCaptcha README file.

本来以为需要加一个判断来根据不同的环境调用内容:
一开始,我把rucaptcha.rb.temp改回rucaptcha.rb,并做如下判断:

if Rails.env.production?
    config.cache_store = :dalli_store
  else
    self.cache_store = :mem_cache_store
  end

这样的话,本地可以用,但上传到heroku之后,在运行migrate之后,报错了:

rake aborted!
NameError: undefined local variable or method `config' for #<RuCaptcha::Configuration:0x007f68

最后经由师兄提醒,我才明白问题在本地环境的代码问题,于是修改development.rb

# Enable/disable caching. By default caching is disabled.
  config.action_controller.perform_caching = true

  # if Rails.root.join('tmp/caching-dev.txt').exist?
  #   config.action_controller.perform_caching = true
  #
  #   config.cache_store = :memory_store
  #   config.public_file_server.headers = {
  #     'Cache-Control' => 'public, max-age=172800'
  #   }
  # else
  #   config.action_controller.perform_caching = false
  #
  #   config.cache_store = :null_store
  # end

即注销了一大段代码,并把其中的一行代码单独拿出来
我的理解是,由于rucaptcha这个gem的源代码有一个这样的判断:

module RuCaptcha
  class Engine < ::Rails::Engine
    isolate_namespace RuCaptcha

    initializer 'rucaptcha.prepend.cache' do
      cache_store = RuCaptcha.config.cache_store
      store_name = cache_store.is_a?(Array) ? cache_store.first : cache_store
      if [:memory_store, :null_store, :file_store].include?(store_name)
        msg = "
  RuCaptcha's cache_store requirements are stored across processes and machines,
  such as :mem_cache_store, :redis_store, or other distributed storage.
  But your current set is :#{store_name}.
  Please make config file `config/initializes/rucaptcha.rb` to setup `cache_store`.
  More infomation please read GitHub RuCaptcha README file.
"
        if store_name == :null_store
          raise msg
        else
          puts msg
        end
      end
    end
  end
end

酱紫的话,就很容易解释为什么移除rucaptcha.rb就会有那样的报错了。

相关资料参考:

  1. Building a Rails 3 Application with Memcache | Heroku Dev Center
  2. https://devcenter.heroku.com/articles/memcachier#rails-3-and-4
  3. 全栈营课程帮助文档

Comments

comments powered by Disqus