ВНИМАНИЕ! Некоторые веб-серверы по умолчанию не передают заголовки авторизации приложению WSGI. Например, если используется Apache с
mod_wsgi
, то необходимо в файле.htaccess
установить опциюWSGIPassAuthorization On
. В NGINX все работает из "коробки".
Расширение Flask-HTTPAuth
упрощает использование HTTP-аутентификации с маршрутами Flask.
Flask-HTTPAuth
в виртуальное окружение.Так как модуль Flask-HTTPAuth
не входит в стандартную библиотеку Python, его необходимо установить отдельно. Cделать это можно с помощью менеджера пакетов pip
.
# создаем виртуальное окружение, если нет $ python3 -m venv .venv --prompt VirtualEnv # активируем виртуальное окружение $ source .venv/bin/activate # обновляем `pip` (VirtualEnv):~$ python3 -m pip install -U pip # ставим модуль `Flask-HTTPAuth` (VirtualEnv):~$ python3 -m pip install Flask-HTTPAuth
HTTPBasicAuth
; HTTPDigestAuth
; HTTPDigestAuth
; HTTPMultiAuth
. В следующем примере приложения используется базовая проверка подлинности HTTP для защиты корневого маршрута /
:
from flask import Flask from flask_httpauth import HTTPBasicAuth from werkzeug.security import generate_password_hash, check_password_hash app = Flask(__name__) auth = HTTPBasicAuth() users = { "john": generate_password_hash("hello"), "susan": generate_password_hash("bye") } @auth.verify_password # функция проверки пароля def verify_pwd(username, password): if (username in users and check_password_hash(users.get(username), password)): return username @app.route('/') # проверка подлинности для защиты маршрута @auth.login_required def index(): return "Hello, {}!".format(auth.current_user()) if __name__ == '__main__': app.run()
Функция verify_pwd()
, украшенная декоратором @auth.verify_password, получает имя пользователя username
и пароль password
, отправленные клиентом. Если учетные данные принадлежат пользователю, то функция должна вернуть объект пользователя. Если учетные данные недействительны, то функция может вернуть None
или False
. Затем объект пользователя можно запросить из метода auth.current_user()
экземпляра аутентификации auth
.
В следующем примере используется дайджест-аутентификация HTTP:
from flask import Flask from flask_httpauth import HTTPDigestAuth app = Flask(__name__) app.config['SECRET_KEY'] = 'secret key here' auth = HTTPDigestAuth() users = { "john": "hello", "susan": "bye" } @auth.get_password def get_pw(username): if username in users: return users.get(username) return None @app.route('/') # проверка подлинности для защиты маршрута @auth.login_required def index(): return "Hello, {}!".format(auth.username()) if __name__ == '__main__': app.run()
В следующем примере используется специальная схема аутентификации HTTP для защиты корневого маршрута /
с помощью токена:
from flask import Flask from flask_httpauth import HTTPTokenAuth app = Flask(__name__) auth = HTTPTokenAuth(scheme='Bearer') tokens = { "secret-token-1": "john", "secret-token-2": "susan" } @auth.verify_token def verify_token(token): if token in tokens: return tokens[token] @app.route('/') @auth.login_required def index(): return "Hello, {}!".format(auth.current_user()) if __name__ == '__main__': app.run()
Класс HTTPTokenAuth
представляет собой универсальный обработчик аутентификации, который можно использовать с нестандартными схемами аутентификации, при этом имя схемы указывается в качестве аргумента в конструкторе. В приведенном выше примере заголовок WWW-Authenticate
, в качестве схемы предоставленный сервером будет использовать 'Bearer'
:
WWW-Authenticate: Bearer realm="Authentication Required"
Обратный вызов verify_token(token)
получает учетные данные аутентификации, предоставленные клиентом в заголовке авторизации. Это может быть простой токен или может содержать несколько аргументов, которые функция verify_token()
должна будет проанализировать и извлечь из строки. Как и в случае с verify_password()
, функция должна возвращать объект пользователя, если токен действителен.
Модуль Flask-HTTPAuth
включает простую систему аутентификации на основе ролей, которую можно добавить при необходимости, чтобы обеспечить дополнительный уровень детализации при фильтрации доступа к маршрутам. Чтобы включить поддержку ролей, необходимо написать функцию, возвращающую список ролей для данного пользователя, и украсить ее декоратором auth.get_user_roles
:
@auth.get_user_roles def get_user_roles(user): # например, `user.get_roles()` может брать роли из БД return user.get_roles()
Чтобы ПРЕДОСТАВИТЬ доступ к маршруту для пользователей, имеющих заданную роль, необходимо добавить аргумент role
в декоратор @auth.login_required
:
@app.route('/admin') # ПУСКАЕМ пользователя с ролью 'admin' @auth.login_required(role='admin') def admins_only(): return f"Hello {auth.current_user()}, you are an admin!"
Аргумент role
может принимать список ролей, и в этом случае ДОСТУП БУДЕТ ПРЕДОСТАВЛЕН пользователям, имеющим любую из заданных ролей:
@app.route('/admin') # ПУСКАЕМ пользователей с ролью 'admin' или 'moderator' @auth.login_required(role=['admin', 'moderator']) def moderator(): return f"Hello {auth.current_user()}, you are an admin or a moderator!"
В наиболее продвинутых случаях пользователей можно фильтровать по нескольким ролям:
@app.route('/admin') @auth.login_required(role=['user', ['moderator', 'contributor']]) def contributor(): return f"Hello {auth.current_user()}, you are a user or a moderator/contributor!"
Иногда приложениям необходимо поддерживать комбинацию методов аутентификации. Например, веб-приложение может проверять доступ путем отправки идентификатора и секретного кода клиента поверх базовой аутентификации, в то время как сторонние клиенты API используют токен носителя JWS или JWT. Класс MultiAuth
позволяет защитить маршрут с помощью более чем одного объекта аутентификации. Чтобы получить доступ к маршруту, необходимо, чтобы один из методов аутентификации прошел проверку.
from time import time from flask import Flask from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth, MultiAuth from werkzeug.security import generate_password_hash, check_password_hash import jwt app = Flask(__name__) app.config['SECRET_KEY'] = 'top secret!' basic_auth = HTTPBasicAuth() token_auth = HTTPTokenAuth('Bearer') multi_auth = MultiAuth(basic_auth, token_auth) users = { "john": generate_password_hash("hello"), "susan": generate_password_hash("bye") } for user in users.keys(): token = jwt.encode({'username': user, 'exp': int(time()) + 3600}, app.config['SECRET_KEY'], algorithm='HS256') print('*** token for {}: {}\n'.format(user, token)) @basic_auth.verify_password # обратный вызов проверки пароля def verify_password(username, password): if username in users: if check_password_hash(users.get(username), password): return username @token_auth.verify_token # обратный вызов проверки токена def verify_token(token): try: data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256']) except: # noqa: E722 return False if 'username' in data: return data['username'] @app.route('/') # защита доступа к маршруту @multi_auth.login_required def index(): return "Hello, %s!" % multi_auth.current_user() if __name__ == '__main__': app.run()
Если нужны роли при использовании MultiAuth
:
# псевдокод from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth, MultiAuth basic_auth = HTTPBasicAuth() token_auth = HTTPTokenAuth(scheme='Bearer') # в `MultiAuth` передаем экземпляры `basic_auth` и `token_auth` multi_auth = MultiAuth(basic_auth, token_auth) ... # ВНИМАНИЕ! Класс `MultiAuth` не имеет метода `.get_user_roles` # следовательно необходимо использовать следующую конструкцию @basic_auth.get_user_roles @token_auth.get_user_roles def get_user_roles(user): return user.get_roles() ... @app.route('/admin') # ПУСКАЕМ пользователей с ролью 'admin' или 'moderator' @multi_auth.login_required(role=['admin', 'moderator']) def moderator(): return f"Hello {auth.current_user()}, you are an ADMIN or a MODERATOR!" ...
Flask-HTTPAuth
flask_httpauth.HTTPBasicAuth(scheme=None, realm=None)
:Класс flask_httpauth.HTTPBasicAuth()
обрабатывает базовую аутентификацию HTTP для маршрутов Flask.
Если указан необязательный аргумент scheme
, то он будет использоваться вместо стандартной "Basic" схемы в ответе WWW-Authenticate
. Довольно распространенной практикой является использование специальной схемы, чтобы браузеры не предлагали пользователю войти в систему.
Аргумент realm
можно использовать для предоставления области, определенной приложением, с заголовком WWW-Authenticate
.
HTTPBasicAuth
HTTPBasicAuth.verify_password(verify_pwd_callback)
:Если функция обратного вызова verify_pwd_callback
определена, то она будет вызываться платформой для проверки правильности комбинации имени пользователя и пароля, предоставленной клиентом. Функция обратного вызова принимает два аргумента: username
и password
и должна вернуть объект пользователя, если учетные данные действительны, или True
, если объект пользователя недоступен. В случае неудачной аутентификации она должна вернуть None
или False
. Пример использования:
@auth.verify_password def verify_password(username, password): user = User.query.filter_by(username).first() if user and passlib.hash.sha256_crypt.verify(password, user.password_hash): return user
Переданная функция verify_pwd_callback()
также вызывается, когда запрос не имеет заголовка авторизации с учетными данными пользователя, и в этом случае аргументам username
и password
присваиваются пустые строки. В этом случае приложение может вернуть True
, и это позволит анонимным пользователям получить доступ к маршруту. Функция обратного вызова verify_pwd_callback()
может указать, что пользователь анонимен, записав переменную состояния в flask.g
или проверив, имеет ли auth.current_user()
значение None
.
HTTPBasicAuth.get_user_roles(roles_callback)
:Если функция обратного вызова roles_callback
определена, то она будет вызываться платформой для получения ролей, назначенных вошедшему пользователю. Функция обратного вызова принимает единственный аргумент - username
, для которого запрашиваются роли.
Пользовательский объект username
, переданный в эту функцию, будет возвращен обратным вызовом verify_password()
. Если обратный вызов проверки вернул True
вместо объекта пользователя, то объект авторизации, предоставленный Flask, будет передан в эту функцию. Функция должна возвращать роль или список ролей, принадлежащих пользователю. Пример:
@auth.get_user_roles def get_user_roles(user): return user.get_roles()
HTTPBasicAuth.error_handler(error_callback)
:Если функция обратного вызова error_callback
определена, то она будет вызываться платформой, когда необходимо отправить ошибку аутентификации обратно клиенту. Функция может принимать один аргумент - код состояния ошибки, который может быть 401 (неправильные учетные данные) или 403 (правильные, но недостаточные учетные данные). Если обратный вызов не предоставлен, то генерируется ответ об ошибке по умолчанию. Пример:
@auth.error_handler def auth_error(status): if status == 403: return "<h1>Access Denied</h1>", status return "<h1>Error Authenticate</h1>", status
HTTPBasicAuth.login_required(view_callback)
:Если функция обратного вызова view_callback
определена, то она будет вызвана при успешной аутентификации. Пример:
@app.route('/private') @auth.login_required def private_page(): return "Only for authorized people!"
Можно указать необязательный аргумент role
для дальнейшего ограничения доступа по ролям. Пример:
@app.route('/private') @auth.login_required(role='admin') def private_page(): return "Only for admins!"
Необязательный аргумент optional
может быть установлен в значение True
, чтобы разрешить выполнение маршрута даже в том случае, если аутентификация не включена в запрос, и в этом случае для auth.current_user()
будет установлено значение None
. Пример:
@app.route('/private') @auth.login_required(optional=True) def private_page(): user = auth.current_user() return f"Hello {user.name if user is not None else 'anonymous'}!"
HTTPBasicAuth.current_user()
:Пользовательский объект, возвращаемый обратным вызовом verify_password()
при успешной аутентификации. Если обратный вызов не возвращает пользователя, то возвращается имя пользователя, переданное клиентом. Пример:
@app.route('/') @auth.login_required def index(): user = auth.current_user() return "Hello, {user.name}!"
flask_httpauth.HTTPDigestAuth(scheme=None, realm=None, use_ha1_pw=False, qop='auth', algorithm='MD5')
:Класс flask_httpauth.HTTPDigestAuth()
обрабатывает аутентификацию HTTP Digest для маршрутов Flask. чтобы сеанс работал, в приложении Flask должен быть установлен SECRET_KEY
. Flask по умолчанию сохраняет пользовательские сеансы в клиенте как защищенные файлы cookie
, поэтому клиент должен иметь возможность обрабатывать файлы cookie
.
Если указан необязательный аргумент scheme
, то он будет использоваться вместо стандартной "Digest" схемы в ответе WWW-Authenticate
. Довольно распространенной практикой является использование специальной схемы, чтобы браузеры не предлагали пользователю войти в систему.
Аргумент realm
можно использовать для предоставления области, определенной приложением, с заголовком WWW-Authenticate
.
Если use_ha1_pw
имеет значение False
, то обратный вызов get_password
должен вернуть простой текстовый пароль для данного пользователя. Если use_ha1_pw
имеет значение True
, то обратный вызов get_password
должен вернуть значение HA1
для данного пользователя. Преимущество установки use_ha1_pw
в значение True
заключается в том, что это позволяет приложению хранить хэш HA1
пароля в базе данных пользователей.
Аргумент qop
настраивает список допустимых расширений качества защиты. Этот аргумент может быть указан в виде строки, разделенной запятыми, списка строк или None
для отключения. По умолчанию используется 'auth'
.
Аргумент algorithm
настраивает используемый алгоритм генерации хеша. По умолчанию - MD5
. Реализованы два алгоритма: MD5
и MD5-Sess
.
HTTPDigestAuth
HTTPBasicAuth.generate_ha1(username, password)
генерирует хэш HA1
, который можно будет сохранить в базе данных пользователя, если для параметра use_ha1_pw
в конструкторе установлено значение True
.HTTPDigestAuth.generate_nonce(nonce_making_callback)
- если функция обратного вызова nonce_making_callback()
определена, то она будет вызываться платформой для генерации nonce
. Для этого следует также определить HTTPDigestAuth.verify_nonce()
. Можно использовать для механизма хранения состояния, отличного от сеанса.HTTPDigestAuth.verify_nonce(nonce_verify_callback)
- если функция обратного вызова nonce_verify_callback()
определена, то она будет вызываться платформой для проверки правильности nonce
. Она будет вызываться с единственным аргументом: nonce
. Можно использовать для механизма хранения состояния, отличного от сеанса.HTTPDigestAuth.generate_opaque(opaque_making_callback)
- если функция обратного вызова opaque_making_callback()
определена, то она будет вызываться платформой для генерации непрозрачного значения. Для этого следует также определить HTTPDigestAuth.verify_opaque()
. Можно использовать для механизма хранения состояния, отличного от сеанса.HTTPDigestAuth.generate_opaque(opaque_making_callback)
- если функция обратного вызова opaque_making_callback()
определена, то она будет вызываться платформой для проверки допустимости непрозрачного значения. Она будет вызываться с единственным аргументом: opaque
, которое необходимо проверить.HTTPDigestAuth.get_user_roles()
- аналогична HTTPBasicAuth.get_user_roles()
.HTTPDigestAuth.error_handler()
- аналогична HTTPBasicAuth.error_handler()
.HTTPDigestAuth.login_required()
- аналогична HTTPBasicAuth.login_required()
. HTTPDigestAuth.current_user()
- аналогична HTTPBasicAuth.current_user()
.flask_httpauth.HTTPTokenAuth(scheme='Bearer', realm=None, header=None)
:Класс flask_httpauth.HTTPTokenAuth()
обрабатывает HTTP-аутентификацию с помощью пользовательских схем для маршрутов Flask.
Аргумент scheme
можно использовать для указания схемы, которая будет использоваться в ответе WWW-Authenticate
. Заголовок авторизации, отправленный клиентом, должен включать эту схему, за которой следует токен. Пример:
Authorization: Bearer this-is-my-token
Аргумент realm
можно использовать для предоставления области, определенной приложением, с заголовком WWW-Authenticate
.
Аргумент header
можно использовать для указания пользовательского заголовка вместо авторизации, откуда можно получить токен. Если используется пользовательский заголовок, то схему включать не следует. Пример:
X-API-Key: this-is-my-token
HTTPTokenAuth
HTTPTokenAuth.verify_token(verify_token_callback)
Если функция обратного вызова verify_token_callback
определена, то она будет вызываться платформой для проверки правильности учетных данных, отправленных клиентом с заголовком авторизации. Функция обратного вызова принимает один аргумент - токен token
, предоставленный клиентом. Функция должна вернуть объект пользователя, если токен действителен, или True
, если объект пользователя недоступен. В случае неудачной аутентификации функция должна вернуть None
или False
. Пример использования:
@auth.verify_token def verify_token(token): return User.query.filter_by(token=token).first()
Обратите внимание, что при использовании этого класса требуется обратный вызов verify_token()
.
HTTPTokenAuth.get_user_roles()
:Метод HTTPTokenAuth.get_user_roles()
аналогичен HTTPBasicAuth.get_user_roles()
.
HTTPTokenAuth.error_handler()
:Метод HTTPTokenAuth.error_handler()
аналогичен HTTPBasicAuth.error_handler()
.
HTTPTokenAuth.login_required()
:Метод HTTPTokenAuth.login_required()
аналогичен HTTPBasicAuth.login_required()
.
HTTPTokenAuth.current_user()
:Метод HTTPTokenAuth.current_user()
аналогичен HTTPBasicAuth.current_user()
.
flask_httpauth.HTTPMultiAuth(auth_object, ...)
:Класс flask_httpauth.HTTPMultiAuth()
создаёт объект множественной аутентификации.
Аргументы представляют собой один или несколько экземпляров HTTPBasicAuth
, HTTPDigestAuth
или HTTPTokenAuth
. Маршрут, защищенный этим методом аутентификации, будет проверять все заданные объекты аутентификации, пока ОДИН ИЗ НИХ не достигнет успеха.
HTTPMultiAuth
HTTPMultiAuth.verify_token(verify_token_callback)
Метод HTTPMultiAuth.login_required()
аналогичен HTTPBasicAuth.login_required()
.
HTTPMultiAuth.current_user()
:Метод HTTPMultiAuth.current_user()
аналогичен HTTPBasicAuth.current_user()
.