Gem 珍爱生命,远离 devise

jasl · 2013年06月26日 · 最后由 andywang 回复于 2015年10月11日 · 8368 次阅读

过去一直采用纯 oauth 方式登录,今天增加了邮件密码方式登录。 方案很标准 mongodb+devise+omniauth

由于直接通过 oauth 方式登录的话是不会填写邮箱和密码的,所以 email 和 password 字段都为空。 通过 oauth 方式注册用户的代码:

def create_from_omniauth(data)
  create do |user|
    auth = Auth.from_omniauth(data)
    user.auths << auth
    user.name = auth.nickname
    user.remote_avatar_url = auth.parse_image(data)
  end
end

有关的 oauth 回调代码

user = User.create_from_omniauth(omniauth)
user.update_from_omniauth(omniauth)
sign_in user
redirect_to edit_user_registration_path(:new => true)

要注意的是:由于 devise 的 validatable 的限制,email 和 password 都是必填,所以按道理这个 create 是一定失败的,但是奇迹的是,保存确实成功了! 经过调试,create 确实没保存成功,但是调用过 sign_in 方法后,user 被持久化了,于是顺着 sign_in 一路查阅源码没有任何可疑的地方。。。

@lilu 突然提到,可能是 trackable 的问题,查阅源码,果然发现。。。

def update_tracked_fields!(request)
  old_current, new_current = self.current_sign_in_at, Time.now.utc
  self.last_sign_in_at     = old_current || new_current
  self.current_sign_in_at  = new_current

  old_current, new_current = self.current_sign_in_ip, request.remote_ip
  self.last_sign_in_ip     = old_current || new_current
  self.current_sign_in_ip  = new_current

  self.sign_in_count ||= 0
  self.sign_in_count += 1

  save(:validate => false) or raise "Devise trackable could not save #{inspect}." \
    "Please make sure a model using trackable can be saved at sign in."
end

在更新 track 的时候,会禁用 validation,所以造成了上述问题,顺便看了下其他模块的代码,使用 save:validate => false 很频繁。

所以,如果因为一些原因要自己处理登录逻辑,调用sign_in之前,一定要确保对象的状态。

PS:这个问题很久前已经有人提issue 了,但是作者的回答完全不着边际 PS2:去济南的时候 @lgn21st 说过,能搞懂 devise 的绝对不是一般人... PS3:其实自从看到 omniauth 的设计后,就被深深吸引了,推荐大家放弃复杂的 devise...虽然他很省事,但是需要实现一些用户系统的高级功能的时候,要考虑下自己能不能驾驭他...

仅仅使用 devise 来实现它自带的那些功能时还是很方便的,一旦要自己定制点东西就麻烦了,前段时间就被坑过,最后干脆放弃不用了。。。

devuse 用在快速构建上,要定制,算了,我真心觉得自己水平吃不透,

从不赞成使用这类 gem 对自己水平提高没好处 踏上坑赔上的时间几倍都不止

devise 太重了,一般情况下用不到那么复杂的功能,还是自己写吧。

devise 不算复杂,花点时间看一下源码,特别是 models 下面的几个 able.rb,就能理解一下它为什么这么设计。对于掌握一个系统的用户登录,管理等基础设施来说,这点投资是绝对划算的。

另外,你说的这个问题,devise 的 wiki 上已经提到过,可以翻一下 wiki 上关于 omniauth 集成的,password 是用随机 token,email 是从 api 获取。

另另外,通常我喜欢用 create!,提早抛出异常,而不是用 create 的返回值来判断

不过 devise 的文档还是很丰富,基本都找到自己问题解决方案。我之前用过 sorcery,也觉得很好用……说到定制,我也觉得 devise 很蛋疼,不知其它类似的 gem 是不是这样?

还是很不错的,用起来方便

发现 LZ 没看官方 wiki 的飘过

如果用 omniauth 进行 OAuth 登陆,可以将 password(email 也一样) 设置为不需要。

def password_required?
  super && provider.blank?
end

这里的 provider 就是 OAuth 的 provider,也可以设成任意的其它逻辑。

这样的话,既支持 OAuth 登陆,邮箱与密码登陆也不会受影响。

#6 楼 @quakewang 很多网站都不会返回 email 呀 这里的问题是 sign_in 本身是不应该影响 model 状态的,却影响了 devise 的很多策略都会绕过 validation,绕过 validation 后还失败才抛出异常,没想通他为什么要这样做

#10 楼 @hisea 嗯,昨晚花了些时间研究了下源码,搞定了

#9 楼 @leopku 关键是 sign_in 不应当影响 model 的状态呀,而且 sign_in 本身不对 model 操作,而是执行 model 里定义的 devise 策略,这样很难去 debug

#11 楼 @jasl 不会返回 email 的 api,可以给用户随机生成一个

#14 楼 @quakewang 也不用,覆盖下 email_required 就好了,这样通过 email 是否为空还能判断一些东西

#1 楼 @edgar_wang_cn 放弃 devise 之后,请问你是怎么做用户系统的?

#5 楼 @quakewang wiki 应该讲的是 how, 但我想知道 why, 有没有哪里有讲 devise 设计的思想的,或者你可以分享一下看源码的心得?

谢谢!

#16 楼 @Peter 有很多啊,omniauth、authlogic、或者纯手写 几个月前我了解过 omniauth 的设计之后,已经被深深吸引了,虽然他还没有一个类似于 devise 的 full-stack 解决方案,但我自己写一个也可以

devise 的源码不算难,warden 那块才算有点小复杂,但是有大量的 hook,所以不全面了解他的话...还是会踩坑里

#17 楼 @jasl 纯手写只是登陆注册啥的不算复杂,就是如果要做 reset 啊啥的逐渐就麻烦上去了-,- 如果定制要求不高,我觉得还是用 devise,毕竟那么多人用过,测试充分。

跟安全有关的东西我觉得还是保守点好,毕竟 devise 经过大量项目验证,虽然自己写一个验证系统也不难,但万一疏忽了,那可能是难以挽回的损失

#16 楼 @Peter 我当时的要求比较简单,最后直接纯手写解决的。- -。

#19 楼 @HungYuHei omniauth authlogic 同样经过验证的嘛!

#18 楼 @xmonkeycn 确实,还是要用现有成熟的 gem 来做

当然,我这是标题党了...我几乎所有的项目都在用 devise...

#16 楼 @Peter 纯手写也能做用户系统啊 只是复杂而已 太轻量级的系统有时就没必要用 devise 了啊 我自己的一个小系统的认证就只用了 oauth 连 gem 都没用 直接写的 HTTParty 2 个函数解决 毕竟 gem 也是要占内存空间的

哈哈,楼主标题党! 对任何完善封装的产品进行定制化都是麻烦的,不仅 devise,类似的还能说好多: 珍惜生命,远离 Rails(因为可定制化不如手写 HTML) 珍惜生命,远离 Ruby(因为很多 C 容易做到的定制化,Ruby 不能做) 珍惜生命,远离 HTTP(因为如果要定制化一些数据链路层的结构,它做不到) 珍惜生命,远离餐厅(因为厨师给豆腐脑加了糖,而我希望自己加盐,它做不到) ……

#11 楼 @jasl 不是很多,是基本上没有,不可能将其比较敏感的用户信息数据返回给第三方的。

不要用 devise, 用 omniauth-identity 嘛。统一用 omniauth

#17 楼 @jasl authlogic 我不建议使用,commit 得不活跃,很多提交 issue 都没解决,正准备迁到 devise。

登录/注册 自己动手丰衣足食

需要 登录 后方可回复, 如果你还没有账号请 注册新账号