
使用Flask-Login
注册登录
登录可见
为了确保只有登录的用户才能访问页面,在
Flask CSRF
保护中,我们也自己实现了一个装饰器.# limit.py
from functools import wraps
from flask import redirect,url_for,session
def limit_session(func):
@wraps(func)
def wrapper(*args,**kwargs):
username = session.get('username',None)
if username:
return func(*args,**kwargs)
else:
session.clear()
return redirect(url_for('login'))
return wrapper并把它应用到了视图中
class SaveMoneyView(views.MethodView):
decorators = [limit_session]
pass同样的,
Flask-Login
也实现了相同的功能login_required
from flask_login import login_required
# 登录可见的页面
@app.route('/personal/', methods=['GET', 'POST'])
@login_required
def personal():
pass同样可以查看源码,检查这一过程.
def login_required(func):
@wraps(func)
def decorated_view(*args, **kwargs):
if request.method in EXEMPT_METHODS: # 通常该请求是获取服务器支持的HTTP请求方法
return func(*args, **kwargs)
elif current_app.config.get('LOGIN_DISABLED'): # 如果LOGIN_DISABLED 为True,则忽略验证
return func(*args, **kwargs)
elif not current_user.is_authenticated: # 判断用户是否登录
"""
没有登录的情况下:
1.如果注册了 LoginManager.unauthorized_handler 则这个时候调用这个函数
2. 向用户提示 LoginManager.login_message信息
3.有 login_view的情况下,跳转到login_view,没有则返回abort(401)
"""
return current_app.login_manager.unauthorized()
return func(*args, **kwargs)
return decorated_view以上涉及到了
unauthorized()
方法,这里可以简单看下def unauthorized(self):
# current_app._get_current_object() 指向了app本身
# 查看 flask上下文相关
user_unauthorized.send(current_app._get_current_object())
if self.unauthorized_callback:
return self.unauthorized_callback()
if request.blueprint in self.blueprint_login_views:
login_view = self.blueprint_login_views[request.blueprint]
else:
login_view = self.login_view
if not login_view:
abort(401)
if self.login_message:
if self.localize_callback is not None:
flash(self.localize_callback(self.login_message),
category=self.login_message_category)
else:
flash(self.login_message, category=self.login_message_category)
config = current_app.config
if config.get('USE_SESSION_FOR_NEXT', USE_SESSION_FOR_NEXT):
login_url = expand_login_view(login_view)
session['_id'] = self._session_identifier_generator()
session['next'] = make_next_param(login_url, request.url)
redirect_url = make_login_url(login_view)
else:
redirect_url = make_login_url(login_view, next_url=request.url)
return redirect(redirect_url)
退出登录
对于退出登录,只需要调用
logout_user
即可.from flask_login import logout_user
# 登出
@app.route('/logout/')
@login_required
def logout():
logout_user()
return 'logout'源码
def _get_user():
if has_request_context() and not hasattr(_request_ctx_stack.top, 'user'):
current_app.login_manager._load_user()
return getattr(_request_ctx_stack.top, 'user', None)
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')
if '_id' in session:
session.pop('_id')
cookie_name = current_app.config.get('REMEMBER_COOKIE_NAME', COOKIE_NAME)
if cookie_name in request.cookies:
session['_remember'] = 'clear'
if '_remember_seconds' in session:
session.pop('_remember_seconds')
user_logged_out.send(current_app._get_current_object(), user=user)
current_app.login_manager._update_request_context_with_user()
return True从堆栈结构中找到
session
中的user
,实现登出操作.
自定义未授权访问的处理方法
@login_required
装饰器对于未登录用户访问的默认处理是重定向到登录视图,如果我们不想它这么做的话,可以自定义处理方法:@login_manager.unauthorized_handler
def unauthorized_handler():
return 'Unauthorized'这个
@login_manager.unauthorized_handler
装饰器所修饰的方法就会代替@login_required
装饰器的默认处理方法.有了上面的代码,当未登录用户访问
index
视图时,页面就会直接返回Unauthorized
信息.
REmember Me
在登录视图中,调用
login_user()
方法,传入remeber=True
,就可以实现记住我
功能login_user(curr_user, remember=True)
Flask-Login
是通过在Cookie
实现的,它会在Cookie
中添加一个remember_token
字段来记住之前登录的用户信息,所以禁用Cookie
的话,该功能将无法工作.
fresh
当用户登录时,他们的会话会被标记为
fresh
(login_user()
操作),即在session
中设置_fresh=True
.如果使用rember me
的cookie
从新登录,会话被标记成not fresh
.在某些情况下,会强制要求用户登录一次,比如修改登录密码,这就需要
fresh_login_required
.
fresh_login_required
除了可以验证用户登录,也将确保他们的登录是fresh
,如果不是fresh
,它会重新将用户准到输入验证条件的页面,这主要是为了确保用户修改个人信息的敏感操作.def fresh_login_required(func):
@wraps(func)
def decorated_view(*args, **kwargs):
if request.method in EXEMPT_METHODS:
return func(*args, **kwargs)
elif current_app.config.get('LOGIN_DISABLED'):
return func(*args, **kwargs)
elif not current_user.is_authenticated:
return current_app.login_manager.unauthorized()
elif not login_fresh():
return current_app.login_manager.needs_refresh()
return func(*args, **kwargs)
return decorated_view# confirm_login 将会话重新标记为 fresh
def confirm_login():
session['_fresh'] = True
session['_id'] = current_app.login_manager._session_identifier_generator()
user_login_confirmed.send(current_app._get_current_object())from flask_login import fresh_login_required
@app.route('/login/')
@fresh_login_required
def home():
return 'Logged in as: %s' % current_user.get_id()
Cookie
设置
可以在
config
中设置cookie
信息
REMEMBER_COOKIE_NAME
存储 remember me
信息的cookie
名称,默认为remember_token
REMEMBER_COOKIE_DURATION
cookie
过期时长,默认365天REMEMBER_COOKIE_DOMAIN
默认为 None
,比如设置为.example.com
将会允许所有子域名REMEMBER_COOKIE_PATH
限制 remember me
到一个路径,默认为/
REMEMBER_COOKIE_SECURE
限制 remember me
到https
REMEMBER_COOKIE_HTTPONLY
限制 remember me
到客户端脚本访问REMEMBER_COOKIE_REFRESH_EACH_REQUEST
Ture
:cookie
每次刷新会延长生命周期
session
保护
Flask-Login自动启用会话保护功能.对于每个请求,它会验证用户标识,这个标识是由客户端IP地址和”User Agent”的值经SHA512编码而来.在用户登录成功时,Flask-Login就会将这个值保存起来以便后续检查.默认的会话保护模式是
basic
,为了加强安全性,你可以启用强会话保护模式.方法是配置LoginManager实例对象中的session_protection
属性:login_manager.session_protection = "strong"
在
strong
模式下,一旦用户标识检查失败,便会清空所用Session内容,并且”Remember Me”也失效。而basic
模式下,只是将登录标为非Fresh登录。你还可以将login_manager.session_protection
置为”None”来取消会话保护。
在对
API
进行认证时,我们可能不会用到cookie
.为此,可以使用一个自定义的session
接口,该接口根据在请求中设计的标志跳过保存session
from flask import g
from flask.sessions import SecureCookieSessionInterface
from flask_login import user_loaded_from_header
class CustomSessionInterface(SecureCookieSessionInterface):
"""防止 API 请求创建 session。"""
def save_session(self, *args, **kwargs):
if g.get('login_via_header'):
return
return super(CustomSessionInterface, self).save_session(*args,
**kwargs)
app.session_interface = CustomSessionInterface()
@user_loaded_from_header.connect
def user_loaded_from_header(self, user=None):
g.login_via_header = True这可以放置在用户使用
request_loader
进行认证时设置Flask Session
current_user
Flask-Login
中可以直接使用current_user
访问已经登录的用户,current_user
可以在每个模板中世界使用.{% if current_user.is_authenticated %}
Hi {{ current_user.name }}!
{% endif %}
3.实例
# app.py
from flask import Flask, request, abort, redirect, url_for, render_template, session
from flask_login import LoginManager, login_user, UserMixin, login_required, logout_user
import config
from urllib.parse import urlparse, urljoin
from flask_session import Session as Fsession
from datetime import timedelta
app = Flask(__name__)
app.config.from_object(config)
login_manager = LoginManager()
Fsession(app)
login_manager.init_app(app)
# 指定登录的URL
login_manager.login_view = 'login'
# 创建ORM映射
# 用户记录表
users = [
{'username': 'Tom', 'password': '111111'},
{'username': 'Michael', 'password': '123456'}
]
# 通过用户名,获取用户记录,如果不存在,返回None
def query_user(username):
for user in users:
if user['username'] == username:
return user
else:
return None
class User(UserMixin):
pass
# 如果用户名存在,就构造一个用户类对象,并使用用户名作为ID,如果不存在就返回None
# 回调函数
@login_manager.user_loader
def load_user(username):
if query_user(username) is not None:
curr_user = User()
curr_user.id = username
return curr_user
else:
return None
def is_safe_url(target):
ref_url = urlparse(request.host_url)
test_url = urlparse(urljoin(request.host_url, target))
return test_url.scheme in ('http', 'https') and ref_url.netloc == test_url.netloc
@app.route('/login/', methods=['GET', 'POST'])
def login():
# 假设通过表单验证
# 假设通过数据库验证
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
# 验证表单,数据库
user = query_user(username)
if user and password == user['password']:
# curr_user 是 User类的一个实例
curr_user = User()
curr_user.id = username
# 通过 Flask-login的login_user来登录账户
login_user(curr_user, remember=True, duration=timedelta(days=60))
nextD = request.args.get('next')
print(nextD)
# is_safe_url 用来检查url是否可以安全的重定向
# 避免重定向攻击
# if not is_safe_url(nextD):
# return abort(404)
# return redirect(next or url_for('index'))
return redirect(url_for('index'))
else:
return render_template('login.html')
@app.route('/')
def index():
return 'Hello World!'
# 登录可见的页面
@app.route('/personal/', methods=['GET', 'POST'])
@login_required
def personal():
pass
# 登出
@app.route('/logout/')
@login_required
def logout():
print(session['_user_id'])
logout_user()
try:
print(session['_user_id'])
except Exception as e:
print('session已经删除')
return 'logout'
if __name__ == '__main__':
app.run()# config.py
#!/usr/bin/env python
# coding=utf-8
import os
from exts import client
from datetime import timedelta
DEBUG = True
TEMPLATES_AUTO_RELOAD = True
SECRET_KEY = os.urandom(24)
# 设置flask-session相关
SESSION_TYPE = 'memcached' # 指定类型
SESSION_MEMCACHED = client # 指定实例
SESSION_USE_SIGNER = True # 设置加密
SESSION_KEY_PREFIX = 'session' # 指定值的前缀
PERMANENT_SESSION_LIFETIME = timedelta(days=0)# exts.py
from pymemcache.client import Client
client = Client(('192.168.0.101', 11211), allow_unicode_keys=True, encoding='utf8')<!-- login.html --->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if current_user.is_authenticated %}
Hi {{ current_user.name }}
{% endif %}
<form action="{{ url_for('login') }}" method="post">
<label for="">username:</label><input type="text" name="username">
<label for="">password:</label><input type="password" name="password">
<input type="checkbox" value="remember me" name="remember">
<input type="submit" name="submit">
</form>
</body>
</html>
– END –
原文始发于微信公众号(Flask学习笔记):使用Flask-Login注册登录(2)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/36416.html