问题描述:
在一个flask应用程序上设计登录界面,登录后,另一个flask应用可以获取当前的登录状态。
问题解决思路:
session的共享,首先将登录的session存储到redis中,然后另外一个flask获取当前的session状态,通过user_id来判断当前的登录状态。login_user()函数会将user_id存入,logout_user()会将用户的user_id进行弹出。以下为login_user logout_user的源码:
def login_user(user, remember=False, force=False, fresh=True):
‘’’
Logs a user in. You should pass the actual user object to this. If the
user’s is_active
property is False
, they will not be logged in
unless force
is True
.
This will return ``True`` if the log in attempt succeeds, and ``False`` if
it fails (i.e. because the user is inactive).
:param user: The user object to log in.
:type user: object
:param remember: Whether to remember the user after their session expires.
Defaults to ``False``.
:type remember: bool
:param force: If the user is inactive, setting this to ``True`` will log
them in regardless. Defaults to ``False``.
:type force: bool
:param fresh: setting this to ``False`` will log in the user with a session
marked as not "fresh". Defaults to ``True``.
:type fresh: bool
'''
if not force and not user.is_active:
return False
user_id = getattr(user, current_app.login_manager.id_attribute)()
session['user_id'] = user_id
session['_fresh'] = fresh
session['_id'] = _create_identifier()
if remember:
session['remember'] = 'set'
_request_ctx_stack.top.user = user
user_logged_in.send(current_app._get_current_object(), user=_get_user())
return True
def logout_user():
‘’’
Logs a user out. (You do not need to pass the actual user.) This will
also clean up the remember me cookie if it exists.
‘’’
user = _get_user()
if 'user_id' in session:
session.pop('user_id')
if '_fresh' in session:
session.pop('_fresh')
cookie_name = current_app.config.get('REMEMBER_COOKIE_NAME', COOKIE_NAME)
if cookie_name in request.cookies:
session['remember'] = 'clear'
user_logged_out.send(current_app._get_current_object(), user=user)
current_app.login_manager.reload_user()
return True
代码实现:
首先,应用1的配置:用的是MySQL数据库
DATABASE_URL=‘mysql+pymysql://root:root@127.0.0.1/flask’
SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'app.db')
REDIS_URL=os.environ.get('REDIS_URL') or 'redis://'
SESSION_TYPE=os.environ.get('SESSION_TYPE') or 'redis'
SESSION_PERMANENT=os.environ.get('SESSION_PERMANENT') or False
SESSION_USE_SIGNER=os.environ.get('SESSION_USE_SIGNER') or False
SESSION_KEY_PREFIX=os.environ.get('SESSION_KEY_PREFIX') or 'session:'
REDIS_HOST=os.environ.get('REDIS_HOST') or '127.0.0.1'
REDIS_PORT=os.environ.get('REDIS_PORT') or '6379'
REDIS_PASSWORD=os.environ.get('REDIS_PASSWORD') or ''
SESSION_REDIS=redis.Redis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD)
PERMANENT_SESSION_LIFETIME= datetime.timedelta(seconds=30*60)
应用2的配置:
SESSION_TYPE=os.environ.get('SESSION_TYPE') or 'redis'
SESSION_PERMANENT=os.environ.get('SESSION_PERMANENT') or False
SESSION_USE_SIGNER=os.environ.get('SESSION_USE_SIGNER') or False
SESSION_KEY_PREFIX=os.environ.get('SESSION_KEY_PREFIX') or 'session:'
REDIS_HOST=os.environ.get('REDIS_HOST') or '127.0.0.1'
REDIS_PORT=os.environ.get('REDIS_PORT') or '6379'
REDIS_PASSWORD=os.environ.get('REDIS_PASSWORD') or ''
SESSION_REDIS=redis.Redis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWORD)
PERMANENT_SESSION_LIFETIME= datetime.timedelta(seconds=30*60)
然后可以自己写一个判断登录状态的装饰器代替login_required
def is_login(func):
@functools.wraps(func)
def inner(*args,**kwargs):
user = session.get('user_id')
if not user:
return redirect('login')
return func(*args,**kwargs)
return inner
方法二是依然利用login_required 但是将load_user 函数更改一下。
返回值必须是一个object的user且带属性is_authenticated ,否则在login_required中会出现错误
def login_required(func):
'''
If you decorate a view with this, it will ensure that the current user is
logged in and authenticated before calling the actual view. (If they are
not, it calls the :attr:`LoginManager.unauthorized` callback.) For
example::
@app.route('/post')
@login_required
def post():
pass
If there are only certain times you need to require that your user is
logged in, you can do so with::
if not current_user.is_authenticated:
return current_app.login_manager.unauthorized()
...which is essentially the code that this function adds to your views.
It can be convenient to globally turn off authentication when unit testing.
To enable this, if the application configuration variable `LOGIN_DISABLED`
is set to `True`, this decorator will be ignored.
.. Note ::
Per `W3 guidelines for CORS preflight requests
<http://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0>`_,
HTTP ``OPTIONS`` requests are exempt from login checks.
:param func: The view function to decorate.
:type func: function
'''
@wraps(func)
def decorated_view(*args, **kwargs):
if request.method in EXEMPT_METHODS:
return func(*args, **kwargs)
elif current_app.login_manager._login_disabled:
return func(*args, **kwargs)
**elif not current_user.is_authenticated:**
return current_app.login_manager.unauthorized()
return func(*args, **kwargs)
return decorated_view
总体大概应该就是这样了。
参考文章
https://www.py.cn/kuangjia/flask/11390.html