使用MongoDB的Flask-login usermixin类

2w3kk1z5  于 2023-04-11  发布在  Go
关注(0)|答案(4)|浏览(283)

我正在努力尝试构建一个登录方法一段时间了。我正在运行一个Flask应用程序,并使其运行良好。它都在我的机器上本地运行。目前,我使用pymongoMongoClient来连接到数据库。这一切都运行良好,如果可能的话,我不想改变这一点。
我尝试使用Flask-Login来创建一个users类,使用usermixin。这是我非常不成功的地方。我尝试了一些不同的事情,我的问题是如何从我的数据库中提取数据。我以前用SQL数据库做过这件事,但对于这个项目,我明确地想使用MongoDB。这是我试图遵循的教程,但我很难理解一切,因为它没有很好地解释每一行在做什么。
https://medium.com/@dmitryrastorguev/basic-user-authentication-login-for-flask-using-mongoengine-and-wtforms-922e64ef87fe
这是我与DB的连接:client = MongoClient('mongodb://localhost:27017')
这是我当前的用户类,我没有工作,我需要帮助。

class User(UserMixin):

  def __init__(self, username, password_hash):
    self.username = username
    self.password_hash = password_hash

  def check_password(self, password):
    return check_password_hash(self.password_hash, password)

  def get_id(self):
    return self.username

@login_manager.user_loader
def load_user(user_id):
    return User.objects(pk=user_id).first()

最后一部分是我的登录表单:

@app.route('/login', methods=["GET" , "POST"])
def login():
  if request.method == "GET":
    return render_template("login.html", error=False)
  if request.method == "POST":
    check_user = request.form["username"]
    if check_user:
      if check_password_hash(check_user['password'], request.form["password"]):
        login_user(check_user)
        return redirect(url_for('index'))

我知道本教程使用MongoEngine,我没有使用,或者还没有,但这里有一些帮助,无论是如何让上面的代码工作或如何适应它将是伟大的。当我运行此代码时,我没有得到任何错误,它只是不工作。我的测试是我尝试登录,然后我尝试转到注销页面,其中加载了以下代码:

@app.route("/logout")
@login_required
def logout():
  logout_user()
  return redirect(url_for('index'))

当我这样做时,它不会加载页面,我得到了未经授权的页面通知。因此,我知道我的代码不工作。最后,我在一个静态文件位置的所有模板。
提前感谢您的帮助,如果有任何不清楚的地方,请询问,我会尝试添加更多的细节。越具体,我将能够帮助更好。

更新:

我意识到,描述我的数据库是如何结构化的,以确保我正确地访问它,这可能也很重要,因为这是我遇到问题的一个主要点。我有一个数据库,我的集合名为Users,它的结构是每个文档都是不同的用户记录,如下所示:

{
    "_id" : 1,
    "Reset" : false,
    "FirstName" : "John",
    "LastName" : "Doe",
    "Email" : "JohnDoe@gmail.com",
    "Username" : "",
    "admin" : false,
    "Pass" : "[hashed_password]"
}
{
    "_id" : 2,
    "Reset" : true,
    "FirstName" : "Jane",
    "LastName" : "Smith",
    "Email" : "JaneSmith@hotmail.com",
    "Username" : "Jane",
    "admin" : false,
    "Pass" : "[hashed_password]"
}
{
    "_id" : 3,
    "Reset" : true,
    "FirstName" : "Gary",
    "LastName" : "Bettman",
    "Email" : "GBettman@yahoo.com",
    "Username" : "HockeyGuy",
    "admin" : false,
    "Pass" : "[hashed_password]"
}
jtw3ybtb

jtw3ybtb1#

关于Flask-login你需要知道的:此扩展与应用程序的用户模型一起工作,并期望在其中实现某些属性和方法。(源代码:https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-v-user-logins)。
以下列出了四个必需项目:

  • is_authenticated:如果用户具有有效凭据,则为True,否则为False的属性。
  • is_active:如果用户的帐户处于活动状态,则为True,否则为False。
  • is_anonymous:对于普通用户为False,对于特殊匿名用户为True的属性。
  • get_id():以字符串形式返回用户的唯一标识符的方法

不幸的是,官方文档和Miguel Grinberg的优秀博客中的所有示例都使用SQLAlchemy。好消息是,可以使用Pymongo实现它...

解决方案

  1. routes.py
from flask import Flask
from flask_pymongo import PyMongo
from flask_login import LoginManager
from flask import render_template, url_for, request, flash
from app.forms import Login
from flask import request
from werkzeug.urls import url_parse
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import current_user, login_user, logout_user, login_required

mongo = PyMongo(app)
login = LoginManager(app)
login.login_view = 'login'

class User:
    def __init__(self, username):
        self.username = username

    @staticmethod
    def is_authenticated():
        return True

    @staticmethod
    def is_active():
        return True

    @staticmethod
    def is_anonymous():
        return False

    def get_id(self):
        return self.username

    @staticmethod
    def check_password(password_hash, password):
        return check_password_hash(password_hash, password)

    @login.user_loader
    def load_user(username):
        u = mongo.db.Users.find_one({"Name": username})
        if not u:
            return None
        return User(username=u['Name'])

    @app.route('/login', methods=['GET', 'POST'])
    def login():
        if current_user.is_authenticated:
            return redirect(url_for('index'))
        form = Login()
        if form.validate_on_submit():
            user = mongo.db.Users.find_one({"Name": form.name.data})
            if user and User.check_password(user['Password'], form.password.data):
                user_obj = User(username=user['Name'])
                login_user(user_obj)
                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)
            else:
                flash("Invalid username or password")
        return render_template('login.html', title='Sign In', form=form)

    @app.route('/logout')
    def logout():
        logout_user()
        return redirect(url_for('login'))
  1. form.py
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField
from wtforms.validators import DataRequired

class Login(FlaskForm):
    name = StringField('name' validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])
    login = SubmitField('Login')

假设我们在Mongodb端有一个集合(Users),其中包含一些登录信息。例如:

{
  Name: [username],
  Password: [hashed_password]
}

要进一步解释每行代码的作用,我建议您参考以下链接:

nzk0hqpo

nzk0hqpo2#

我发现下面的作品与flask-loginUserMixinpymongo
这里是用户模型

import datetime
import uuid
from depo import bcrypt, login_manager
from flask import session, flash
from depo.common.database import Database
from depo.models.blog import Blog
from flask_login import UserMixin

class User(UserMixin):

    def __init__(self, username, email, password, _id=None):

        self.username = username
        self.email = email
        self.password = password
        self._id = uuid.uuid4().hex if _id is None else _id

    def is_authenticated(self):
        return True
    def is_active(self):
        return True
    def is_anonymous(self):
        return False
    def get_id(self):
        return self._id

    @classmethod
    def get_by_username(cls, username):
        data = Database.find_one("users", {"username": username})
        if data is not None:
            return cls(**data)

    @classmethod
    def get_by_email(cls, email):
        data = Database.find_one("users", {"email": email})
        if data is not None:
            return cls(**data)

    @classmethod
    def get_by_id(cls, _id):
        data = Database.find_one("users", {"_id": _id})
        if data is not None:
            return cls(**data)

    @staticmethod
    def login_valid(email, password):
        verify_user = User.get_by_email(email)
        if verify_user is not None:
            return bcrypt.check_password_hash(verify_user.password, password)
        return False

    @classmethod
    def register(cls, username, email, password):
        user = cls.get_by_email(email)
        if user is None:
            new_user = cls( username, email, password)
            new_user.save_to_mongo()
            session['email'] = email
            return True
        else:
            return False

    def json(self):
        return {
            "username": self.username,
            "email": self.email,
            "_id": self._id,
            "password": self.password
        }

    def save_to_mongo(self):
        Database.insert("users", self.json())

这里是路线

from flask import flash, render_template, request, session, make_response,  redirect, url_for
from depo import app, bcrypt, login_manager
from depo.models.blog import Blog
from depo.models.post import Post
from depo.models.user import User
from depo.common.database import Database
from depo.usercon.forms import RegistrationForm, LoginForm
from flask_login import login_user

@app.before_first_request
def initialize_database():
    Database.initialize()

@app.route("/register", methods=['GET', 'POST'])
def register():
    form = RegistrationForm()
    if form.validate_on_submit():

        if request.method == 'POST':
            username = request.form["username"]
            email = request.form["email"]
            password = bcrypt.generate_password_hash(request.form["password"])
                       .decode('utf-8')
            find_user =  User.get_by_email(email)
            if find_user is None:
                User.register(username, email, password)
                flash(f'Account created for {form.username.data}!', 'success')
                return redirect(url_for('home'))
            else:
                flash(f'Account already exists for {form.username.data}!', 'success')
    return render_template('register.html', title='Register', form=form)
@app.route("/login", methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        email = request.form["email"]
        password = request.form["password"]
        find_user = Database.find_one("users", {"email": email})
        if User.login_valid(email, password):
            loguser = User(find_user["_id"], find_user["email"], find_user["password"])
            login_user(loguser, remember=form.remember.data)
            flash('You have been logged in!', 'success')
            return redirect(url_for('home'))
        else:
            flash('Login Unsuccessful. Please check email and password', 'danger')
    return render_template('login.html', title='Login', form=form)

@login_manager.user_loader
def load_user(user_id):
    user =User.get_by_id(user_id)
    if user is not None:
        return User(user["_id"])
    else:
        return None

如果尚未初始化,可能还需要app的一些代码

from flask_login import LoginManager
login_manager = LoginManager(app)
8xiog9wr

8xiog9wr3#

我遇到了同样的问题,https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-v-user-logins
我做了一些修改,让它在Mongo中运行,但我确实从MongoClient切换到了MongoEngine。

__init__.py

from flask import Flask
from config import Config
from flask_mongoengine import MongoEngine, Document
from flask_login import LoginManager

app = Flask(__name__)
app.config.from_object(Config)

login = LoginManager(app)
login.login_view = 'login'

db = MongoEngine(app)

from app import routes
from app.model import user

.

app/model/user.py

from datetime import datetime
from app import db, login
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin

@login.user_loader
def load_user(id):
    return User.objects.get(id=id)

class User(UserMixin, db.Document):
    meta = {'collection': 'users'}
    username = db.StringField(default=True)
    email = db.EmailField(unique=True)
    password_hash = db.StringField(default=True)
    active = db.BooleanField(default=True)
    isAdmin = db.BooleanField(default=False)
    timestamp = db.DateTimeField(default=datetime.now())

    def __repr__(self):
        return '<User {}>'.format(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)

.

app/forms.py

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
from app.model.user import User

class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])
    remember_me = BooleanField('Remember Me')
    submit = SubmitField('Sign In')

class RegistrationForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Password', validators=[DataRequired()])
    password2 = PasswordField(
        'Repeat Password', validators=[DataRequired(), EqualTo('password')])
    submit = SubmitField('Register')

    def validate_username(self, username):
        user = User.objects(username=username.data).first()
        if user is not None:
            raise ValidationError('Please use a different username.')

    def validate_email(self, email):
        user = User.objects(email=email.data).first()
        if user is not None:
            raise ValidationError('Please use a different email address.')

.

app/routes.py

from flask import render_template, flash, redirect, request, url_for
from app import app, db
from app.forms import LoginForm, RegistrationForm
from flask_login import current_user, login_user, login_required, logout_user
from app.model.user import User

@app.route('/')
@app.route('/index')
@login_required
def index():
    posts = [
        {
            'author': {'username': 'John'},
            'body': 'Beautiful day in Portland!'
        },
        {
            'author': {'username': 'Susan'},
            'body': 'The Avengers movie was so cool!'
        }
    ]
    return render_template("index.html", title='Home Page', posts=posts)

@app.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('index'))
    form = LoginForm()
    if form.validate_on_submit():
        user = User.objects(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)
    return render_template('login.html', title='Sign In', form=form)

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('index'))

@app.route('/register', methods=['GET', 'POST'])
def register():
    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)
        user.save()
        flash('Congratulations, you are now a registered user!')
        return redirect(url_for('login'))
    return render_template('register.html', title='Register', form=form)
z6psavjg

z6psavjg4#

关于Sazzad
在Flask文档中:

def get_id(self):
    try:
        return str(self.id)
    except AttributeError:
        raise NotImplementedError("No `id` attribute - override `get_id`") from None

所以,不是“_id”就是“id”

相关问题