Problema
Um usuário precisa acessar um conteúdo estático protegido (por exemplo, imagem) que deve ser servido em https por um servidor web (para uma velocidade de download muito mais rápida) somente após a solicitação ter sido autenticada com sucesso e autorizada por um servidor Rails.
A configuração
Vamos supor o seguinte:
- Nginx : nginx está sendo executado como um servidor proxy reverso e da web. Todas as solicitações para o servidor Rails são passadas para a porta 3000 (ou seja, a porta padrão para o servidor Rails)
- Thin : servidor rails em execução na porta 3000
- Diretório Rails : o diretório raiz do projeto rails está localizado em
/var/rails/rails_app
- Diretório onde os recursos estáticos protegidos são chamados :
/var/rails/rails_app/images_fs_dir
- URL para acessar os recursos estáticos protegidos :
http://myhost.com/images_uri_dir
Solução
Por que usar o nginx para servir o conteúdo estático?
Porque o nginx é 5-20x mais rápido do que o thin quando se trata de servir conteúdo estático.
Fluxo lógico:
- O pedido é feito para
http://myhost.com/images_uri_dir/companyA/bar_chart.png
- A solicitação é redirecionada para
https://myhost.com/images_uri_dir/companyA/bar_chart.png
(consulte reescrever ^ https: // $ host $ request_uri permanent) - O Nginx recebe a solicitação, combina o caminho da solicitação com suas regras de mapeamento em nginx.conf, adiciona cabeçalhos HTTP X-Accel-Redirect e X-Accel-Mapping e envia a solicitação para
http://localhost:3000/images_uri_dir/companyA/bar_chart.png
(consulte proxy_pass em nginx.conf) - Thin recebe a solicitação, procura
routes.rb
e encaminha a solicitação paraImagesController#show
- O método before_filter no controlador realiza a autenticação e autorização apropriadas
- config / environment / production.rb diz ao servidor Rails para procurar por X-Accel-Redirect e X-Accel-Mapping em uma solicitação
- O Rails compara o caminho do arquivo que é passado para enviar o arquivo com o que é definido no lado esquerdo do valor do X-Accel-Mapping (ex. / Var / rails / rails app / = / images fs dir /). Se os 2 valores corresponderem, o controlador passa a solicitação de volta ao nginx. Caso contrário, o controlador irá enviar o arquivo de volta para o cliente
- O Nginx recebe a solicitação e olha para o lado direito do valor do X-Accel-Mapping (ou seja, / var / rails / rails app / = / images fs_dir /). Como a solicitação é interna ao host, o nginx carrega e envia o arquivo de volta ao cliente (consulte interno em nginx.conf)
O diretório raiz do projeto Rails:
$ cd /var/rails/rails_app
$ ls
images_fs_dir
$ ls -R # List files recursively
.:
customerA customerB
./customerA
bar_chart.png
pie_chart.png
./customerB
bar_chart.png
pie_chart.png
...
O arquivo nginx deve ser semelhante a este:
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
index index.html index.htm;
default_type application/octet-stream;
upstream rails {
server localhost:3000;
}
server {
listen 80;
server_name localhost;
# Direct all http requests to https.
location / {
rewrite ^ https://$host$request_uri permanent;
}
}
server {
listen 443 default_server ssl;
server_name localhost;
root /var/rails/rails_app;
ssl on;
ssl_certificate /etc/ssl/certs/my_cert.crt;
ssl_certificate_key /etc/ssl/keys/my_key.key;
# Protected directory
# Note: rails will handle /images_uri_dir requests
location /images_fs_dir/ {
alias /var/rails/rails_app/; # Append the path with /
internal; # Can't access this directory from direct access from the web
}
location / {
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Sendfile-Type X-Accel-Redirect;
proxy_set_header X-Accel-Mapping /var/rails/rails_app/=/images_fs_dir/;
# Make sure you open and close your X-Accel-Mapping value with /
proxy_pass http://rails;
}
}
}
config / routes.rb deve ser assim:
# ...
get '/images_uri_dir/:customer_id/:category.png' => 'images#show'
#...
config / environment / production.rb deve ter a seguinte aparência:
# ...
# This redirects the request back to nginx to load and serve the static content after a success authentication/authorization
RailsApp::Application.configure do
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
config.middleware.insert(0, Rack::Sendfile, config.action_dispatch.x_sendfile_header)
# ...
app / controllers / images_controller.rb
# ...
before_filter :validate_user
def validate_user
# Authentication and authorization logic here
end
def show
file_path = "#{Rails.root}/images_fs_dir/#{customer_id}/#{category}.png"
send_file(file_path, type: 'image/png', disposition: 'inline')
end
def images_params
params.permit(:customer_id, :category)
end
# ...