在保存用户模型时,我收到(验证失败:Uid 不能为空)。Uid 本身属于 Identity 对象 - 它用作多种身份类型的单个身份访问点。换句话说,您将能够在应用程序中使用 facebook、twitter 或linkedin omniauth 策略,而无需创建多个帐户。它似乎不像宣传的那样工作。鉴于我从应用程序登录 LinkedIn 后无法创建用户。
我试图将 env 引用换成 request.env。因为,它不存在于当前范围内。我试图定位错误,但无法准确找到身份模型在创建用户之前没有存储身份的确切原因。它在创建用户对象后立即失败,而不是检查用户是否存在并附加了身份或应该创建。
服务器开发日志
Started GET "/users/auth/linkedin" for 127.0.0.1 at 2019-08-03 22:56:03 -0400
I, [2019-08-03T22:56:04.789247 #23148] INFO -- omniauth: (linkedin) Request phase initiated.
Started GET "/users/auth/linkedin/callback?oauth_token=77--2555555-5555-4444-ssfs-dcsfsfsfsfsf93&oauth_verifier=28090" for 127.0.0.1 at 2019-08-03 22:56:31 -0400
I, [2019-08-03T22:56:32.422234 #23148] INFO -- omniauth: (linkedin) Callback phase initiated.
Processing by OmniauthCallbacksController#linkedin as HTML
Parameters: {"oauth_token"=>"77--2555555-5555-4444-ssfs-dcsfsfsfsfsf93", "oauth_verifier"=>"28090"}
Identity Load (1.4ms) SELECT "identities".* FROM "identities" WHERE "identities"."uid" IS NULL AND "identities"."provider" = ? LIMIT ? [["provider", "linkedin"], ["LIMIT", 1]]
↳ app/models/identity.rb:7
(0.2ms) begin transaction
↳ app/models/identity.rb:7
Identity Exists (1.1ms) SELECT 1 AS one FROM "identities" WHERE "identities"."uid" IS NULL AND "identities"."provider" = ? LIMIT ? [["provider", "linkedin"], ["LIMIT", 1]]
↳ app/models/identity.rb:7
(0.2ms) rollback transaction
↳ app/models/identity.rb:7
(0.2ms) begin transaction
↳ app/models/user.rb:47
User Exists (2.0ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "change@me--linkedin.com"], ["LIMIT", 1]]
↳ app/models/user.rb:47
User Create (44.3ms) INSERT INTO "users" ("email", "encrypted_password", "created_at", "updated_at", "confirmed_at") VALUES (?, ?, ?, ?, ?) [["email", "change@me--linkedin.com"], ["encrypted_password", "$2a"], ["created_at", "2019-08-04 02:56:33.928307"], ["updated_at", "2019-08-04 02:56:33.928307"], ["confirmed_at", "2019-08-04 02:56:33.921311"]]
↳ app/models/user.rb:47
(134.0ms) commit transaction
↳ app/models/user.rb:47
(0.1ms) begin transaction
↳ app/models/user.rb:54
Identity Exists (2.1ms) SELECT 1 AS one FROM "identities" WHERE "identities"."uid" IS NULL AND "identities"."provider" = ? LIMIT ? [["provider", "linkedin"], ["LIMIT", 1]]
↳ app/models/user.rb:54
(0.2ms) rollback transaction
↳ app/models/user.rb:54
Completed 422 Unprocessable Entity in 879ms (ActiveRecord: 191.3ms)
ActiveRecord::RecordInvalid (Validation failed: Uid can't be blank):
app/models/user.rb:54:in `find_for_oauth'
(eval):3:in `linkedin'
omniauth_callbacks_controller.rb
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def self.provides_callback_for(provider)
class_eval %Q{
def #{provider}
@user = User.find_for_oauth(request.env["omniauth.auth"], current_user)
if @user.persisted?
sign_in_and_redirect @user, event: :authentication
set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
else
session["devise.#{provider}_data"] = env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
}
end
[:facebook, :linkedin].each do |provider|
provides_callback_for provider
end
def after_sign_in_path_for(resource)
if resource.email_verified?
super resource
else
finish_signup_path(resource)
end
end
def failure
redirect_to root_path
end
end
身份.rb
class Identity < ApplicationRecord
belongs_to :user
validates_presence_of :uid, :provider
validates_uniqueness_of :uid, :scope => :provider
def self.find_for_oauth(auth)
find_or_create_by(uid: auth.uid, provider: auth.provider)
end
end
用户.rb
class User < ApplicationRecord
TEMP_EMAIL_PREFIX = 'change@me'
TEMP_EMAIL_REGEX = /\Achange@me/
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :omniauthable, :trackable, :confirmable
validates_format_of :email, :without => TEMP_EMAIL_REGEX, on: :update
def self.find_for_oauth(auth, signed_in_resource = nil)
# Get the identity and user if they exist
identity = Identity.find_for_oauth(auth)
# If a signed_in_resource is provided it always overrides the existing user
# to prevent the identity being locked with accidentally created accounts.
# Note that this may leave zombie accounts (with no associated identity) which
# can be cleaned up at a later date.
user = if signed_in_resource then
signed_in_resource
else
identity.user
end
# Create the user if needed
if user.nil?
# Get the existing user by email if the provider gives us a verified email.
# If no verified email was provided we assign a temporary email and ask the
# user to verify it on the next step via UsersController.finish_signup
email_is_verified = auth.info.email && (auth.info.verified || auth.info.verified_email)
email = auth.info.email if email_is_verified
user = User.where(:email => email).first if email
# Create the user if it's a new registration
if user.nil?
user = User.new(
name: auth.extra.raw_info.name,
#username: auth.info.nickname || auth.uid,
email: email ? email : "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",
password: Devise.friendly_token[0,20]
)
user.skip_confirmation!
user.save!
end
end
# Associate the identity with the user if needed
if identity.user != user
identity.user = user
identity.save! # Where error occurs
end
user
end
def email_verified?
self.email && self.email !~ TEMP_EMAIL_REGEX
end
protected
def confirmation_required?
false
end
end
预期行为:用户将成功登录其 Linkedin 帐户并被重定向到应用程序以完成注册。
实际结果:user对象创建,然后保存Identity失败,Uid留空。