19、文件上传与认证系统的实现与优化

文件上传与认证系统的实现与优化

1. 文件验证

在进行文件上传时,存在两个安全漏洞需要修复。首先,上传的文件可以是任意类型。为了解决这个问题,我们安装了 file_validators 宝石(gem),它可以为表单提供通用的文件验证功能。这个宝石的一个巨大优势是它不包含与 ActiveRecord 相关的代码,否则会导致表单出错。

以下是具体的验证代码:

contract do
  property :file, virtual: true
  validates :file, file_size: { less_than: 1.megabyte },
                  file_content_type: { allow: ['image/jpeg', 'image/png'] }
end

validates 中的 :file_size :file_content_type 选项帮助我们排除不需要的格式和过大的文件。

另一个问题是 image_meta_data 在表单中是可写的,这意味着有人可以将该字段注入到参数中,从而将任意内容写入到 thing image_meta_data 属性中。为了避免这个问题,我们可以使用 :deserializer 选项来实现写保护:

property :image_meta_data, deserializer: {writeable: false}
2. 上传流程概述

过去使用 Paperclip 时,文件上传似乎一切都能自动完成,但当出现问题或需要更改特定处理步骤时,会发现它就像一个黑盒子,难以控制。而 Paperdragon 结合 Reform Trailblazer 的操作,使上传过程变得清晰明确,便于我们进行调试和修改。上传过程如下:
1. 提交包含图像文件的表单。
2. Form#validate 之后, file 属性将指向该文件对象。
3. 运行验证时,会对 file 属性进行特定的文件检查,此时还没有进行处理或存储。
4. 在调用 validate 的操作中,通过表单的 image!(file) 访问器调用 Paperdragon ,可以处理和存储不同版本的图像。
5. 处理和存储完成后,会编译一个元数据哈希并推送到表单的 image_meta_data 字段。
6. 最后,当表单保存时, image_meta_data 字段将写入模型,模型会持久化图像在文件系统中的位置。

以下是上传流程的 mermaid 流程图:

graph LR
    A[提交表单] --> B[Form#validate]
    B --> C[文件验证]
    C --> D[调用 Paperdragon 处理和存储]
    D --> E[生成元数据哈希]
    E --> F[保存表单,写入模型]
3. 回调组

在处理表单时,如果没有上传文件就运行上传逻辑,会引发异常。为了避免这种情况,我们可以使用表单双胞胎的 changed? 方法:

def process(params)
  validate(params[:thing]) do |f|
    upload_image!(f) if f.changed?(:file)
    #..
  end
end

另一种方法是引入第二个回调组。 Trailblazer 允许我们根据需要创建任意数量的回调组。以下是重构后的操作代码:

callback(:upload) do
  on_change :upload_image!, property: :file
end

callback do
  collection :users do
    on_add :notify_author!
    on_add :reset_authorship!
  end
  on_create :expire_cache!
end

def process(params)
  validate(params[:thing]) do |f|
    dispatch!(:upload)
    f.save
    dispatch!
  end
end
4. 图像渲染

上传文件后,我们希望看到可视化的结果。使用 Paperdragon 渲染上传的图像非常简单。我们可以创建一个新的单元格 Thing::Cell::Decorator 来处理图像渲染:

class Thing::Cell::Decorator < Cell::Concept
  extend Paperdragon::Model::Reader
  processable_reader :image
  property :image_meta_data

  def thumb
    image_tag image[:thumb].url if image.exists?
  end
end

app/views/things/show.html.haml 中,我们可以使用这个装饰器单元格来渲染上传文件的缩略图:

%h1
  = concept("thing/cell/decorator", @thing).(:thumb)
5. 上传测试

为了确保上传功能正常工作,我们需要进行测试。以下是一个有效的上传测试示例:

it do
  thing = Thing::Create.(thing: {name: "Rails",
                                 file: File.open("test/images/cells.jpg")}).model
  Paperdragon::Attachment.new(thing.image_meta_data).exists?.must_equal true
end

相反的测试用例,即无效上传测试如下:

it "invalid upload" do
  res, op = Thing::Create.run(thing: {name: "Rails",
                                      file: File.open("test/images/hack.pdf")})
  res.must_equal false
  op.errors.to_s.must_equal "{:file=>[\"file has invalid extension\"]}"
end
6. 装饰器测试

我们还需要对装饰器单元格进行测试:

describe "Cell::Decorator" do
  it do
    thing = Thing::Create.(thing: {name: "Rails",
                                   file: File.open("test/images/cells.jpg")}).model
    concept("thing/cell/decorator", thing).thumb.must_equal "<img src=...>"
  end
end
7. 认证系统

在应用中,我们需要建立一些策略和规则,涉及用户的认证和授权。

7.1 按 ID 填充用户

在添加评论时,目前需要添加用户的电子邮件,提交后会创建一个新的用户对象,无论该电子邮件是否属于现有用户。为了保证用户信息的完整性,我们需要在评论的 Create#setup_model! 方法中进行修改,使用预填充器和填充器的组合:

class Comment::Create < Trailblazer::Operation
  contract do
    property :user,
             prepopulator: ->(*) { self.user = User.new },
             populator: :populate_user! do
      # .. nested setup ..
    end

    def populate_user!(fragment, *)
      self.user = User.find_by(email: fragment["email"]) or User.new
    end
  end
end

以下是测试代码:

it do
  params = {
    id: thing.id,
    comment: {"body"=>"Fantastic!", "weight"=>"1",
              "user"=>{"email"=>"joe@trb.org"}}
  }
  op1 = Comment::Create.(params)
  op2 = Comment::Create.(params)
  op1.model.user.id.must_equal op2.model.user.id
end
7.2 休眠用户

当用户在添加事物或评论时“即时”创建时,我们称这些用户为“休眠用户”。这些用户存在于系统中,但账户尚未确认。我们将先介绍手动注册流程,让用户能够明确地注册并开始使用应用。

7.3 Tyrant 认证宝石

为了实现整个认证系统,我们不使用 Devise ,而是选择 Tyrant 宝石。 Tyrant 具有所有常见的认证功能,如登录、注册、密码更改、暴力破解保护等。它以 Trailblazer 操作的形式提供功能,通过双胞胎暴露公共 API,并使用 Cells 实现视图。我们可以使用 Trailblazer 的多态性来定制验证、后处理逻辑和内部行为。

通过以上步骤,我们实现了文件上传的安全验证、优化了上传流程、添加了回调组来控制逻辑,同时建立了用户认证系统,为应用的安全性和用户体验提供了保障。

文件上传与认证系统的实现与优化

8. 按 ID 填充用户的详细分析

在按 ID 填充用户的过程中,我们使用了预填充器和填充器的组合。下面详细解释这两个部分的作用:
- 预填充器(Prepopulator) :在 Comment::Create 操作的合约中,预填充器 ->(*) { self.user = User.new } 确保在调用 prepopulate! 时,评论表单总是包含一个嵌套的 User 实例。这为后续的填充操作提供了基础。
- 填充器(Populator) :填充器 populate_user! 方法会在 validate 处理提交的表单时被调用。它会根据表单中提交的电子邮件地址查找现有的用户。如果找到,则将该用户与评论关联;如果未找到,则创建一个新用户。

以下是这个过程的表格总结:
| 步骤 | 操作 | 说明 |
| ---- | ---- | ---- |
| 1 | 预填充 | 使用预填充器创建一个新的 User 实例 |
| 2 | 填充 | 在 validate 时,根据电子邮件查找用户或创建新用户 |
| 3 | 关联 | 将找到或创建的用户与评论关联 |

9. 休眠用户的管理

休眠用户是指在系统中隐式创建,但账户尚未确认的用户。为了更好地管理这些用户,我们需要考虑以下几个方面:
- 通知机制 :当休眠用户被创建时,应该及时通知他们加入系统。可以通过电子邮件或其他方式发送通知,引导他们完成注册流程。
- 状态管理 :为休眠用户设置特定的状态,以便在系统中区分他们与已确认的用户。可以使用数据库字段来存储用户的状态信息。
- 清理机制 :定期清理长时间未确认的休眠用户,以减少系统中的无效数据。

以下是休眠用户管理的 mermaid 流程图:

graph LR
    A[用户隐式创建] --> B[发送通知]
    B --> C{用户是否确认}
    C -- 是 --> D[成为正式用户]
    C -- 否 --> E[定期清理]
10. Tyrant 宝石的使用

Tyrant 宝石为我们提供了一个强大的认证系统。以下是使用 Tyrant 实现常见认证功能的步骤:
1. 安装宝石 :在 Gemfile 中添加 gem 'tyrant' ,然后运行 bundle install 进行安装。
2. 配置 :根据项目需求,对 Tyrant 进行配置。可以在 config/initializers 目录下创建一个配置文件,例如 tyrant.rb ,并在其中设置相关参数。
3. 实现认证功能 :使用 Tyrant 提供的操作来实现登录、注册、密码更改等功能。例如,实现注册功能可以创建一个 User::Create 操作,并在其中使用 Tyrant 的相关方法。

以下是一个简单的注册操作示例:

class User::Create < Trailblazer::Operation
  include Tyrant::Authentication::SignUp

  contract do
    property :email
    property :password
    validates :email, presence: true
    validates :password, presence: true
  end

  def process(params)
    validate(params[:user]) do |f|
      f.save
    end
  end
end
11. 总结与展望

通过以上的实现和优化,我们成功地解决了文件上传的安全问题,优化了上传流程,并建立了一个完整的用户认证系统。文件验证确保了上传文件的安全性,回调组使代码更加清晰和可维护,而 Tyrant 宝石为我们提供了一个灵活的认证解决方案。

在未来的开发中,我们可以进一步扩展这些功能。例如,在文件上传方面,可以添加更多的文件处理功能,如图片压缩、水印添加等。在认证系统方面,可以实现更复杂的授权机制,如角色管理、权限控制等。同时,我们还可以对系统进行性能优化,提高系统的响应速度和稳定性。

总之,通过合理的设计和实现,我们可以构建一个安全、高效、易用的应用系统。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值