我完全支持瘦控制器和胖模型,我认为 auth 不应该打破这个原则。
我已经使用 Rails 编码一年了,我来自 PHP 社区。对我来说,将当前用户设置为“request-long global”是一个简单的解决方案。这在某些框架中是默认完成的,例如:
在 Yii 中,你可以通过调用 Yii::$app->user->identity 来访问当前用户。见http://www.yiiframework.com/doc-2.0/guide-rest-authentication.html
在 Lavavel 中,你也可以通过调用 Auth::user() 来做同样的事情。请参阅http://laravel.com/docs/4.2/security
为什么我可以从控制器传递当前用户?
假设我们正在创建一个支持多用户的简单博客应用程序。我们正在创建公共站点(匿名用户可以阅读和评论博客文章)和管理站点(用户已登录并且他们可以对数据库中的内容进行 CRUD 访问。)
这是“标准AR”:
class Post < ActiveRecord::Base
has_many :comments
belongs_to :author, class_name: 'User', primary_key: author_id
end
class User < ActiveRecord::Base
has_many: :posts
end
class Comment < ActiveRecord::Base
belongs_to :post
end
现在,在公共网站上:
class PostsController < ActionController::Base
def index
# Nothing special here, show latest posts on index page.
@posts = Post.includes(:comments).latest(10)
end
end
那是干净和简单的。然而,在管理站点上,还需要更多东西。这是所有管理控制器的基本实现:
class Admin::BaseController < ActionController::Base
before_action: :auth, :set_current_user
after_action: :unset_current_user
private
def auth
# The actual auth is missing for brievery
@user = login_or_redirect
end
def set_current_user
# User.current needs to use Thread.current!
User.current = @user
end
def unset_current_user
# User.current needs to use Thread.current!
User.current = nil
end
end
因此添加了登录功能,并将当前用户保存到全局。现在用户模型看起来像这样:
# Let's extend the common User model to include current user method.
class Admin::User < User
def self.current=(user)
Thread.current[:current_user] = user
end
def self.current
Thread.current[:current_user]
end
end
User.current 现在是线程安全的
让我们扩展其他模型以利用这一点:
class Admin::Post < Post
before_save: :assign_author
def default_scope
where(author: User.current)
end
def assign_author
self.author = User.current
end
end
帖子模型已扩展,因此感觉好像只有当前登录的用户帖子。多么酷啊!
管理员后控制器可能看起来像这样:
class Admin::PostsController < Admin::BaseController
def index
# Shows all posts (for the current user, of course!)
@posts = Post.all
end
def new
# Finds the post by id (if it belongs to the current user, of course!)
@post = Post.find_by_id(params[:id])
# Updates & saves the new post (for the current user, of course!)
@post.attributes = params.require(:post).permit()
if @post.save
# ...
else
# ...
end
end
end
对于 Comment 模型,管理员版本可能如下所示:
class Admin::Comment < Comment
validate: :check_posts_author
private
def check_posts_author
unless post.author == User.current
errors.add(:blog, 'Blog must be yours!')
end
end
end
恕我直言:这是一种强大且安全的方式,可确保用户一次性访问/修改他们的数据。想想如果每个查询都需要以“current_user.posts.whatever_method(...)”开头,那么开发人员需要编写多少测试代码?很多。
如果我错了,请纠正我,但我认为:
这都是关于关注点分离的。即使很明显只有控制器应该处理身份验证检查,但当前登录的用户绝不应该留在控制器层。
唯一要记住的是:不要过度使用它!请记住,可能有电子邮件工作人员不使用 User.current 或者您可能从控制台等访问应用程序......