为 Rails 应用程序添加行级安全性
如果您正在使用数据库构建 Web 应用程序,您可能需要多个用户,这意味着添加行级安全性和身份验证。
Rails 使用了一个很酷的 gem,叫做 Devise。Devise 创建一个 User 表和视图以通过会话向 Web 应用程序进行身份验证,然后您可以向每个视图添加身份验证以确保您有一个有效的会话来查看该视图。
这一切都很好,但并没有解决我的另一个问题,即按用户限制数据库中的数据。所以我选择使用 Devise User 模型,特别是 id 字段,并由其他数据库表更改以包含 id 字段(我称之为 user_id )。当您随后调整 Rails 应用程序的其余部分(例如模型调用)以使用会话 user_id 作为每个 SQL 查询的一部分时,这一切都有效。让我尝试解释基本步骤,其中包含一些细节,但您需要针对您的情况即兴发挥。
我眼中的过程分为特定于设计的步骤和行级安全步骤。
设计步骤
将设计添加到您的 Rails 项目
add devise gem to gem file
gem ‘devise’
bundle install
rails generate devise:install
(将设计安装到 Rails 应用程序中 - 位于应用程序目录根目录中)
rails g devise:views
(可选步骤:这会复制视图,以便我们可以自定义和样式)
rails generate devise User
(创建用户模型)
bundle exec rake db:migrate
(创建表 - 注意您需要 yml 中的权限才能更改数据库结构)
其余与设计相关的步骤
添加顶部 div 到 application.html.erb 检查用户是否登录,并显示登录 ID 或新用户和登录链接
<div class=”top”>
<p class=”notice”><%= notice %></p>
<p class=”alert”><%= alert %></p>
<p>
<% if user_signed_in? %>
Logged in as <strong><%= current_user.email %></strong>.
<%= link_to ‘Edit profile’, edit_user_registration_path %> |
<%= link_to “Logout”, destroy_user_session_path, method: :delete %>
<% else %>
<%= link_to “Sign up”, new_user_registration_path %> |
<%= link_to “Login”, new_user_session_path %>
<% end %>
</div>
创建了一个“访客”控制器并在未登录时显示 - 还更新了 routes.rb 以使其成为根 - 即,当有人第一次访问您的站点时,您应该将他们带到访客控制器,而不是数据!您还没有会话。
更新了来宾/根控制器,特别是如果用户已登录,则索引操作以转到已登录的控制器
if user_signed_in?
redirect_to :controller=>’home’, :action=> ‘index’
end
在每个其他 data/loggedin 控制器中,将以下内容添加到控制器顶部的操作之前:这将确保没有人可以使用 URL 路径在未经身份验证的情况下登陆页面。
before_filter :authenticate_user!
行级步骤
使用 user_id 列更改您想要行级安全性“RLS”的表。
mysql> ALTER TABLE mytable ADD COLUMN user_id VARCHAR(255) AFTER id;
使用 users 表中的有效 user_id 更新表(假设您已经使用注册表单创建了一个用户。或者只使用 1,因为 Devise 似乎从 1 开始第一个用户 id)
mysql> UPDATE mytable SET user_id = ( SELECT id FROM users WHERE email = ‘mememe@me.com’ );
处理将 user_id 添加到表插入
在 data/loggedin 控制器 CREATE 操作中,更新参数对象中的 user_id 字段。
def create
params[:mytable][:user_id] = current_user.id
…
end
通过传递 user_id 作为参数来处理选择/查询
在数据/登录模型中,将 user_id 传递到新的 my 查询范围,然后将该新范围与其他范围组合(确保将 user_id 传递到其他范围;还有其他方法可以做到这一点,但我喜欢范围):
scope :my, -> (user_id) { where(“user_id = ?”, user_id) }
scope :mylovelyquery, -> (user_id) { my(user_id).where(:mylovelyselectioncriteria=>”A”).order(“mysortfield”) }
在 data/logged 控制器中,更改索引操作以获取 current_user.id 并将其传递给您创建的 ActiveRecord 范围。这一步很乏味,可以经常完成——查找任何控制器索引或任何执行 SELET 的控制器操作——需要 RLS!这也适用于 Ajax 调用。
def index
@user_id = current_user.id
@mytables = Mytable.my(@user_id).order(mylovelysortfield DESC”)
end
得到教训
可能让我最头疼的一件事是在检索数据库之前忘记获取 current_user.id。