欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

FLask 初探三( 隐藏配制文件实践 )

程序员文章站 2022-07-15 10:17:29
...

引言

Flask初探一(Flask 各参数的应用) 中提到了隐藏重要配置( 敏感配置) 的方式, 今天详细研究一下怎么样实现.

创建项目

项目结构

FLask 初探三( 隐藏配制文件实践 )

main.py

假设config_private 目录下的config_private.py是要隐藏的配置文件

导入模板 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:aaa@qq.com: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()

隐藏敏感信息

配置完成, 可以正常运行. 里面有一些账号密码 等敏感信息,不想公开, 怎么办?

首先, 将配置信息抽取到config_private.py 文件, 结合 配制的最佳实践1

config_private.py

class Config(object):
    # debug
    DEBUG = False

    # SQLAlchemy
    SQLALCHEMY_DATABASE_URI = "mysql://root:aaa@qq.com: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

配置信息抽取完成

从文件加载配置信息

实验

# 配置
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()

实验结果

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()

实验结果

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

实验结果

 * 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 , 都没有出现过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

        # 重点在这, from_pyfile 的本质也是通过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):

            # 重点在这
            if key.isupper():
                self[key] = getattr(obj, key)

key.isupper() , 键是全大写吗? 是就加入app.config 字典, 从这里可以看出来, 文件中的配置信息必须大写, 才能被添加到app.config 字典中, 不然, 你只能修改这句代码了.

实验

config_private.py

# 如果使用类, 请将类名关不大写, 不然需要改变源码,才能将属性放到app.config 字典中
class CONFIGPRIVATE(object):
    # SQLAlchemy
    SQLALCHEMY_DATABASE_URI = "mysql://root:aaa@qq.com: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()

实验结果

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 字典不同

正确姿势

言归正传,怎样正确隐藏敏感配置, 如果要使用文件并且通过类进行配置, 略微有些麻烦. 至于不使用文件不使用类的方式这里不做讨论, 配置DEBUG 属性就是一个使用文件,不使用类的最简单的例子

config_private.py

# 如果使用类, 请将类名关不大写, 不然需要改变源码,才能将属性放到app.config 字典中
class CONFIGPRIVATE(object):

    DEBUG = False

    # SQLAlchemy
    SQLALCHEMY_DATABASE_URI = "mysql://root:aaa@qq.com: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()

运行结果

# 配置
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 ! 完美 !


到此结  DragonFangQy 2018.6.20