为什么我在Rails应用程序中使用force_ssl出现无限重定向循环? [英] Why am I getting infinite redirect loop with force_ssl in my Rails app?

查看:68
本文介绍了为什么我在Rails应用程序中使用force_ssl出现无限重定向循环?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想让我的API控制器使用SSL,所以我在我的nginx.conf中添加了另一个监听指令

upstream unicorn {
  server unix:/tmp/unicorn.foo.sock fail_timeout=0;
}

server {
  listen 80 default deferred;
  listen 443 ssl default;
  ssl_certificate /etc/ssl/certs/foo.crt;
  ssl_certificate_key /etc/ssl/private/foo.key;

  server_name foo;
  root /var/apps/foo/current/public;

  try_files $uri/system/maintenance.html $uri/index.html $uri @unicorn;

  location @unicorn {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://unicorn;
  }

  error_page 502 503 /maintenance.html;
  error_page 500 504 /500.html;
  keepalive_timeout 5;
}

顺利通过nginx竞赛.我还向我的ApiController添加了force_ssl指令

class ApiController < ApplicationController
  force_ssl if Rails.env.production?

  def auth
    user = User.authenticate(params[:username], params[:password])
    respond_to do |format|
      format.json do
        if user
          user.generate_api_key! unless user.api_key.present?
          render json: { key: user.api_key }
        else
          render json: { error: 401 }, status: 401
        end
      end
    end
  end

  def check
    user = User.find_by_api_key(params[:api_key])
    respond_to do |format|
      format.json do
        if user
          render json: { status: 'ok' }
        else
          render json: { status: 'failure' }, status: 401
        end
      end
    end
  end
end

当我不使用SSL时,它工作得很好,但是现在当我尝试使用curl --LI http://foo/api/auth.json时,可以正确地重定向到https,但是随后我继续重定向到http://foo/api/auth,以无限重定向结束循环.

我的路线只是

get "api/auth"
get "api/check"

我正在Ruby 1.9.2和Nginx 0.7.65上使用Rails 3.2.1

解决方案

您没有转发有关此请求是否为HTTPS终止请求的任何信息.通常,在服务器中,"ssl on";指令将设置这些标头,但您使用的是组合块.

机架(和force_ssl)通过以下方式确定SSL:

  • 如果请求是通过端口443传入的(这很可能不会从nginx传递回Unicorn)
  • 如果ENV ['HTTPS'] =="on"
  • 如果X-Forwarded-Proto标头=="HTTPS"

有关完整故事,请参见 force_ssl来源. /p>

由于您正在使用组合块,因此您想使用第三种形式.试试:

proxy_set_header X-Forwarded-Proto $scheme;

在服务器或位置阻止中根据nginx文档.

当您进入端口80请求时,会将标头设置为"http",而当您进入443请求时,将标头设置为"https".

I want to have my API controller use SSL, so I added another listen directive to my nginx.conf

upstream unicorn {
  server unix:/tmp/unicorn.foo.sock fail_timeout=0;
}

server {
  listen 80 default deferred;
  listen 443 ssl default;
  ssl_certificate /etc/ssl/certs/foo.crt;
  ssl_certificate_key /etc/ssl/private/foo.key;

  server_name foo;
  root /var/apps/foo/current/public;

  try_files $uri/system/maintenance.html $uri/index.html $uri @unicorn;

  location @unicorn {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://unicorn;
  }

  error_page 502 503 /maintenance.html;
  error_page 500 504 /500.html;
  keepalive_timeout 5;
}

which passes the nginx conftest without any problems. I also added a force_ssl directive to my ApiController

class ApiController < ApplicationController
  force_ssl if Rails.env.production?

  def auth
    user = User.authenticate(params[:username], params[:password])
    respond_to do |format|
      format.json do
        if user
          user.generate_api_key! unless user.api_key.present?
          render json: { key: user.api_key }
        else
          render json: { error: 401 }, status: 401
        end
      end
    end
  end

  def check
    user = User.find_by_api_key(params[:api_key])
    respond_to do |format|
      format.json do
        if user
          render json: { status: 'ok' }
        else
          render json: { status: 'failure' }, status: 401
        end
      end
    end
  end
end

which worked just fine when I wasn't using SSL, but now when I try to curl --LI http://foo/api/auth.json, I get properly redirected to https, but then I keep on getting redirected to http://foo/api/auth ending in an infinite redirect loop.

My routes simply have

get "api/auth"
get "api/check"

I'm using Rails 3.2.1 on Ruby 1.9.2 with nginx 0.7.65

解决方案

You're not forwarding any information about whether this request was an HTTPS-terminated request or not. Normally, in a server, the "ssl on;" directive will set these headers, but you're using a combined block.

Rack (and force_ssl) determines SSL by:

  • If the request came in on port 443 (this is likely not being passed back to Unicorn from nginx)
  • If ENV['HTTPS'] == "on"
  • If the X-Forwarded-Proto header == "HTTPS"

See the force_ssl source for the full story.

Since you're using a combined block, you want to use the third form. Try:

proxy_set_header X-Forwarded-Proto $scheme;

in your server or location block per the nginx documentation.

This will set the header to "http" when you come in on a port 80 request, and set it to "https" when you come in on a 443 request.

这篇关于为什么我在Rails应用程序中使用force_ssl出现无限重定向循环?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆