You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker环境下Cucumber+Devise+远程Selenium Chrome登录失败问题

Docker环境下Cucumber测试Devise登录失败(401未授权)

背景

用rails-new在devcontainer搭建新系统,开发环境基于Docker。测试采用Docker化的Selenium Chromium,认证框架用Devise,BDD工具为Cucumber。

问题现象

手动登录功能正常,但执行Cucumber测试时登录失败。已确认测试中用户创建正确,first_user.valid_password?(password)返回true,但Devise返回401未授权。日志显示邮箱和密码参数传递正确,推测问题与CSRF令牌或会话相关。

相关配置代码

1. features/support/config.rb

require "capybara/cucumber"
require "selenium-webdriver"

Capybara.register_driver :selenium_remote_chrome do |app|
  options = Selenium::WebDriver::Chrome::Options.new
  options.add_argument("--window-size=1400,1400")
  options.add_argument("--no-sandbox")
  options.add_argument("--disable-dev-shm-usage")
  Capybara::Selenium::Driver.new(
    app,
    browser: :remote,
    url: "http://#{ENV.fetch("SELENIUM_HOST")}:4444/wd/hub",
    options: options
  )
end

Capybara.configure do |config|
  config.run_server = true
  config.server_port = ENV.fetch("CAPYBARA_SERVER_PORT", 3015).to_i
  config.server_host = "0.0.0.0"
  config.default_driver = :selenium_remote_chrome
  config.app_host = "http://rails-app:#{config.server_port}"
end

2. Feature文件

Background:
    Given a user exists with email "user@example.com" and password "password123"

  Rule: A user can login with the correct email and password

    Scenario: User logs in successfully
      When I go to the login page
      And I fill in "Email" with "user@example.com"
      And I fill in "Password" with "password123"
      And I press "Log in" 
      And I wait for the page to load
      Then I should see "Dashboard"
      And I should see "Log out"

3. 测试步骤代码

Given("a user exists with email {string} and password {string}") do |email, password|
  User.create!(email: email, password: password, password_confirmation: password)
  expect(User.count).to eq(1) # Ensure that the user was created
  u = User.first
  expect(u.valid_password?(password)).to be true # Ensure that the password is correct
end

When("I go to the login page") do
  visit new_user_session_path
end

When("I fill in {string} with {string}") do |field, value|
  fill_in field, with: value
end

When("I press {string}") do |button|
  click_button button
end

Then("I should see {string}") do |text|
  expect(page).to have_content(text)
end

And("I wait for the page to load") do
  sleep 1
end

And("I wait for {int} seconds") do |seconds|
  sleep seconds
end

日志信息

Completed 401 Unauthorized in 2ms (ActiveRecord: 0.2ms (1 query, 0 cached) | GC: 0.0ms)
Processing by Devise::SessionsController#new as TURBO_STREAM
  Parameters: {"user"=>{"email"=>"user@example.com", "password"=>"password123"}, "commit"=>"Log in"}
  Rendering layout layouts/application.html.haml
  Rendering devise/sessions/new.html.haml within layouts/application
  Rendered devise/shared/_links.html.haml (Duration: 0.3ms | GC: 0.0ms)
  Rendered devise/sessions/new.html.haml within layouts/application (Duration: 2.5ms | GC: 0.0ms)
  Rendered layout layouts/application.html.haml (Duration: 3.3ms | GC: 0.0ms)

注:已移除日志中的邮箱和密码过滤,可确认参数传入正确。手动登录正常,求问问题原因及解决方案?

问题原因及解决方案

原因1:CSRF令牌验证不通过

Docker环境中,Selenium容器与Rails容器的主机名/域名不一致,导致浏览器存储的CSRF令牌对应的Origin与Rails服务器接收的请求Origin不匹配,Devise验证CSRF失败。

解决方法:
修改config/environments/test.rb,添加允许的主机:

Rails.application.configure do
  # 其他配置...
  config.hosts << "rails-app" # 对应Capybara的app_host中的主机名
end

原因2:Turbo Stream请求的CSRF处理问题

日志显示请求为as TURBO_STREAM,若登录表单未正确包含Turbo所需的CSRF元标签,或Capybara未正确处理Turbo请求方式,会导致CSRF验证失败。

解决方法:

  1. 确认登录页面布局包含Turbo的CSRF元标签(Rails 7+默认已包含):
# 在layouts/application.html.haml中
%meta{ name: "csrf-token", content: form_authenticity_token }
  1. 或在测试中禁用Turbo提交,修改点击按钮的步骤:
When("I press {string}") do |button|
  find_button(button).set_attribute("data-turbo", "false")
  click_button button
end

原因3:会话存储跨容器配置问题

Docker容器间使用Cookie存储会话时,若Cookie的domain设置不正确,会导致会话无法共享。

解决方法:
修改config/environments/test.rb中的会话配置:

Rails.application.configure do
  # 其他配置...
  config.session_store :cookie_store, key: '_your_app_session', domain: :all
end

原因4:容器网络解析问题

Selenium容器无法正确解析Rails容器的主机名rails-app,导致请求无法正常携带会话信息。

解决方法:
检查Docker Compose网络配置,确保Selenium容器与Rails容器在同一网络中;在Selenium容器中执行ping rails-app验证解析是否正常,若失败需调整网络别名配置。


内容的提问来源于stack exchange,提问作者John Small

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 14:17:33