嗨,我正在使用Devise进行用户身份验证,突然我的新用户注册不起作用了。
这是我得到的错误。
ActionController::InvalidAuthenticityToken
Rails.root: /home/example/app
Application Trace | Framework Trace | Full Trace
Request
Parameters:
{"utf8"=>"✓",
"user"=>{"email"=>"example@gmail.com",
"password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]"},
"x"=>"0",
"y"=>"0"}
这是我的注册控制器
class RegistrationsController < Devise::RegistrationsController
prepend_before_filter :require_no_authentication, :only => [ :new, :create, :cancel ]
prepend_before_filter :authenticate_scope!, :only => [:edit, :update, :destroy]
before_filter :configure_permitted_parameters
prepend_view_path 'app/views/devise'
# GET /resource/sign_up
def new
build_resource({})
respond_with self.resource
end
# POST /resource
def create
build_resource(sign_up_params)
if resource.save
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_navigational_format?
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}" if is_navigational_format?
expire_session_data_after_sign_in!
respond_with resource, :location => after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
respond_to do |format|
format.json { render :json => resource.errors, :status => :unprocessable_entity }
format.html { respond_with resource }
end
end
end
# GET /resource/edit
def edit
render :edit
end
# PUT /resource
# We need to use a copy of the resource because we don't want to change
# the current user in place.
def update
self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)
if update_resource(resource, account_update_params)
if is_navigational_format?
flash_key = update_needs_confirmation?(resource, prev_unconfirmed_email) ?
:update_needs_confirmation : :updated
set_flash_message :notice, flash_key
end
sign_in resource_name, resource, :bypass => true
respond_with resource, :location => after_update_path_for(resource)
else
clean_up_passwords resource
respond_with resource
end
end
# DELETE /resource
def destroy
resource.destroy
Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
set_flash_message :notice, :destroyed if is_navigational_format?
respond_with_navigational(resource){ redirect_to after_sign_out_path_for(resource_name) }
end
# GET /resource/cancel
# Forces the session data which is usually expired after sign
# in to be expired now. This is useful if the user wants to
# cancel oauth signing in/up in the middle of the process,
# removing all OAuth session data.
def cancel
expire_session_data_after_sign_in!
redirect_to new_registration_path(resource_name)
end
protected
# Custom Fields
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) do |u|
u.permit(:first_name, :last_name,
:email, :password, :password_confirmation)
end
end
def update_needs_confirmation?(resource, previous)
resource.respond_to?(:pending_reconfirmation?) &&
resource.pending_reconfirmation? &&
previous != resource.unconfirmed_email
end
# By default we want to require a password checks on update.
# You can overwrite this method in your own RegistrationsController.
def update_resource(resource, params)
resource.update_with_password(params)
end
# Build a devise resource passing in the session. Useful to move
# temporary session data to the newly created user.
def build_resource(hash=nil)
self.resource = resource_class.new_with_session(hash || {}, session)
end
# Signs in a user on sign up. You can overwrite this method in your own
# RegistrationsController.
def sign_up(resource_name, resource)
sign_in(resource_name, resource)
end
# The path used after sign up. You need to overwrite this method
# in your own RegistrationsController.
def after_sign_up_path_for(resource)
after_sign_in_path_for(resource)
end
# The path used after sign up for inactive accounts. You need to overwrite
# this method in your own RegistrationsController.
def after_inactive_sign_up_path_for(resource)
respond_to?(:root_path) ? root_path : "/"
end
# The default url to be used after updating a resource. You need to overwrite
# this method in your own RegistrationsController.
def after_update_path_for(resource)
signed_in_root_path(resource)
end
# Authenticates the current scope and gets the current resource from the session.
def authenticate_scope!
send(:"authenticate_#{resource_name}!", :force => true)
self.resource = send(:"current_#{resource_name}")
end
def sign_up_params
devise_parameter_sanitizer.sanitize(:sign_up)
end
def account_update_params
devise_parameter_sanitizer.sanitize(:account_update)
end
end
这是我的会话控制器
class SessionsController < DeviseController
prepend_before_filter :require_no_authentication, :only => [ :new, :create ]
prepend_before_filter :allow_params_authentication!, :only => :create
prepend_before_filter { request.env["devise.skip_timeout"] = true }
prepend_view_path 'app/views/devise'
# GET /resource/sign_in
def new
self.resource = resource_class.new(sign_in_params)
clean_up_passwords(resource)
respond_with(resource, serialize_options(resource))
end
# POST /resource/sign_in
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
respond_to do |format|
format.json { render :json => {}, :status => :ok }
format.html { respond_with resource, :location => after_sign_in_path_for(resource) }
end
end
# DELETE /resource/sign_out
def destroy
redirect_path = after_sign_out_path_for(resource_name)
signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
set_flash_message :notice, :signed_out if signed_out && is_navigational_format?
# We actually need to hardcode this as Rails default responder doesn't
# support returning empty response on GET request
respond_to do |format|
format.all { head :no_content }
format.any(*navigational_formats) { redirect_to redirect_path }
end
end
protected
def sign_in_params
devise_parameter_sanitizer.sanitize(:sign_in)
end
def serialize_options(resource)
methods = resource_class.authentication_keys.dup
methods = methods.keys if methods.is_a?(Hash)
methods << :password if resource.respond_to?(:password)
{ :methods => methods, :only => [:password] }
end
def auth_options
{ :scope => resource_name, :recall => "#{controller_path}#new" }
end
end
这是登记表
<%= form_for(:user, :html => {:id => 'register_form'}, :url => user_registration_path, :remote => :true, :format => :json) do |f| %>
<div class="name_input_container">
<div class="name_input_cell">
<%= f.email_field :email, :placeholder => "email" %>
<%= f.password_field :password, :placeholder => "password", :title => "8+ characters" %>
<%= f.password_field :password_confirmation, :placeholder => "confirm password" %>
<div class="option_buttons">
<div class="already_registered">
<%= link_to 'already registered?', '#', :class => 'already_registered', :id => 'already_registered', :view => 'login' %>
</div>
<%= image_submit_tag('modals/account/register_submit.png', :class => 'go') %>
<div class="clear"></div>
</div>
<% end %>
8条答案
按热度按时间tcbh2hod1#
根据内核
application_controller.rb
中的the comments,将protect_from_forgery
设置为以下值:protect_from_forgery
* 而不使用:with
参数 *,默认情况下将使用:null_session
:更新:
这似乎是Devise行为中的documented bug。Devise的作者建议在引发此异常的特定控制器操作上禁用
protect_from_forgery
:5anewei62#
您忘记在布局文件中添加
<%= csrf_meta_tags %>
。例如:
flseospp3#
**TLDR:**您遇到此问题可能是因为您的表单通过XHR提交。
先做几件事:
1.此令牌在您登录或注销时过期
一个标准的HTTP登录将导致整个页面刷新,旧的CSRF令牌将被“刷新”,并被Rails在您登录时创建的全新令牌“替换”。
AJAX 登录将不刷新页面,因此现在无效的陈旧的CSRF令牌仍然存在于您的页面上。
解决方案是 AJAX 登录后手动更新HEAD标记内的CSRF令牌。
我无耻地从一个有用的thread on this matter中借用了一些步骤。
**步骤1:**将新的CSRF-token添加到成功登录后发送的响应标头中
**步骤2:**当
ajaxComplete
事件触发时,使用jQuery用新值更新页面:YMMV取决于你的Devise配置。我怀疑这个问题最终是由旧的CSRF令牌杀死请求和rails抛出异常引起的。
rkkpypqq4#
如果您只使用API,您应该尝试:
http://edgeapi.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html#method-i-protect_against_forgery-3F
zphenhs45#
对于Rails 5,可能是由于
protect_from_forgery
和您的before_actions
的触发顺序。我最近也遇到了类似的情况,尽管
protect_from_forgery with: :exception
是ApplicationController
中的第一行,但before_action
仍然在干扰。解决办法是改变:
致:
有a blog post about it
h22fl7wq6#
我花了整个上午来调试它,所以我想我应该在这里分享一下,以防有人在将Rails更新到5.2或6时遇到类似的问题。
我有两个问题
1)无法验证CSRF令牌的真实性。
并且,在增加跳过验证之后,
2)请求将通过,但用户仍未登录。
我没有在开发中缓存
在会话存储中
我猜应用程序试图在缓存中存储会话,但它是空的-所以它没有登录。
它启动了缓存-登录开始工作。
您可能需要
ddarikpa7#
浏览器缓存HTML问题(2020年)
如果你已经尝试了这个页面上的所有补救措施,你仍然有一个
InvalidAuthenticityToken
异常的问题,它可能与浏览器缓存HTML有关。有an issue on Github与100的注解沿着一些可复制的代码。简单地说,这是发生在我身上的事情,因为它涉及到HTML缓存:1.用户浏览网站。Rails在第一个GET请求时设置一个签名的会话cookie。有关配置选项,请参见
config/initializers/session_store.rb
。此会话cookie存储有用的信息,包括用于解密和验证请求真实性的CSRF令牌。重要信息:默认情况下,会话cookie将在浏览器窗口关闭时过期。1.用户浏览到一个包含表单的页面。对我来说,我在登录页面上收到的异常最多。
verified_request?
方法使用cookie中的CSRF标记对其进行验证。许多浏览器现在都实现了HTML缓存,这样当您打开页面时,HTML就可以在没有请求的情况下加载。不幸的是,当浏览器关闭时,会话cookie会被破坏,因此如果用户在表单(如登录页面)上关闭浏览器,那么第一个请求将不包含CSRF标记,从而引发InvalidAuthenticityError。
两种常见解决方案
1.将会话cookie的有效期延长到浏览器窗口之外。
1.在浏览器中检测会话cookie是否丢失(通过代理cookie),如果丢失,则刷新页面。
1.延长会话cookie的有效期
正如Github评论中提到的,Django采用了这种方法:
Django将令牌添加到自己的cookie CSRF_COOKIE中,这是一个一年后过期的永久性cookie,如果有后续请求,cookie的过期时间会被更新。
在导轨中:
与安全相关的许多事情,有concern,这可能会产生漏洞,但我还没有找到任何攻击者如何利用这一点的例子。
2.使用javascript刷新页面
这种方法包括设置一个单独的标记,该标记可以被浏览器读取,如果该标记不存在,则刷新页面。因此,当浏览器加载缓存的HTML(没有会话cookie),在页面上执行JS时,用户可以被重定向或刷新HTML。
例如,为每个不受保护的请求设置Cookie:
在JS中检查此cookie:
这将强制刷新浏览器。
结论
我希望这能帮助一些人;这个bug让我花了好几天的时间。如果你仍然有问题,考虑阅读以下内容:
prepend: true
错误,在此详细描述。nukf8bse8#
您必须将protect_from_forgery放在验证用户身份的操作之前,这是正确的解决方案