引言
Flask初探一(Flask 各參數(shù)的應(yīng)用) 中提到了隱藏重要配置( 敏感配置) 的方式, 今天詳細(xì)研究一下怎么樣實(shí)現(xiàn).
創(chuàng)建項目
項目結(jié)構(gòu)
main.py
假設(shè)config_private 目錄下的config_private.py是要隱藏的配置文件
導(dǎo)入模板 flask_migrate, flask_redis, flask_script, flask_session, flask_sqlalchemy
main.py
# 配置
from flask import Flask, session
from flask_migrate import Migrate, MigrateCommand
from flask_redis import redis
from flask_script import Manager
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
# app = Flask(__name__,
# instance_path="E:\\workspace\\pycharm\\demo\\config_private",
# instance_relative_config=True)
app = Flask(__name__)
class Config(object):
# debug
DEBUG = False
# SQLAlchemy
SQLALCHEMY_DATABASE_URI = "mysql://root:fangfang@127.0.0.1:3306/fang"
SQLALCHEMY_TRACK_MODIFICATIONS = False
# redis
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
REDIS_DB = 0
# session
SECRET_KEY = "EjpNVSNQTyGi1VvWECj9TvC/+kq3oujee2kTfQUs8yCM6xX9Yjq52v54g+HVoknA"
SESSION_TYPE = "redis" # 指定 session 保存到 redis 中
SESSION_USE_SIGNER = True # 讓 cookie 中的 session_id 被加密簽名處理
PERMANENT_SESSION_LIFETIME = 60 * 60 * 24 * 7 # session 的有效期,單位是秒
app.config.from_object(Config)
redis = redis.StrictRedis(host=Config.REDIS_HOST,
port=Config.REDIS_PORT,
db=Config.REDIS_DB)
db_SQLAlchemy = SQLAlchemy(app)
Migrate(app, db_SQLAlchemy)
Session(app)
manager = Manager(app)
manager.add_command("db", MigrateCommand)
@app.route('/')
def index():
session["aa"] = "aa"
return 'index'
if __name__ == '__main__':
manager.run()
隱藏敏感信息
配置完成, 可以正常運(yùn)行. 里面有一些賬號密碼 等敏感信息,不想公開, 怎么辦?
首先, 將配置信息抽取到config_private.py 文件, 結(jié)合 配制的最佳實(shí)踐[1]
config_private.py
class Config(object):
# debug
DEBUG = False
# SQLAlchemy
SQLALCHEMY_DATABASE_URI = "mysql://root:fangfang@127.0.0.1:3306/fang"
SQLALCHEMY_TRACK_MODIFICATIONS = False
# redis
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
REDIS_DB = 0
# session
SECRET_KEY = "EjpNVSNQTyGi1VvWECj9TvC/+kq3oujee2kTfQUs8yCM6xX9Yjq52v54g+HVoknA"
SESSION_TYPE = "redis" # 指定 session 保存到 redis 中
SESSION_USE_SIGNER = True # 讓 cookie 中的 session_id 被加密簽名處理
PERMANENT_SESSION_LIFETIME = 60 * 60 * 24 * 7 # session 的有效期,單位是秒
class ProductionConfig(Config):
DEBUG = False
# redis
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
REDIS_DB = 0
class DevelopmentConfig(Config):
DEBUG = True
# redis
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
REDIS_DB = 0
配置信息抽取完成
從文件加載配置信息
實(shí)驗(yàn)
# 配置
from flask import Flask, session
from flask_migrate import Migrate, MigrateCommand
from flask_redis import redis
from flask_script import Manager
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
# app = Flask(__name__,
# instance_path="E:\\workspace\\pycharm\\demo\\config_private",
# instance_relative_config=True)
app = Flask(__name__)
app.config.from_pyfile("config_private.py")
redis = redis.StrictRedis(host=Config.REDIS_HOST,
port=Config.REDIS_PORT,
db=Config.REDIS_DB)
db_SQLAlchemy = SQLAlchemy(app)
Migrate(app, db_SQLAlchemy)
Session(app)
manager = Manager(app)
manager.add_command("db", MigrateCommand)
@app.route('/')
def index():
session["aa"] = "aa"
return 'index'
if __name__ == '__main__':
manager.run()
實(shí)驗(yàn)結(jié)果
Traceback (most recent call last):
File "E:/workspace/pycharm/demo/main.py", line 14, in <module>
app.config.from_pyfile("config_private.py")
File "D:\software\Python\lib\site-packages\flask\config.py", line 128, in from_pyfile
with open(filename,encoding="UTF-8") as config_file:
FileNotFoundError: [Errno 2] Unable to load configuration file (No such file or directory): 'E:\\workspace\\pycharm\\demo\\config_private\\config_private.py'
沒有指定文件或文件夾?instance_path 和 instance_relative_config 登場
instance_path 和 instance_relative_config
main.py
# 配置
from flask import Flask, session
from flask_migrate import Migrate, MigrateCommand
from flask_redis import redis
from flask_script import Manager
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__,
instance_path="E:\\workspace\\pycharm\\demo\\config_private",
instance_relative_config=True)
# app = Flask(__name__)
app.config.from_pyfile("config_private.py")
# 屏蔽一下,Config 類被抽取到config_private.py 文件了
# redis = redis.StrictRedis(host=Config.REDIS_HOST,
# port=Config.REDIS_PORT,
# db=Config.REDIS_DB)
db_SQLAlchemy = SQLAlchemy(app)
Migrate(app, db_SQLAlchemy)
Session(app)
manager = Manager(app)
manager.add_command("db", MigrateCommand)
@app.route('/')
def index():
session["aa"] = "aa"
return 'index'
if __name__ == '__main__':
manager.run()
實(shí)驗(yàn)結(jié)果
E:\workspace\pycharm\demo\venv\Scripts\python.exe E:/workspace/pycharm/demo/main.py runserver
D:\software\Python\lib\site-packages\flask_sqlalchemy\__init__.py:774: UserWarning: Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. Defaulting SQLALCHEMY_DATABASE_URI to "sqlite:///:memory:".
'Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. '
D:\software\Python\lib\site-packages\flask_sqlalchemy\__init__.py:794: FSADeprecationWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future. Set it to True or False to suppress this warning.
'SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and '
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
什么情況? 配制不正確了?! 為什么呢?修改一下配置文件, 只保留一個配置
config_private.py
DEBUG = True
實(shí)驗(yàn)結(jié)果
* Restarting with stat
* Debugger is active!
* Debugger PIN: 213-569-008
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Debugger is active! 說明我們從文件引入配置是沒問題的, 那么問題在哪? 問題在于from_object , 都沒有出現(xiàn)過from_object, 為什么說問題出在他身上?
from_pyfile 源碼
def from_pyfile(self, filename, silent=False):
filename = os.path.join(self.root_path, filename)
d = imp.new_module('config')
d.__file__ = filename
try:
with open(filename,encoding="UTF-8") as config_file:
exec(compile(config_file.read(), filename, 'exec'), d.__dict__)
except IOError as e:
if silent and e.errno in (errno.ENOENT, errno.EISDIR):
return False
e.strerror = 'Unable to load configuration file (%s)' % e.strerror
raise
# 重點(diǎn)在這, from_pyfile 的本質(zhì)也是通過from_object 添加配置信息
self.from_object(d)
return True
那么from_object 的問題是什么?
from_object 源碼
def from_object(self, obj):
if isinstance(obj, string_types):
obj = import_string(obj)
for key in dir(obj):
# 重點(diǎn)在這
if key.isupper():
self[key] = getattr(obj, key)
key.isupper() , 鍵是全大寫嗎? 是就加入app.config 字典, 從這里可以看出來, 文件中的配置信息必須大寫, 才能被添加到app.config 字典中, 不然, 你只能修改這句代碼了.
實(shí)驗(yàn)
config_private.py
# 如果使用類, 請將類名關(guān)不大寫, 不然需要改變源碼,才能將屬性放到app.config 字典中
class CONFIGPRIVATE(object):
# SQLAlchemy
SQLALCHEMY_DATABASE_URI = "mysql://root:fangfang@127.0.0.1:3306" # TODO 敏感信息
SQLALCHEMY_TRACK_MODIFICATIONS = False
# Redis # TODO 敏感信息
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
REDIS_DB = 0
# Session
"""
import base64,os
data = os.urandom(32)
print(base64.b64encode(data).decode("utf-8"))
"""
SECRET_KEY = "GwjkfXdUOa8bt4WwF0FgSbtCbbv9Wc8Aeny/SMEQ1+L5q/svnQhWubMOPITa1JU+ptprYf1RoFGys0Qana7XoQ==" # TODO 敏感信息
SESSION_TYPE = "redis" # 指定 session 保存到 redis 中
SESSION_USE_SIGNER = True # 讓 cookie 中的 session_id 被加密簽名處理
PERMANENT_SESSION_LIFETIME = 60 * 60 * 24 * 7 # session 的有效期,單位是秒
class PRODUCTIONCONFIG(CONFIGPRIVATE):
# redis
REDIS_HOST = "192.168.70.45"
REDIS_PORT = 6379
REDIS_DB = 5
class DEVELOPMENTCONFIG(CONFIGPRIVATE):
# redis
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
REDIS_DB = 1
class TESTINGCONFIG(CONFIGPRIVATE):
# redis
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
REDIS_DB = 3
main.py
# 配置
from flask import Flask, session
from flask_migrate import MigrateCommand, Migrate
from flask_script import Manager
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__,
instance_path="E:\\workspace\\pycharm\\demo\\config_private",
instance_relative_config=True)
old_config = app.config
app.config.from_pyfile("config_private.py")
# redis = redis.StrictRedis(host=Config.REDIS_HOST,
# port=Config.REDIS_PORT,
# db=Config.REDIS_DB)
db_SQLAlchemy = SQLAlchemy(app)
Migrate(app, db_SQLAlchemy)
Session(app)
manager = Manager(app)
manager.add_command("db", MigrateCommand)
@app.route('/')
def index():
print(old_config)
print(app.config)
return 'index'
if __name__ == '__main__':
app.run()
實(shí)驗(yàn)結(jié)果
D:\software\Python\lib\site-packages\flask_sqlalchemy\__init__.py:774: UserWarning: Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. Defaulting SQLALCHEMY_DATABASE_URI to "sqlite:///:memory:".
<Config {'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31), 'USE_X_SENDFILE': False, 'LOGGER_NAME': '__main__', 'SERVER_NAME': None, 'APPLICATION_ROOT': None, 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': 43200, 'TRAP_BAD_REQUEST_ERRORS': False, 'TRAP_HTTP_EXCEPTIONS': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': True}>
'Neither SQLALCHEMY_DATABASE_URI nor SQLALCHEMY_BINDS is set. '
D:\software\Python\lib\site-packages\flask_sqlalchemy\__init__.py:794: FSADeprecationWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future. Set it to True or False to suppress this warning.
'SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and '
<Config {'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31), 'USE_X_SENDFILE': False, 'LOGGER_NAME': '__main__', 'SERVER_NAME': None, 'APPLICATION_ROOT': None, 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': 43200, 'TRAP_BAD_REQUEST_ERRORS': False, 'TRAP_HTTP_EXCEPTIONS': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': True, 'CONFIGPRIVATE': <class 'config.CONFIGPRIVATE'>, 'DEVELOPMENTCONFIG': <class 'config.DEVELOPMENTCONFIG'>, 'PRODUCTIONCONFIG': <class 'config.PRODUCTIONCONFIG'>, 'TESTINGCONFIG': <class 'config.TESTINGCONFIG'>}>
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
雖然失敗了, 但是可以看出兩次的config 字典不同
正確姿勢
言歸正傳,怎樣正確隱藏敏感配置, 如果要使用文件并且通過類進(jìn)行配置, 略微有些麻煩. 至于不使用文件不使用類的方式這里不做討論, 配置DEBUG 屬性就是一個使用文件,不使用類的最簡單的例子
config_private.py
# 如果使用類, 請將類名關(guān)不大寫, 不然需要改變源碼,才能將屬性放到app.config 字典中
class CONFIGPRIVATE(object):
DEBUG = False
# SQLAlchemy
SQLALCHEMY_DATABASE_URI = "mysql://root:fangfang@127.0.0.1:3306" # TODO 敏感信息
SQLALCHEMY_TRACK_MODIFICATIONS = False
# Redis # TODO 敏感信息
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
REDIS_DB = 0
# Session
"""
import base64,os
data = os.urandom(32)
print(base64.b64encode(data).decode("utf-8"))
"""
SECRET_KEY = "GwjkfXdUOa8bt4WwF0FgSbtCbbv9Wc8Aeny/SMEQ1+L5q/svnQhWubMOPITa1JU+ptprYf1RoFGys0Qana7XoQ==" # TODO 敏感信息
SESSION_TYPE = "redis" # 指定 session 保存到 redis 中
SESSION_USE_SIGNER = True # 讓 cookie 中的 session_id 被加密簽名處理
PERMANENT_SESSION_LIFETIME = 60 * 60 * 24 * 7 # session 的有效期,單位是秒
class PRODUCTIONCONFIG(CONFIGPRIVATE):
DEBUG = False
# redis
REDIS_HOST = "192.168.70.45"
REDIS_PORT = 6379
REDIS_DB = 5
class DEVELOPMENTCONFIG(CONFIGPRIVATE):
DEBUG = True
# redis
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
REDIS_DB = 1
class TESTINGCONFIG(CONFIGPRIVATE):
DEBUG = True
# redis
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
REDIS_DB = 3
main.py
# 配置
import redis
from flask import Flask, session
from flask_migrate import MigrateCommand, Migrate
from flask_script import Manager
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__,
instance_path="E:\\workspace\\pycharm\\demo\\config_private\\",
instance_relative_config=True)
app.config.from_pyfile("config_private.py")
dev_config = app.config["DEVELOPMENTCONFIG"]
app.config.from_object(dev_config)
redis = redis.StrictRedis(host=dev_config.REDIS_HOST,
port=dev_config.REDIS_PORT,
db=dev_config.REDIS_DB)
db_SQLAlchemy = SQLAlchemy(app)
Migrate(app, db_SQLAlchemy)
Session(app)
manager = Manager(app)
manager.add_command("db", MigrateCommand)
@app.route('/')
def index():
return 'index'
if __name__ == '__main__':
app.run()
運(yùn)行結(jié)果
# 配置
import redis
from flask import Flask, session
from flask_migrate import MigrateCommand, Migrate
from flask_script import Manager
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__,
instance_path="E:\\workspace\\pycharm\\demo\\config_private\\",
instance_relative_config=True)
app.config.from_pyfile("config_private.py")
dev_config = app.config["DEVELOPMENTCONFIG"]
app.config.from_object(dev_config)
redis = redis.StrictRedis(host=dev_config.REDIS_HOST,
port=dev_config.REDIS_PORT,
db=dev_config.REDIS_DB)
db_SQLAlchemy = SQLAlchemy(app)
Migrate(app, db_SQLAlchemy)
Session(app)
manager = Manager(app)
manager.add_command("db", MigrateCommand)
@app.route('/')
def index():
return 'index'
if __name__ == '__main__':
app.run()
perfect ! 完美 !
到此結(jié)? DragonFangQy 2018.6.20