Аутентификация во Flask

Для веб-приложения аутентификация является одной из наиболее важных составляющих. С помощью нее можно предотвратить попадание определенных пользователей на страницы, которые для них не предназначены. Собственную систему аутентификации можно создать с использованием куки, а также использовав механизмы хэширования паролей для обеспечения большей безопасности.

Такой небольшой проект станет хорошей проверкой того, насколько качественно были усвоены знания. Как вы могли догадаться, для реализации функционала по аутентификации, во Flask существует специализированное расширение. Оно называется Flask-Login. С его помощью просто встроить аутентификацию в программу, разработанную с использованием этого фреймворка. Чтобы его инсталлировать, используется такая инструкция.

(env) gvido@vm:~/flask_app$  pip install flask-login

Создание модели пользователя

Сейчас данные по пользователях, которые являются администраторами или людьми, которые имеют право редактирования контента, не сохраняется нигде. Поэтому сперва необходимо создать модель User, которая бы сохраняла данные о пользователях. Давайте внесем соответствующие изменения в файл main2.py 

#..

class User(db.Model):

    __tablename__ = 'users'

    id = db.Column(db.Integer(), primary_key=True)

    name = db.Column(db.String(100))

    username = db.Column(db.String(50), nullable=False, unique=True)

    email = db.Column(db.String(100), nullable=False, unique=True)

    password_hash = db.Column(db.String(100), nullable=False)

    created_on = db.Column(db.DateTime(), default=datetime.utcnow)

    updated_on = db.Column(db.DateTime(), default=datetime.utcnow,  onupdate=datetime.utcnow)




    def __repr__(self):

return "<{}:{}>".format(self.id, self.username)

#...

Здесь мы добавили модель user после модели Employee. Чтобы обновлять базы данных, создается новая миграция. Для этого открывается терминал и вводится следующая инструкция. 

(env) gvido@vm:~/flask_app$ python main2.py db migrate -m "Adding users table"

Для того, чтобы запустить миграцию, необходимо воспользоваться командой upgrade.

(env) gvido@vm:~/flask_app$ python main2.py db upgrade

INFO [alembic.runtime.migration] Context impl MySQLImpl.

INFO [alembic.runtime.migration] Will assume non-transactional DDL.

INFO [alembic.runtime.migration] Running upgrade 6e059688f04e -> 0f0002bf91cc,

Adding users table




(env) gvido@vm:~/flask_app$

Этот код позволяет создать таблицу users в базе данных.

Хэширование паролей

Пароли никогда не должны храниться в виде чистого текста. Это приведет к тому, что их будет очень просто взломать. В результате, злоумышленники будут знать и пароли, и электронные адреса. Известно, что очень часто используются одни и те же пароли для разных сайтов. Следовательно, если злоумышленник получает доступ к одной комбинации, он может потенциально взломать и большое количество других этого же пользователя.

Поэтому хранить пароли в базе данных нельзя. Необходимо сохранить их хэши. Что это такое? Под хэшем подразумевается строка символов, которые выглядят таким образом, как будто они подбираются случайным образом. То есть, так.

pbkdf2:sha256:50000$Otfe3YgZ$4fc9f1d2de2b6beb0b888278f21a8c0777e8ff980016e043f3eacea9f48f6dea

Для создания такой комбинации символов используется односторонняя функция хэширования. Она принимает длительность переменной, а ею возвращается вывод фиксированной длины в виде совокупности знаков такого плана, как показан в примере выше.

Почему это безопаснее, чем пароль? Дело в том, что из него невозможно получить изначальный пароль. Собственно, именно по этой причине функция и получила название односторонней.

Тем не менее, если вводить одну и ту же последовательность значений, функция будет возвращать одинаковый результат. И если хэши совпадают, значит, и пароль введен правильный.

При создании хэша пароля используются следующие процессы. Когда пользователь записывает пароль впервые (то есть, регистрирует аккаунт). необходимо его хэшировать и занести в базу данных. При повторной авторизации эта функция еще раз создает такой же самый хэш и сравнивает с тем значением, которое хранится в базе. Если данные одинаковые, то пользователь может воспользоваться своим аккаунтом. Иначе возникнет ошибка.

В комплект Flask уже входит пакет Werkzeug, который дает возможность хэшировать строковые значения, что и дает возможность хранить пароли надежным способом. Он имеет ряд методов:

Метод Описание
generate_password_hash(password) Принимает пароль и возвращает хэш. По умолчанию использует одностороннюю функцию pbkdf2 для создания хэша.
check_password_hash(password_hash, password) Принимает хэш и пароль в чистом виде, затем сравнивает password и password_hash. Если они одинаковые, возвращает True.

А теперь давайте приведем фрагмент кода, который наглядно демонстрирует особенности работы с функциями хэширования. 

>>>

>>> from werkzeug.security import generate_password_hash, check_password_hash

>>>

>>> hash = generate_password_hash("secret password")

>>>

>>> hash

'pbkdf2:sha256:50000$zB51O5L3$8a43788bc902bca96e01a1eea95a650d9d5320753a2fbd16bea984215cdf97ee'

>>>

>>> check_password_hash(hash, "secret password")

True

>>>

>>> check_password_hash(hash, "pass")

False

>>>

>>>

Учтите, что если check_password_hash() вызывается с верным паролем (“secret password”), то возвращается значение True. Если же нет – False.

Затем необходимо обновить модель пользователя и туда добавить хэширование. 

#...

from werkzeug.security import generate_password_hash,  check_password_hash

#...




#...

class User(db.Model):

    #...

    updated_on = db.Column(db.DateTime(), default=datetime.utcnow, onupdate=datetime.utcnow)




    def __repr__(self):

return "<{}:{}>".format(self.id, self.username)




    def set_password(self, password):

self.password_hash = generate_password_hash(password)




    def check_password(self,  password):

return check_password_hash(self.password_hash, password)

    #...

Создадим пользователей, чтобы проверить хэширование паролей.




(env) gvido@vm:~/flask_app$ python main2.py shell

>>>

>>> from main2 import db, User

>>>

>>> u1 = User(username='spike', email='spike@example.com')

>>> u1.set_password("spike")

>>>

>>> u2 = User(username='tyke', email='tyke@example.com')

>>> u2.set_password("tyke")

>>>

>>> db.session.add_all([u1, u2])

>>> db.session.commit()

>>>

>>> u1, u2

(<1:spike>, <2:tyke>)

>>>

>>>

>>> u1.check_password("pass")

False

>>> u1.check_password("spike")

True

>>>

>>> u2.check_password("foo")

False

>>> u2.check_password("tyke")

True

>>>

>>>

По выводу мы можем судить о том, как все работает. В нашем случае – отлично, и в базе данных теперь 2 человека, создавших свои аккаунты.

Интеграция Flask-Login

Чтобы запустить Flask-Login, необходимо осуществить импорт класса LoginManager из пакета flask_login и создать новый объект этого класса. 

#...

from werkzeug.security import generate_password_hash, check_password_hash

from flask_login import LoginManager




app = Flask(__name__)

app.debug = True

app.config['SECRET_KEY'] = 'a really really really really long secret key'

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:pass@localhost/flask_app_db'

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

app.config['MAIL_SERVER'] = 'smtp.googlemail.com'

app.config['MAIL_PORT'] = 587

app.config['MAIL_USE_TLS'] = True

app.config['MAIL_USERNAME'] = 'youmail@gmail.com'

app.config['MAIL_DEFAULT_SENDER'] = 'youmail@gmail.com'

app.config['MAIL_PASSWORD'] = 'password'




manager = Manager(app)

manager.add_command('db', MigrateCommand)

db = SQLAlchemy(app)

migrate = Migrate(app,  db)

mail = Mail(app)

login_manager = LoginManager(app)

#...

 Чтобы проверять пользователей, расширение использует несколько методов. Приведем таблицу с ними:

Метод Описание
is_authenticated() Возвращает True, если пользователь проверен (то есть, зашел с корректным паролем). В противном случае — False.
is_active() Возвращает True, если действие аккаунта не приостановлено.
is_anonymous() Возвращает True для неавторизованных пользователей.
get_id() Возвращает уникальный идентификатор объекта User.

Ограничение доступа к просмотру

На данный момент сайт не содержит никакой административной панели. В качестве нее будет выступать обычная страница. Чтобы не допустить пользователей, не прошедших авторизацию, к защищенным страницам, необходимо использовать декоратор login_required. Работает это следующим образом. 

#...

from flask_login import LoginManager, UserMixin, login_required

#...

@app.route('/admin/')

@login_required

def admin():

    return render_template('admin.html')

#...

С помощью этого декоратора мы вызываем функцию представления admin() исключительно в тех ситуациях, если пользователь прошел авторизацию. Если же страницу будет посещать человек, который не подтвердил свои права на ее просмотр, будет выдана ошибка 401.

Выводы

Конечно, функционал расширения значительно шире, чем тот, который мы рассмотрели. Это – лишь база. Но чтобы рассмотреть каждый нюанс использования Flask для работы с пользователями, необходимо много времени, а также личный опыт. В функционал расширения входит целый спектр возможностей по созданию формы авторизации, организации непосредственно процесса авторизации, а также выхода из аккаунтов.

ОфисГуру
Adblock
detector