如何在Rails中根据应用用户配置从数据库获取的发件邮箱凭据
Hey there! Let's walk through how to set up per-user email sending credentials in your Rails app—pulling directly from the database instead of relying on global configs. This approach gives each user their own unique email setup, which is exactly what you're looking for. Here's a step-by-step guide that's proven to work:
First, you need a place to store each user's SMTP details. You can either add columns directly to your users table or create a separate email_credentials table (better if you might need multiple credentials per user later).
For a simple start, let's add columns to the users table:
# Generate a migration rails generate migration AddEmailCredentialsToUsers smtp_address:string smtp_port:integer smtp_username:string smtp_password:string smtp_authentication:string smtp_enable_starttls_auto:boolean # Run the migration rails db:migrate
Tweak the columns based on your email provider's requirements (add smtp_domain for SendGrid, for example).
Rails lets you define custom email delivery methods, which is perfect for pulling per-user credentials. Create an initializer like config/initializers/per_user_smtp.rb:
class PerUserSmtpDelivery def initialize(options = {}) # No global settings needed here—we'll pass the user at delivery time end def deliver!(mail) # Fetch the user we attached to the mail object user = mail.instance_variable_get(:@user) raise "No user specified for per-user email delivery" unless user # Map the user's stored credentials to SMTP settings smtp_config = { address: user.smtp_address, port: user.smtp_port, user_name: user.smtp_username, password: user.smtp_password, authentication: user.smtp_authentication&.to_sym || :plain, enable_starttls_auto: user.smtp_enable_starttls_auto || true } # Use Rails' built-in SMTP to send the email Net::SMTP.start( smtp_config[:address], smtp_config[:port], 'localhost', # Use your server's hostname if required by the provider smtp_config[:user_name], smtp_config[:password], smtp_config[:authentication] ) do |smtp| smtp.send_message(mail.encoded, mail.from.first, mail.to) end end end # Register the custom method with Rails ActionMailer::Base.add_delivery_method :per_user_smtp, PerUserSmtpDelivery
In your mailer (e.g., app/mailers/user_mailer.rb), attach the user to the mail object and switch to the custom delivery method:
class UserMailer < ApplicationMailer def welcome_email(user) @user = user # Attach the user so our delivery method can access their credentials mail.instance_variable_set(:@user, user) # Send using the per-user SMTP method mail( to: user.email, from: user.smtp_username, # Or a custom from address tied to the user subject: "Welcome aboard!", delivery_method: :per_user_smtp ) end end
If all emails from this mailer should use per-user credentials, set it as the default:
class UserMailer < ApplicationMailer default delivery_method: :per_user_smtp def welcome_email(user) @user = user mail.instance_variable_set(:@user, user) mail(to: user.email, from: user.smtp_username, subject: "Welcome aboard!") end end
Don't skip these critical steps:
- Encrypt passwords: Never store plaintext SMTP passwords! Use Rails' encrypted attributes to protect the
smtp_passwordcolumn:
(Make sure you've set up encryption keys in your Rails app first—check the Rails docs if you haven't done this.)# In app/models/user.rb encrypts :smtp_password - Validate credentials: Add validations to ensure users provide complete, valid SMTP details:
# In app/models/user.rb validates :smtp_address, :smtp_port, :smtp_username, :smtp_password, presence: true validates :smtp_port, numericality: { only_integer: true, greater_than: 0 } - Catch errors: Add rescue blocks to handle SMTP failures (invalid credentials, connection issues) and log/notify appropriately:
# Inside PerUserSmtpDelivery#deliver! begin Net::SMTP.start(...) do |smtp| smtp.send_message(mail.encoded, mail.from.first, mail.to) end rescue Net::SMTPAuthenticationError => e Rails.logger.error "SMTP auth failed for user #{user.id}: #{e.message}" raise EmailDeliveryError, "Invalid email credentials" rescue => e Rails.logger.error "Email delivery failed for user #{user.id}: #{e.message}" raise EmailDeliveryError, "Could not send email" end
Create a test user with valid SMTP credentials (use a service like Mailtrap for safe testing):
user = User.create( email: "test@example.com", smtp_address: "smtp.mailtrap.io", smtp_port: 2525, smtp_username: "your_mailtrap_username", smtp_password: "your_mailtrap_password", smtp_authentication: "plain", smtp_enable_starttls_auto: true ) UserMailer.welcome_email(user).deliver_now
Check your Mailtrap inbox or provider logs to confirm the email was sent using the user's credentials.
内容的提问来源于stack exchange,提问作者mamatha

