Rails框架下基于Metamask的Web3签名注册/登录API实现:Devise支持性及自定义验证方案咨询
很棒的问题!Devise本身并没有内置Web3签名登录的功能,但它的扩展性极强,完全可以通过自定义配置和逻辑来实现你想要的Web3签名注册/登录流程。下面一步步拆解具体怎么做:
1. 禁用用户模型的密码字段
首先,我们需要修改Devise的用户模型,移除和密码相关的验证及模块,同时调整数据库结构:
- 修改用户模型:打开
app/models/user.rb,去掉devise方法里的:database_authenticatable模块(这个是处理密码验证的核心模块),同时禁用密码相关的默认验证:
class User < ApplicationRecord # 保留你需要的Devise模块,比如:registerable、:trackable,去掉:database_authenticatable devise :registerable, :trackable, :validatable # 注意:如果保留:validatable,需要自定义验证逻辑,避免默认的密码校验 # 禁用密码字段的必填验证 validates :password, presence: false, allow_nil: true end
- 调整数据库结构:如果已经生成了包含
password_digest字段的迁移,需要创建一个新迁移来移除它:
# 生成迁移文件:rails generate migration RemovePasswordDigestFromUsers class RemovePasswordDigestFromUsers < ActiveRecord::Migration[7.0] def change remove_column :users, :password_digest, :string end end
运行迁移:rails db:migrate
2. 自定义Web3签名的登录验证逻辑
接下来我们要实现“请求随机Nonce → 钱包签名 → 后端校验签名”的Web3登录流程:
第一步:添加钱包地址字段
首先给用户模型添加wallet_address字段,用来存储用户的Web3钱包地址:
# 生成迁移文件:rails generate migration AddWalletAddressToUsers class AddWalletAddressToUsers < ActiveRecord::Migration[7.0] def change add_column :users, :wallet_address, :string, unique: true, null: false end end
运行迁移:rails db:migrate
然后在用户模型里添加钱包地址的验证,以及生成Nonce、校验签名的方法:
class User < ApplicationRecord # ... 之前的Devise配置 # 验证钱包地址格式(以太坊地址为例) validates :wallet_address, presence: true, uniqueness: true, format: { with: /\A0x[a-fA-F0-9]{40}\z/ } # 生成随机Nonce(防止重放攻击) def generate_nonce update!(nonce: SecureRandom.hex(16)) nonce end # 校验Web3签名是否有效(这里以以太坊为例,使用ethereum.rb库) def valid_signature?(signature) # 构造签名消息,必须和前端保持一致 message = "请签名此消息完成验证,Nonce: #{nonce}" # 从签名中恢复钱包地址 recovered_address = Ethereum::Signature.recover(message, signature) # 对比恢复的地址和用户存储的地址(统一转小写避免大小写问题) recovered_address.downcase == wallet_address.downcase end end
第二步:添加Web3登录的控制器和路由
创建一个专门处理Web3登录的控制器Web3SessionsController:
class Web3SessionsController < ApplicationController skip_before_action :authenticate_user! # 前端请求获取随机Nonce def nonce wallet_address = params[:wallet_address] # 找到或创建用户(自动注册逻辑,可根据需求调整) user = User.find_or_initialize_by(wallet_address: wallet_address) user.save! if user.new_record? render json: { nonce: user.generate_nonce } end # 校验签名并完成登录 def create user = User.find_by(wallet_address: params[:wallet_address]) if user&.valid_signature?(params[:signature]) # 使用Devise的sign_in方法登录用户 sign_in(user, scope: :user) render json: { success: true, user: user } else render json: { error: "签名或钱包地址无效" }, status: :unauthorized end end end
然后在config/routes.rb里添加对应的路由:
Rails.application.routes.draw do devise_for :users # Web3登录相关路由 post '/web3/nonce', to: 'web3_sessions#nonce' post '/web3/login', to: 'web3_sessions#create' end
第三步:添加Web3依赖库
我们需要用到Web3相关的Ruby库来处理签名校验,比如ethereum.rb,在Gemfile里添加:
gem 'ethereum.rb'
然后运行bundle install安装依赖。
3. 额外注意事项
- 如果保留了Devise的
:validatable模块,建议自定义验证规则,避免它默认的密码校验逻辑干扰。 - Nonce使用后可以重置,或者设置过期时间,进一步提升安全性。
- 签名消息的内容必须前后端完全一致,否则校验会失败,建议把消息内容抽成常量统一管理。
内容的提问来源于stack exchange,提问作者Sami Fakhfakh
相关产品推荐
相关产品推荐

