发布于 2026-01-06 3 阅读
0

使用 Ruby on Rails 和 Devise 实现魔法链接

使用 Ruby on Rails 和 Devise 实现魔法链接

密码对用户来说是个问题。一个好的做法是通过电子邮件发送一个账户访问链接,该链接包含一个令牌,我们将使用该令牌来验证用户身份,最终让用户登录到他们的账户。接下来,我将向您展示一个使用 Devise 的 Ruby on Rails 6 实现示例。

注意:为了避免出错,我将从零开始创建一个应用程序。

设置

创建项目:$ rails new magicLinks -T --skip-turbokinks --database=postgresql

正在安装依赖项……

将此添加到您的Gemfile.rb

gem 'devise', '~> 4.7', '>= 4.7.1'
group :development, :test do
  gem 'letter_opener', '~> 1.7' // For open emails in the browser
end
Enter fullscreen mode Exit fullscreen mode

然后,$ bundle install
我们来安装 Devise$ rails g devise:install && rails g devise User && rails db:create && rails db:migrate

config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
config.action_mailer.delivery_method = :letter_opener
Enter fullscreen mode Exit fullscreen mode

Devise 需要根路径,我们来添加:$ rails g controller welcome index
将此添加到您的app/config/routes.rb

root 'welcome#index'
Enter fullscreen mode Exit fullscreen mode

开始吧👊

首先,我们需要在数据库中创建一个表来保存生成的令牌。
所以,$ rails g model EmailLink token expires_at:datetime user:references && rails db:migrate

现在我们将生成令牌,并在发生这种情况时发送电子邮件,为此,请在以下位置app/models/email_link.rb

class EmailLink < ApplicationRecord
  belongs_to :user
  after_create :send_mail

  def self.generate(email)
    user = User.find_by(email: email)
    return nil if !user

    create(user: user, expires_at: Date.today + 1.day, token: generate_token)
  end

  def self.generate_token
    Devise.friendly_token.first(16)
  end

  private
  def send_mail
    EmailLinkMailer.sign_in_mail(self).deliver_now
  end
end
Enter fullscreen mode Exit fullscreen mode

让我们生成控制器,以便在用户提供电子邮件地址并提交表单时触发之前的回调函数rails g controller EmailLinks new create

让我们在代码中添加必要的路由app/config/routes.rb

root 'welcome#index'
get 'email_links/new', as: :new_magic_link
post 'email_links/create', as: :magic_link
get 'email_links/validate', as: :email_link
Enter fullscreen mode Exit fullscreen mode

现在,让我们为控制器添加功能:

class EmailLinksController < ApplicationController
  def new

  end

  def create
    @email_link = EmailLink.generate(params[:email])

    if @email_link
      flash[:notice] = "Email sent! Please, check your inbox."
      redirect_to root_path
    else
      flash[:alert] = "There was an error, please try again!"
      redirect_to new_magic_link_path
    end
  end

  def validate
    email_link = EmailLink.where(token: params[:token]).where("expires_at > ?", DateTime.now).first

    unless email_link
      flash[:alert] = "Invalid or expired token!"
      redirect_to new_magic_link_path
    end

    sign_in(email_link.user, scope: :user)
    redirect_to root_path
  end
end
Enter fullscreen mode Exit fullscreen mode

至此,我们的程序已经能够生成令牌、验证令牌并完成登录。剩下的工作就是发送电子邮件以及显示用户输入邮箱地址的页面。

注意:如果您想查看这些提示,必须将它们添加到app/views/layouts/application.html.erbbody 标签内:

<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
Enter fullscreen mode Exit fullscreen mode

发送电子邮件

很简单,首先我们要生成邮件程序,$ rails g mailer EmailLinkMailer然后赋予它相应的功能:

class EmailLinkMailer < ApplicationMailer
  def sign_in_mail(email_link)
    @token = email_link.token
    @user = email_link.user

    mail to: @user.email, subject: "Here is your magic link! 🚀"
  end
end
Enter fullscreen mode Exit fullscreen mode

让我们app/views/email_link_mailer/sign_in_mail.html.erb稍微修改一下邮件内容():

<p>Hello, <%= @user.email %>!</p>
<p>Recently someone requested a link to enter your account, if it was you, just press the button below to log in</p>

<%= link_to "Sign in to my account", email_link_url(token: @token) %>
Enter fullscreen mode Exit fullscreen mode

太棒了!目前我们的程序已经能够发送电子邮件了,我们只需要一个主界面,用户可以在主界面上输入他们的电子邮件地址,然后我们就可以启动所有后端程序了。

只需添加以下简单表单app/views/email_links/new.html.erb

<%= form_with(url: magic_link_path, method: :post) do %>
  <%= label_tag :email, "E-mail address" %>
  <%= email_field_tag :email, nil, placeholder:"carpintinimatias@gmail.com", autofocus: true, required: true %>
  <%= submit_tag "Send!" %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

为了验证这种方法是否有效,我们可以添加以下示例app/views/welcome/index.html.erb

<% if user_signed_in? %>
  <p>Hello, <%= current_user.email %></p>
  <%= link_to "Sign out", destroy_user_session_path, method: :delete %>
<% else %>
  <p>Hey, Sign In with Magic Links!</p>
  <%= link_to "Click here", new_magic_link_path %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

好了,就到这里。谢谢阅读。👋

文章来源:https://dev.to/matiascarpintini/magic-links-with-ruby-on-rails-and-devise-4e3o