User Log-in operations (Kullanıcı giriş işlemleri)
Password Hashing
Usermodelindepassword_hashalanını tanımladık ancak password hashing işlemini gerçekleştirmedik.Hashing; matematiksel bir fonksiyon kullanarak belirli bir string veya karakterlerden bir değer veya değerler üretmektir.- Password Hashing ise parolaların belirli fonksiyonlar kullanılarak mevcut parola stringi yerine daha güçlü, karmaşık, güvenli değer üretme işlemidir.
- Werkzeug'un bize sunduğu method yardımı ile projemizde parolalarımızı güvenli şekilde veritabanında tutacağız.
xxxxxxxxxx>>> from werkzeug.security import generate_password_hash>>> hashed_password = generate_password_hash("gizli-parolam")>>> hashed_password'pbkdf2:sha256:50000$xhYmUxv5$5094f0409457656a174c65b7807381751a2c261b20220c9b0319cc713eb603c0'- Gördüğünüz üzere
gizli-parolamstringine karşılık üretilen değer oldukça güçlü. - Her seferinde farklı string üretilir. Böylece aynı parolaya sahip kullanıcılar için hash sonucu farklı olur ve tespit edilmesi imkansız değerler üretilir.
xxxxxxxxxx>>> from werkzeug.security import check_password_hash>>> check_password_hash(hashed_password, 'gizli-parolam')True>>> check_password_hash(hashed_password, 'cok-cok-gizli-parolam')False- Hash işlemi yapılmış parolanın kontrolü için de
check_password_hash()methodunu kullanıyoruz. - Şimdi password hash işlemini
Usermodelimize uygulayalım.myapp/models.py
xxxxxxxxxxfrom werkzeug.security import generate_password_hash, check_password_hashclass User(db.Model): #... def set_password(self, password): """Parola için hash oluşturur""" self.password_hash = generate_password_hash(password) def check_password(self, password): """Parolayı kontrol et""" return check_password_hash(self.password_hash, password)- Bu methodlar yardımı ile kullanıcı güçlü ve güvenli bir parolaya sahip olacak.
flask shellile terminalden Shell ortamına geçelim.
xxxxxxxxxx>>> User<class 'myapp.models.User'>>>> u = User(username='adnan', email='adnan@kaya.com')>>> u.set_password('cok-gizli-parolam')>>> u.password_hash'pbkdf2:sha256:50000$IqvJtY5p$d3c2b06a8c7cb79fc40bf267863b3afc9f524366579bca8d899f0597df97e908'>>> u.check_password('parolan-guclu-mu-?')False>>> u.check_password('cok-gizli-parolam')TrueFlask Log-in kullanımı
Flask Log-in Extension'ı:
- Kullanıcı log-in olmuşsa, görebileceği sayfalar arasında tekrar tekrar giriş yapmadan (log-in olmadan) gezinebilir.
Remember me(Beni hatırla) fonksiyonelliği sağlar. Böylece browser kapansa bile giriş yapmaya olanak sağlar.pip install flask-loginile extension'ı kuruyoruz.- Diğer extension'lar gibi Flask-Login de
myapp/__init__.pyiçerisinde application instance oluşturulduktan sonra eklenecek.
xxxxxxxxxxfrom flask_login import LoginManager#...app = Flask(__name__)#...login = LoginManager(app)Flask-Login için
Usermodelin hazırlanması
is_authenticated: Kullanıcı geçerli yetkilere sahip ise True değilse False döner.is_active: Kullanıcı aktif ise True değilse False döner.is_anonymous: Kullanıcı sıradan kullanıcı ise False, değilse True döner.get_id(): Kullanıcı için unique identifier dönen method.- Yukarıdaki dörtlüyü
UserMixinsınıfı ile kolaycaUsermodelimize implement edebiliriz.myapp/models.py
xxxxxxxxxx# ...from flask_login import UserMixinclass User(UserMixin, db.Model): # ...- Burada ayrıca pythondaki multiple inheritance örneğini de görmektesiniz.
user_loader Fonksiyonu.
- Flask Login, Flask'in user sessionında(uygulamaya bağlanan her kullanıcı için atanmış depolama alanı) giriş yapan kullanıcının unique ID'sini izler.
- Kullanıcı sayfayı değiştirdikçe Flask Login user session'dan kullanıcı ID'sini okur ve kullanıcıyı hafızaya yükler.
- Flask Login'in kullanıcıyı ID'sinden yüklemesi için
myapp/models.pymodülümüzeload_usermethodu ekleyeceğiz.
xxxxxxxxxxfrom myapp import login# ....user_loaderdef load_user(id): return User.query.get(int(id))user_loaderFlask Login extension'ı ile kayıtlıdır.- Flask Login
id'yi string olarak fonksiyona göndereceği için database query işleminde integer'a convert ettik.
Projemizde Kulanıcı Giriş İşlemleri (User Login Operations)
myapp/routes.py
xxxxxxxxxxfrom flask_login import current_user, login_user.route('/login', methods=['GET', 'POST'])def login(): """Kullanıcı login isteklerini karşılayacak method""" if current_user.is_authenticated: return redirect(url_for('index')) form = LoginForm() if form.validate_on_submit(): user = User.query.filter_by(username=form.username.data).first() if user is None or not user.check_password(form.password.data): flash("Geçersiz kullanıcı adı veya parola") return redirect(url_for('login')) login_user(user, remember=form.remember_me.data) return redirect(url_for('index')) return render_template('login.html', title="Giriş Yap", form=form)current_user: Flask Login extension'ının bize sağladığı, istemciyi(client) temsil eden user objesidir.is_authenticateddaha önce bahsedildiği gibi kullanıcı login olmuşsa True değilse False dönen özelliktir(property).filter_by()methodunu kullanarak formdan gelen kullanıcı adıyla database query çalıştırılıyor vefirst()methodu ile query tamamlanıyor.first()methodu unutulmamalı.
xxxxxxxxxx>>> user = User.query.filter_by(username='adnan').first()>>> type(user)<class 'myapp.models.User'>>>> user = User.query.filter_by(username='adnan')>>> type(user)<class 'flask_sqlalchemy.BaseQuery'>- Gördüğünüz üzere tipler farklı. query tamamlanması için first( ) methodunu da yazacağız.
Usermodele yazdığımızcheck_password()methodu yardımı ile formdan gelen password bigisini kontrol ediyoruz. username veya password geçersiz iseloginekranına yönlendiriyoruz.login_user()methodu da Flask Login extension'ının bize sağladığı bir method. Aldığıuserobjesi ile login işlemini tamamlar.
Log-out (Kullanıcı çıkış) işlemi:
myapp/routes.py
xxxxxxxxxxfrom flask_login import current_user, login_user, logout_user.route('/logout')def logout(): """Kullanıcı logout isteklerini karşılar""" logout_user() return redirect(url_for('index'))- Kullanıcılara log-in & log-out linklerini göstermek için,
base.html'de düzenleme yapalım.
xxxxxxxxxx<td><a href="{{url_for('index')}}">Anasayfa</a></td>{% if current_user.is_anonymous %} <td><a href="{{url_for('login')}}">Giriş Yap</a></td>{%else%} <td><a href="{{url_for('logout')}}">Çıkış Yap</a></td>{%endif%}- Eğer kullanıcı log-in olmamışsa anonymous'tur. Giriş Yap linki görünecektir.
Eğer kullanıcıyı login olmaya mecbur bırakmak istiyorsanız:
myapp/__init__.pyiçerisine şu satırı ekleyin.
xxxxxxxxxx# ...login = LoginManager(app)login.login_view = 'login'- Bu satır ile beraber yetki gerektiren sayfalarda gezinmek için login olmak mecburi hale getirildi.
Belirli sayfalara yetki sınırlaması eklemek istiyorsanız
@app.route'dan sonra@login_requireddecorator ekleyerek, log-in olmayan kullanıcılara sınırlama getirebilirsiniz.myapp/routes.py
xxxxxxxxxxfrom flask_login import login_required.route('/').route('/index')def index(): # ...- Eğer kullanıcı
/indexsayfasına istek yaparsa@login_requireddecorator, URL sonuna bir string ekleyecek. URL :/login?next=%2Findexolacak.nextstringi orjinal URL'e ayarlanmıştır. Böylecek kullanıcı log-in olduktan sonra uygulamaredirectederek next'ten sonra gelen sayfaya yönlendirme yapabilir.
xxxxxxxxxxfrom flask import requestfrom werkzeug.urls import url_parse.route('/login', methods=['GET', 'POST'])def login(): # ... if form.validate_on_submit(): user = User.query.filter_by(username=form.username.data).first() if user is None or not user.check_password(form.password.data): flash('Invalid username or password') return redirect(url_for('login')) login_user(user, remember=form.remember_me.data) next_page = request.args.get('next') if not next_page or url_parse(next_page).netloc != '': next_page = url_for('index') return redirect(next_page) # ...Flask
requestdeğişkeni ile istemcinin gönderdiği bütün bilgiyi tutar.request.argsdictionary tipinde query string içeriğini barındırır.Login olduktan sonra 3 muhtemel durum var:
- Eğer login URL
nextparametresine sahip değilse , kullanıcı index sayfasına yönlendirilir. - Eğer login URL
nextparametresine sahipse, index parametresi de ilgili path'e ayarlanmış(atanmış, belirlenmiş) ise o zaman kullanıcı belirlenmiş URL'e yönlendirilir. - Eğer login URL
nextparametresine sahipse ve tam URL'e(domain name'i içeren) ayarlanmış ise o zamanindexsayfasına yönlendirilir. - 3.durum (tam domain name'i içeren URL) daha güvenlidir çünkü saldırganlar
nextparametresine zaafiyetlerinizden yararlanarak başka siteye gidecek şekilde URL ataması yapabilir. - absolute URL örneği:
<a href="https://adnankayace.blogspot.com">Blog</a> - relative URL örneği:
<a href="/blog">Blog</a>. - URL'imizin absolute mü relative mi olup olmadığını tespit etmek için
url_parse()kullanıyoruz. Aşağıdaki örnek ilenetloc'un (network location part) değerini görebilirsiniz. Bizim örneğimizde de netloc componenti ayarlanmış mı ayarlanmamış mı diye kontrol ediyoruz.
- Eğer login URL
xxxxxxxxxx>>> from werkzeug.urls import url_parse>>> url_parse('https://adnankayace.blogspot.com:80/python/flask.html')URL(scheme='https', netloc='adnankayace.blogspot.com:80', path='/python/flask.html', query='', fragment='')index.htmliçinde ufak bir değişiklik yapıp, gerçek kullanıcıları gösterelim artık.
xxxxxxxxxx...<h1>Selam {{current_user.username}}! </h1>...- Ve
myapp/routes.pyiçindeindex()metodunu aşağıdaki gibi düzenleyelim. Geçici belirlediğimiz user değişkenini kaldırdık.postsdeğişkeni de database query'den gelen dataları alıyor.
xxxxxxxxxx.route('/').route('/index')def index(): posts = Post.query.all() return render_template('index.html', title='Anasayfa', posts=posts)Kullanıcı Kayıt İşlemleri (User Registration)
xxxxxxxxxx# externalfrom flask_wtf import FlaskFormfrom wtforms import StringField, PasswordField, BooleanField, SubmitFieldfrom wtforms.validators import DataRequired, Email, EqualTo, ValidationError#internalfrom myapp.models import User#...class RegistrationForm(FlaskForm): """ Kullanıcı kayıt formu""" username = StringField('Kullanıcı Adı', validators=[DataRequired()]) email = StringField('Email', validators=[DataRequired(), Email()]) password = PasswordField('Parola', validators=[DataRequired()]) password2 = PasswordField('Parola Doğrula', validators=[DataRequired(), EqualTo('password')]) submit = SubmitField('Register') def validate_username(self, username): user = User.query.filter_by(username=username.data).first() if user is not None: raise ValidationError("Lütfen başka bir kullanıcı adı giriniz") def validate_email(self, email): user = User.query.filter_by(email=email.data).first() if user is not None: raise ValidationError("Lütfen başka bir e-posta giriniz")validators='teki Email( ) validator sınıfı, email formatında adres girilmesi için eklendi.- İki adet parola alıyoruz, bu kullanıcıyı doğru parola girmeye yönlendirmek ve yanlış parola riskini azaltmak içindir.
EqualTo()başka bir validator olarak kullanılıyor. İlk parolaya eşit olup olmadığını kontrol eden sınıftır. validate_<field_name>ile WTForms bu methodları özel validator(doğrulayıcı) olarak çağırır.
templates/register.html
xxxxxxxxxx{%extends 'base.html'%} {%block content%}<h1>Kaydol</h1><form action="" method="POST"> {{form.hidden_tag()}} <table> <tr> <td>{{form.username.label}}</td> <td>{{form.username(size=20)}}</td> <td>{% for error in form.username.errors %} <span style="color: red;">[{{ error }}]</span> {% endfor %} </td> </tr> <tr> <td>{{form.email.label}}</td> <td>{{form.email(size=20)}}</td> <td>{% for error in form.email.errors %} <span style="color: red;">[{{ error }}]</span> {% endfor %} </td> </tr> <tr> <td>{{form.password.label}}</td> <td>{{form.password(size=20)}}</td> <td>{% for error in form.password.errors %} <span style="color: red;">[{{ error }}]</span> {% endfor %} </td> </tr> <tr> <td>{{form.password2.label}}</td> <td>{{form.password2(size=20)}}</td> <td>{% for error in form.password2.errors %} <span style="color: red;">[{{ error }}]</span> {% endfor %} </td> </tr> </table> <p>{{ form.submit() }}</p></form>{%endblock%}
base.html'de kayıt sayfası için link ekleyelim.
xxxxxxxxxx<td><a href="{{url_for('login')}}">Giriş Yap</a></td><td><a href="{{url_for('register')}}">Kaydol</a></td>
myapp/routes.pymodülüne deregister()methodumuzu yazalım.
xxxxxxxxxxfrom myapp import dbfrom myapp.forms import RegistrationForm# ....route('/register', methods=['GET', 'POST'])def register(): """Yeni kullanıcı kayıt""" if current_user.is_authenticated: return redirect(url_for('index')) form = RegistrationForm() if form.validate_on_submit(): user = User(username=form.username.data, email=form.email.data) user.set_password(form.password.data) db.session.add(user) db.session.commit() flash("Tebrikler yeni kullanıcı eklendi!") return redirect(url_for('index')) return render_template('register.html', title="Kaydol", form=form)- Böylece kullanıcı kaydını da tamamlamış olduk.

Yorumlar