Rails 环境变量实战

环境变量

应用程序中会牵涉到很多配置,有些配置又非常的隐私,像数据库连接地址用户名密码,如果应用本身在运行,同时又将源码开放,这时你就要考虑下如何保护这些数据(环境变量)的安全了。

操作系统也会提供一些系统/用户级别的变量定义,(如果做过Java开发,一定不会忘记JAVA_Home),在Rails App中我们可以继续使用系统提供的变量,也可以用App中的一些方法来定义变量。

数据库配置实例

下面是一个Rails中标准的数据库配置方案,可以在$rails_root/config/database.yml中找到。

1
2
3
4
5
6
7
8
9
production:
adapter: mysql2
encoding: utf8
reconnect: false
database: railsapp_dev
pool: 5
username: root
password: password
host: localhost

上面的做法是直接Hardcode username, password,这种作法是极为不推荐的,规范的公司里都应该有对应的事物权限,而Live数据库账号就是一个不应该被所有人看到的信息。

PS: 通常的作法是,将database.yml文件从source control中移除,给出一个示例配置,开发人员根据文档配置自己Dev Box。

系统环境变量配置

上面提到过,可以将这些隐私数据配置到操作系统的环境变量里(如 Linux的~/.bashrc),然后在Rails中通过ENV来读取。

1
2
3
4
production:
...
username: ENV["DATABASE_USERNAME"]
password: ENV["DATABASE__PASSWORD"]

这样,就将变量从Rails App中剥离出来了,增强了安全性。

自定义env.yml文件

创建config/env.yml配置文件

1
2
3
# For example, setting:
DATABASE_USERNAME: 'changeme'
DATABASE_PASSWORD: 'changeme'

将该文件从Source Control中移除,然后,在Rails App启动时,将该文件定义的值读出,放到ENV下,就可以在App中正常读取了。

加入方法,在config/application.rb 中找到下面一段代码:

1
2
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'

在该段代码后,加入以下代码:

1
2
3
4
5
6
config.before_configuration do
env_file = File.join(Rails.root, 'config', 'env.yml')
YAML.load(File.open(env_file)).each do |key, value|
ENV[key.to_s] = value
end if File.exists?(env_file)
end

然后就可以像上面示例一样用ENV["DATABASE_USERNAME"]来读取配置信息了。

第三方Gem

有很多Gem也可以做同样的事情,用过的有Figaro, Foreman

Figaro 默认会创建一个application.yml文件,并通过ENV来读取配置信息,示例:

configuration file:
1
2
3
4
5
6
7
# config/application.yml
pusher_app_id: "2954"
# 可以为不同的Rails Environment来配置
test:
pusher_app_id: "5112"
1
2
3
4
# config/initializers/pusher.rb
# 会根据当前运行的Rails Environment来自动读取
Pusher.app_id = ENV["pusher_app_id"]

secrets.yml in Rails 4.1

Rails 4.1中也终于对变量信息安全性重视起来了,默认加入了secret.yml文件,安全理念和上述一致。只是它并不是将变量添加到ENV中,而是被保存在Rails.application.secrets中。

添加一个新的wechat的配置,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 通过这样定义一个公用的wechat配置
wechat: &wechat
wechat_appid: 'wxaf7xxxxxxxxxx'
wechat_secret: '91aaxxxxxx5432643578456854'
wechat_token: 'wechat_token'
development:
#在具体的Rails Environment中引用这个配置
<<: *wechat
production:
<<: *wechat
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

在Rails App中可以通过这样来读取该变量值:

1
2
# 读取代码很长很丑
appid = Rails.application.secrets.wechat_appid

secrets.yml不支持Nested风格,有点小不爽。

REF::