Flask账户注册(3)

Flask账户注册(3)

Flask账户注册

综合使用所学习过的知识,搭建登录页面

代码示例:https://github.com/ningwenyan/demo_code/tree/master/flask_demo_code/T26

总览

❯ tree
.
├── app.py
├── auth
│   ├── auth_form.py
│   ├── __init__.py
│   └── views.py
├── common
│   ├── errors.py
│   ├── exts.py
│   ├── __init__.py
│   ├── mailModel.py
│   └── sqlModel.py
├── config.py
├── manager.py
├── migrations
│   ├── alembic.ini
│   ├── env.py
│   ├── README
│   ├── script.py.mako
│   └── versions
│       ├── 44bc21866b0d_.py
│       └── __pycache__
│           └── 44bc21866b0d_.cpython-37.pyc
├── static
└── templates
    ├── 401.html
    ├── 403.html
    ├── 404.html
    ├── 500.html
    └── auth
        ├── change_email.html
        ├── change_password.html
        ├── index.html
        ├── login.html
        ├── mail
        │   ├── change_email.html
        │   ├── change_email.txt
        │   ├── confirm.html
        │   ├── confirm.txt
        │   ├── password_reset.html
        │   └── password_reset.txt
        ├── main
        │   └── reset_password.html
        ├── personal.html
        ├── register.html
        └── reset_password.html

创建数据库迁移脚本

生成manager.py用于管理数据库版本

from flask_script import Manager
from common.exts import db
from flask_migrate import MigrateCommand, Migrate
from app import app


# 导入Manager并绑定app
manager = Manager(app)

# 导入flaks_migrate
# Migrate 绑定app,db
Migrate(app, db)
# MigrateCommand 可以使用Alembic的命令
#

manager.add_command('db', MigrateCommand) # db是别名


if __name__  == '__main__':
   manager.run()

测试,并生成数据库版本

$  python manager.py db --help
$  python manager.py db init
$  python manager.py db migrate
$  python manager.py db upgrade

查看数据库

mysql root@192.168.0.101:flask_login_demo1> show tables;                        
+-----------------------------+
| Tables_in_flask_login_demo1 |
+-----------------------------+
| alembic_version             |
| users                       |
+-----------------------------+
2 rows in set
Time0.026s

运行程序,并注册账户

运行并测试程序.

尝试注册一个用户,并在数据库中查看

mysql root@192.168.0.101:flask_login_demo1> select * from users;  
+----+-------------------+----------+--------------------------------------------------------------+-----------+
| id | email             | username | _password_hash                                               | confirmed |
+----+-------------------+----------+--------------------------------------------------------------+-----------+
| 1  | admin@foxmail.com | admin    | $2b$12$bS4hsyeeqawgd.bmT2Z4ku.gNox02T6HKXzsWRIezm98HpIGKXegq | 0         |
+----+-------------------+----------+--------------------------------------------------------------+-----------+

可以看到密码是密文存储在服务器上的.

然后尝试在未登录的情况下访问http://127.0.0.1:5000/auth/personal/,它会返回一个错误页面.它是401错误,我们在errors.py中添加一下401错误.

# 设计401, 403, 404, 500错误
@common_bp.app_errorhandler(401) # 未授权错误
def unauthorized(e):
   return render_template('401.html'), 401

添加session存储和过期时间操作

# views.py

@auth_bp.route('/login/', methods = ['GET', 'POST'])
def login():
   form = LoginForm()
   if form.validate_on_submit():
       """登录用户"""
       email = form.email.data.lower()
       password = form.password.data
       confirmed  = form.confirmed.data
       user = User.query.filter_by(email=email).first()
       # 数据库中有账户,并且能够验证密码
       if user is not None and user.check_password(password):
           print(confirmed)
           if confirmed:
               login_user(user, remember=True, duration=timedelta(days=30))
           else:
               login_user(user)
           # 判断 next
           next = request.args.get('next')
           if next is None or not next.startswitch('/'):
               next = redirect(url_for('auth.index'))
           return redirect(url_for('auth.personal' or next))
       else:
           flash('无效的邮箱或密码.')
   return render_template('auth/login.html')

设置发送邮件注册

使用Flask Mail配置邮箱,在用户注册时,发送一个注册邮件给用户.

# config.py
# 设置邮箱
MAIL_SERVER = 'smtp.qq.com'
MAIL_PORT = 465
MAIL_USERNAME = '257@qq.com'
MAIL_PASSWORD = "imkzffddijc"
MAIL_USE_SSL = True
MAIL_USE_TLS = False
MAIL_DEFAULT_SENDER = '257@qq.com'

配置app

# exts .py
from flask_mail import Mail
mail = Mail()
# app.py 
from common.exts import mail
mail.init_app(app)
# mailModel.py

from flask_mail import Message

from flask import render_template, current_app
from threading import Thread
from common.exts import mail

# 异步发送邮件
def async_send_mail(app, msg):
   # 要求在Flask的一次访问中发送邮件,下面代码中新建的线程中并
   # 不包含 上下文结构,手动推送
   with app.app_context():
       mail.send(msg)


def sendMail(to, subject, template, **kwargs):
   try:
       # 创建邮件
       msg = Message(subject, recipients=[to])
       # 回传浏览器
       msg.body = render_template('auth/mail/'+ template + '.txt', **kwargs)
       msg.html = render_template('auth/mail/' + template + '.html', **kwargs)
       print('auth/mail/{}'.format(template) + '.txt')
       print('auth/mail/{}'.format(template) + '.html')
       # 创建一个新线程,发送邮件
       # 根据flask上下文,如果不再同一个 app 中,将无法发送邮件
       app = current_app._get_current_object()
       thread = Thread(target=async_send_mail, args=[app, msg])
       thread.start()
       return thread
   except Exception as e:
       print(e)

更新视图

#views.py

from . import auth_bp
from flask import render_template, redirect, url_for, request, flash
from .auth_form import LoginForm, RegisterForm
from common.exts import db
from common.sqlModel import User
from flask_login import login_user, login_required, current_user
from datetime import timedelta
from common.mailModel import sendMail

@auth_bp.route('/')
def index():
   return render_template('auth/index.html')

# 设计注册,登录页面

@auth_bp.route('/register/', methods = ['GET', 'POST'])
def register():
   form = RegisterForm()
   if form.validate_on_submit():
       """注册账户"""
       email = form.email.data.lower()
       username = form.username.data
       password = form.password.data
       user = User(email, username, password)
       db.session.add(user)
       db.session.commit()

       # 生成发送邮件token
       token = user.generate_confirmation_token()
       # 将token发送到用户邮箱中,我们希望用户的激活形式为 http://xx.com/auth/confirm/<token>
       sendMail(user.email, '激活账户''confirm', user=user, token=token)
       flash("请通过注册邮箱激活账户!")
       return redirect(url_for('auth.login'))
   return render_template('auth/register.html')

@auth_bp.route('/login/', methods = ['GET', 'POST'])
def login():
   form = LoginForm()
   if form.validate_on_submit():
       """登录用户"""
       email = form.email.data.lower()
       password = form.password.data
       confirmed  = form.confirmed.data
       user = User.query.filter_by(email=email).first()
       # 数据库中有账户,并且能够验证密码
       if user is not None and user.check_password(password):
           # print(confirmed)
           if confirmed:
               login_user(user, remember=True, duration=timedelta(days=30))
           else:
               login_user(user)
           # 判断 next
           next = request.args.get('next')
           if next is None or not next.startswitch('/'):
               next = redirect(url_for('auth.index'))
           return redirect(url_for('auth.personal' or next))
       else:
           flash('无效的邮箱或密码.')
   return render_template('auth/login.html')

# 限制登录用户访问 login_required
@auth_bp.route('/personal/')
@login_required
def personal():
   return render_template('auth/personal.html', user = current_user)


# 设置激活邮件的路由
@auth_bp.route('/confirm/<token>')
@login_required
def confirm(token):
   # flask_login可以通过 current_user 代理访问用户
   # 激活的账户直接跳转到 index 页面
   if current_user.confirmed:
       return redirect(url_for('auth.index'))
   # 验证序列化token
   if current_user.check_confirmation_token(token):
       # 设置数据库标志位为 True
       db.session.commit()
       flash('您的账户已激活,请登录!')
   else:
       flash('激活链接不正确或已过期!')
   return redirect(url_for('auth.index'))

# 如果激活过期,再次发送激活邮件
@auth_bp.route('/confirm/')
@login_required
def resend_confirmation():
   token = current_user.generate_confirmation_token()
   sendMail(current_user.email, '激活账户''confirm', user=current_user, token=token)
   flash("一个新的激活连接已通过邮箱发送给您,请点击激活!")
   return redirect(url_for('auth.index'))

@auth_bp.route('/logout/')
@login_required
def logout():
   return redirect(url_for('auth.index'))

需要注意的是flash()消息要在前端接收一下.

{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
{{ message }}
{% endfor %}
{% endif %}
{% endwith %}

– END –


原文始发于微信公众号(Flask学习笔记):Flask账户注册(3)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/36404.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!