Seb*_*ian 5 python flask flask-login
我对烧瓶和烧瓶登录很陌生,我已经为此苦苦挣扎了好几天。
我尝试像这样登录用户:
from creds import auth_username, auth_password, pgsql_dbuser, pgsql_dbpassword, pgsql_db1name
from flask import Flask, render_template, request, Response, redirect, url_for
from flask.ext.bcrypt import Bcrypt
from flask.ext.login import LoginManager, login_required, login_user, current_user, logout_user
import logging
import psycopg2
import uuid
import datetime
app = Flask(__name__)
app.secret_key = str(uuid.uuid4()) # <- required by login_manager.init_app(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'index'
@app.route('/', methods=['GET','POST'])
def index():
page_name = '/'
if request.method == 'POST':
email = request.form['email']
candidate_password = request.form['password']
user = finduserindbbyemail(email)
if user != None:
password_hash = checkuserpasswordindb(email)
if bcrypt.check_password_hash(password_hash, candidate_password):
user_object = User(user)
result = login_user(user_object) # <- here for successful login
return redirect(url_for('loggedin', user_object=type(user_object), user=user, result=result, current_user=current_user))
else:
user_object = User(user)
error_message = "The password you entered is incorrect"
return render_template('index.html', error_message=error_message)
else:
error_message = "The email address you entered does not match any we have in our records"
return render_template('index.html', error_message=error_message)
if request.method == 'GET':
return render_template('index.html')
Run Code Online (Sandbox Code Playgroud)
我有一个 User 类和一个用户回调:
class User():
def __init__(self, user):
self.user = user
def is_authenticated(self):
return True
def is_active(self):
return True
def is_anonymous(self):
return False
def get_id(self):
return unicode(self.user)
@login_manager.user_loader
def load_user(user):
con = psycopg2.connect(database=pgsql_db1name, user=pgsql_dbuser, password=pgsql_dbpassword, host='localhost')
uuid = "'"+user+"'"
cur = con.cursor()
cur.execute("SELECT uuid FROM users WHERE uuid = "+ uuid)
uuid = cur.fetchone()
con.close()
if uuid != None:
user = unicode(uuid[0])
return User.get_id(user)
else:
return None
Run Code Online (Sandbox Code Playgroud)
身份验证成功后(显然?),用户将被重定向到具有装饰器的登录页面@login_required。但应用程序没有加载登录页面,而是将用户重定向到登录页面,告诉我用户尚未登录?如果尝试将值发送到页面并删除@login_required装饰器以便我可以看到该页面,这就是我在“登录”后在浏览器中看到的内容:
current_user.is_authenticated() = False
current_user.is_active() = False
current_user.is_anonymous() = True
current_user.get_id() = None
user_object = <type 'instance'>
user = 2ca1296c-374d-43b4-bb7b-94b8c8fe7e44
login_user = True
current_user = <flask_login.AnonymousUserMixin object at 0x7f2aec80f190> Logout
Run Code Online (Sandbox Code Playgroud)
我的用户似乎尚未登录并被视为匿名?谁能看到我做错了什么吗?我很难理解这是如何运作的。
所以..我设法让它工作,但没有使用 user_loader 回调。无论出于何种原因,我的用户加载器表现出与此相同的行为: 使用静态用户的 Flask-login 总是产生 401-未经授权
不管怎样,我根据这个例子使用了 request_loader 回调: http://gouthamanbalaraman.com/blog/minimal-flask-login-example.html
因此,对于登录的用户,从这里开始:
if bcrypt.check_password_hash(password_hash, candidate_password):
user_object = User(user, password_hash)
result = login_user(user_object) # <- here for successful login
token = user_object.get_auth_token(user, password_hash)
return redirect(url_for('loggedin', token=token))
Run Code Online (Sandbox Code Playgroud)
我创建了一个用户对象,其中包含用户的 ID 及其密码哈希值。然后我登录用户。然后我使用itsdangerous 创建用户ID 和密码散列的时间序列化令牌。get_auth_token 函数是 User 类的一部分。它看起来像这样:
class User():
def __init__(self, user, password_hash):
self.user = user
self.password = password_hash
.
.
.
def get_auth_token(self, user, password):
data = [str(self.user), self.password]
return serializer.dumps(data, salt=serializer_secret)
Run Code Online (Sandbox Code Playgroud)
您需要在代码开头的某个位置创建一个序列化器:
serializer = URLSafeTimedSerializer(serializer_secret)
Run Code Online (Sandbox Code Playgroud)
因此,创建令牌后,将其作为 URL 查询参数传递到登录视图。当您尝试加载 login_required 页面(例如我的登录页面)(成功登录后 login_user 将我重定向到该页面)时,将执行 request_loader 回调。它看起来像这样:
@login_manager.request_loader
def load_user_from_request(request):
if request.args.get('token'):
token = request.args.get('token')
max_age = 1
try:
data = serializer.loads(token, salt=serializer_secret, max_age=max_age)
username = data[0]
password_hash = data[1]
found_user = finduserindbbyuuid(username)
found_password = checkuserpasswordindbbyuuid(username)
if found_user and found_password == password_hash:
user_object = User(found_user, password_hash)
if (user_object.password == password_hash):
return user_object
else:
return None
else:
return None
except BadSignature, e:
pass
else:
return None
Run Code Online (Sandbox Code Playgroud)
这就是我的 user_loader 失败的地方。我登录成功,但 user_loader 始终返回 None,因此我的用户将被视为匿名。
因此,请求加载器会检查请求 URL 的查询字符串中是否包含“token”参数。如果是这样,它会获取它的值并使用它的危险来反序列化数据。您可以使用定时序列化器使令牌过期,但也有非定时序列化器。令牌反序列化后,获取用户和密码哈希值并检查数据库是否存在,这与 user_loader 应该工作的方式完全相同..我想?我的 user_loader 不起作用,所以我很可能做错了。
无论如何,如果用户和密码在数据库中匹配,则返回用户对象和 bam,登录有效。
我不确定我这样做是否正确,因为我几乎是凭感觉行事。我看到人们使用 token_loader 而不是 request_loader 回调函数来加载令牌的示例,但我无法弄清楚如何设置和从客户端获取身份验证令牌。也许我会弄清楚......有一天......
如果你有同样的问题,也许这可能有帮助?或者让我知道你的想法
干杯