我想问用户一个问题,如果用户正确回答了我的问题,请让他只注册 。我搜查了devise how-to acticles,但我的情况似乎并不存在。
有没有一种惯用的方法来处理这种情况?
第一个想法可能是使用javascript,但答案存储在LDAP中,我希望在rails中更容易处理。
我还在考虑禁用/users/sign_up
路由,手动调用操作(devise/registration#new
)并渲染视图(devise/registration/new
)。
我能想到的另一种方法是运行一个后台守护进程,它将收集会话ID,用户可以正确回答问题。在正确答案中,用户将被重定向到公共可用的注册页面,该页面将使用守护程序检查用户的会话ID。
答案 0 :(得分:6)
假设你已经签署了cookie数据(这是Rails 3中的默认值),你可以像你说的那样做并使用会话:
# app/controllers/preauth_controller.rb
def new
end
def create
if params[:answer] == 'correct answer'
session[:preauthorized] = true
redirect_to sign_up_path
end
flash[:error] = 'Incorrect answer'
render :new
end
# app/controllers/users_controller.rb
before_filter :verify_preauth, only: [:new, :create]
def verify_preauth
redirect_to new_preauth_path unless session[:preauthorized]
end
如果cookie数据未签名,则preauthorized
密钥可能会被客户端篡改,因此不应被信任。
如果您的网页在传输过程中使用HTTPS通过TLS进行加密,并且您没有任何XSS漏洞,那么这应该足以满足您的需求。如果您认为这是一段特别敏感的代码,那么您需要的不仅仅是StackOverflow用户的想法,而是指导并实施一种全面的方法来保护您的应用程序。
答案 1 :(得分:4)
我的方法有点不同。
用这个覆盖设计的注册控制器,这样即使他们直接访问网址并尝试注册,他们也无法
#app/controllers/registration_controller.rb
class RegistrationsController < Devise::RegistrationsController
before_filter :check_answered_or_not,only:[:create,:new]
def check_answered_or_not
if not session[:answered]==true
redirect_to question_path
end
end
private
def sign_up_params
params.require(:user).permit(:name,:phone,:password,:password_confirmation,:email)
end
def account_update_params
params.require(:user).permit(:name,:phone,:password,:password_confirmation,:email,:current_password)
end
end
我的2cents
答案 2 :(得分:4)
为了提高以前建议的安全性,最好的建议似乎是通过coreyward,但它是不安全的(无论cookie是否加密 - 请参阅我对OP的评论)
# app/controllers/preauth_controller.rb
def new
end
def create
if params[:answer] == 'correct answer'
# Create a secret value, the `token`, we will share it to the client
# through the `session` and store it on the server in `Rails.cache`
# (or you can use the database if you like)
#
# The fact that this token expires (by default) in 15 minutes is
# a bonus, it will secure the system against later use of a stolen
# cookie. The token is also secure against brute force attack
@token = SecureRandom.base64
session[:preauthorization_token] = @token
Rails.cache.write("users/preauthorization_tokens/#{@token}", "OK")
redirect_to sign_up_path
else
flash[:error] = 'Incorrect answer'
render :new
end
end
# app/controllers/users_controller.rb
before_filter :verify_preauth, only: [:new, :create]
def verify_preauth
# When we approve preauthorization we confirm that the
# `token` is known to the client, if the client knows the token
# let him sign up, else make him go away
token = session[:preauthorization_token]
redirect_to new_preauth_path unless token and Rails.cache.read("users/preauthorization_tokens/#{token}") == "OK"
end
可选择的事情/玩....
Rails.cache
条目:expires_in
设置,你通常希望它尽可能短,只要需要:)但轨道默认为15分钟是相当不错的server_session
基本相同的session
对象,但将数据存储在Rails.cache
中使用存储在session
中的随机可过期令牌,用于访问缓存条目的方式与我们在此处的方式非常相似Rails.cache
往返(redis,memcache,AR,...)而导致响应时间更长OK
进入缓存值,您可以存储值的哈希值,如果您需要更多数据,安全地存储在主机上,可以解决这个问题答案 3 :(得分:3)
所以......可能应该在module Validatable
?
...
base.class_eval do
validates_presence_of :email, if: :email_required?
validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
validates_format_of :email, with: email_regexp, allow_blank: true, if: :email_changed?
validates_presence_of :password, if: :password_required?
validates_confirmation_of :password, if: :password_required?
validates_length_of :password, within: password_length, allow_blank: true
validates_presence_of :question, if: :question_required?
validates_format_of :question, with: answered_regexp, if: :answered_changed?
end
end
...
def email_required?
true
end
def question_required?
true
end
这不是解决方案,但我希望它可以帮助你...
答案 4 :(得分:1)
我认为最简单的方法是更改默认设计控制器,其中包含before_action
的自定义控制器:
# routes.rb
devise_for :users, :controllers => {:registrations => "registrations"}
使用以下控制器实现:
# app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
before_action :check_answers, only: :new # :new action is responsible for :sign_up route
private
def check_answers
unless session[:gave_answers]
redirect_to ask_questions_path
false
end
end
end
设置这样的会话:
# somewhere in questions controller:
if answers_correct?
session[:gave_answers] = true
redirect_to new_registration_path
end
一旦此控制器继承自Devise::RegistrationsController
,除检查答案功能外,所有行为都保持默认状态。
关于关于惯用方式的问题 - official documentation中描述了这种方法。你的应用 - 你的逻辑,没关系。
更新:
在评论中@bbozo指出了这个和其他答案的某些安全问题。为了更安全,您可以添加expiration time并设置一些随机密钥令牌(评论中的更多信息)。