第六课 Flask用户注册数据加密手机邮箱激活和内置对象

第六课 Flask用户注册数据加密手机邮箱激活和内置对象

tags:

  • Flask
  • 2019千锋教育

categories:

  • flask
  • 钩子函数
  • 内置对象q和Config
  • 用户注册数据加密
  • 邮箱发送
  • 手机激活

第一节 钩子函数

1.1 钩子函数介绍

  1. 钩子函数面向切面编程,动态介入请求流程。
  2. 请求钩子(相当于中间件)
    • before first request
    • before request
    • after reguest
    • teardown request 请求关闭
  3. 可以使用蓝图的,也可以直接使用app的。
# views.py
# 统计、做反扒、优先级、频率反扒、用户登录的判断
from flask import request
@blue.before_request
def before():
    print(request.url)
    
# 界面统一的动态加载、DebugToBar
@blue.after_request
def after(res):
    print("after", res)
    print(type(res))
    return res

1.2 Django面试题:# Djangs请求流程

  1. 普通回答(连60都不能给你呀兄弟)
    • urls -> views
    • views -> models
    • models -> views
    • views -> response
  2. 添加中间件回答
    • client -> process_ request [ ] (逐一得进行便利process_ request)
    • 逐一进行process_ request
    • process_request -> urls
    • urls -> process view
    • 逐一进行pprocess view
    • process view -> views
    • views -> models
    • models -> views
    • views -> response
    • response -> process response []
    • 逐一进行pprocess response

1.3 把它写成中间件

  1. 创建middleware.py文件。
from flask import request


def load_middleware(app):
    """
    统计
    优先级
    反爬
    频率
    用户认证
    用户权限
    """
    @app.before_request
    def before():
        print("中间件", request.url)

    @app.after_request
    def after(res):
        # 一定要有返回
        print("after", res)
        print(type(res))
        return res
  1. 改__init__.py文件
from flask import Flask
from App.views import blue
from App.ext import init_ext
from App.settings import envs
from App.middleware import load_middleware


def create_app(env):
    app = Flask(__name__)
    app.register_blueprint(blue)
    app.config.from_object(envs.get(env))
    init_ext(app=app)
    load_middleware(app=app)
    return app

1.4 四大内置对象(g和Config(又称app))

  1. cookie和session已经在前面讲过了。
  2. g 可以用来跨函数传递数据的。
  3. 无论哪种服务器,为每个用户的请求创建一个线程(内部维护一个线程池)。变量数据是隔离开的。
@blue.before_request
def before():
	g.msg = "hhd"
    print(request.url)

@blue.after_request
def after(res):
	print(g.msg)
    return g.msg
  1. config. 它实际上是我们的App的配置信息
<ul>
{% for con in config %}
	<li>{{ con }}={{ config|con] }}</li>
{% endfor%}
</ul>
from flask import request
from flask import current_app
# 这里直接导入的config, 并不是四大内置对象的config.
# 从flask 中导入 request
@blue.before_request
def before():
    conf = current_app.config
    keys = conf.keys()
    for key in keys:
        print(key, conf[key])
    print(request.url)

第二节 注册登录-数据加密

2.1 注册登录基础设计

  1. 前端的加密是为了数据传输安全,后台加密为了防家贼(后台开发人员)。
  2. 注册登录基础设计如下。
# models.py
from werkzeug.security import check_password_hash


class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    s_name = db.Column(db.String(15), unique=True)
    s_password = db.Column(db.String(256))

    def check_password(self, password):
        return check_password_hash(self.s_password, password)
# views.py
@blue.route('/student/register/', methods=['GET', 'POST'])
def student_register():
    if request.method == "GET":
        return render_template("student_register.html")

    if request.method == "POST":
        username = request.form.get("username")
        password = request.form.get("password")
        hash_pwd = generate_password_hash(password)
        student = Student()
        student.s_name = username
        student.s_password = hash_pwd
        db.session.add(student)
        db.session.commit()
        return "注册成功"


@blue.route('/student/login/', methods=['GET', 'POST'])
def student_login():
    if request.method == "GET":
        return render_template("Student_Login.html")

    if request.method == "POST":
        username = request.form.get("username")
        password = request.form.get("password")
        student = Student.query.filter(Student.s_name.__eq__(username)).first()
        # if student and check_password_hash(student.s_password, password):
        #     return "Login Success"
        # 把密码检测定义到models中
        if student and Student.check_password(password):
            return "Login Success"
        return "密码输入错误"
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Student_Register</title>
</head>
<body>
<form action="{{ url_for('blue.student_register') }}" method="POST">
    <span>用户名:</span><input type="text" name="username" placeholder="请输入用户名">
    <br>
    <span>密 码:</span><input type="text" name="password" placeholder="请输入密码">
    <button>注册</button>
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Student_Login</title>
</head>
<body>
{% for message in get_flashed_messages() %}
    <li>{{ message }}</li>
{% endfor %}
<form action="{{ url_for('blue.student_login') }}" method="POST">
    <span>用户名:</span><input type="text" name="username" placeholder="请输入用户名">
    <br>
    <span>密 码:</span><input type="text" name="password" placeholder="请输入密码">
    <button>登录</button>
</form>
</body>
</html>

2.2 注册登录加密

  1. 上述登录功能可以实现基本功能。但是,这里显式的对代码加了密, 并不友好。
  2. hash_pwd = generate_password_hash(password)
  3. 使用property干预数据的设置过程。property 将函数编程一个属性
  4. property基本使用。这里可以复习下prpperty和反射。
import hashlib


class Student:
    def __init__(self, _password=None):
        self._password = _password

    @property
    def password(self):
        # 手动抛出异常让它不可访问
        # 设置密码和验证密码都有入口 但是不可以获取密码
        raise Exception("Error Action")
        # return self._password

    @password.setter
    def password(self, value):
        self._password = hashlib.new('md5', value.encode('utf-8')).hexdigest()


if __name__ == "__main__":
    student = Student()
    # 赋值110 获取就是加密后的值了
    student.password = "110"
    print(student.password)
  1. 应用到我们登陆时候的数据安全上。
# models.py
from werkzeug.security import check_password_hash, generate_password_hash


class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    s_name = db.Column(db.String(15), unique=True)
    # 把s_password变成私有属性
    _s_password = db.Column(db.String(256))

    @property
    def s_password(self):
        raise Exception("Error Action Password Cant be access")

    @s_password.setter
    def s_password(self, value):
        self._s_password = generate_password_hash(value)

    def check_password(self, password):
        return check_password_hash(self._s_password, password)
from App.models import Student


@blue.route('/student/register/', methods=['GET', 'POST'])
def student_register():
    if request.method == "GET":
        return render_template("student_register.html")

    if request.method == "POST":
        username = request.form.get("username")
        password = request.form.get("password")
        # hash_pwd = generate_password_hash(password)
        student = Student()
        student.s_name = username
        # 这里边的很酷啦
        student.s_password = password
        db.session.add(student)
        db.session.commit()
        return "注册成功"


@blue.route('/student/login/', methods=['GET', 'POST'])
def student_login():
    if request.method == "GET":
        return render_template("Student_Login.html")

    if request.method == "POST":
        username = request.form.get("username")
        password = request.form.get("password")
        student = Student.query.filter(Student.s_name.__eq__(username)).first()
        # if student and check_password_hash(student.s_password, password):
        #     return "Login Success"
        # 把密码检测定义到models中
        if student and Student.check_password(password):
            return "Login Success"
        # SECRET_KEY = "adsfasfASDAASDAKFHJDLASJLASDJ"
       	flash("用户名或密码错误")
        return redirect(url_for("blue.student_login"))
  1. 修改好数据库进行数据迁移。(这里可能会出现问题,数据库可能不是你想的那样)
    • python manage.py db migrate
    • python manage.py db upgrade
    • 到数据库观察一下,发现之前的s_password没了,多了_s_password字段。且之前的密码也都没了。所以迁移细节并不细致。
    • 删除数据库中的脏数据
    • 重新运行服务器添加

第三节 邮箱激活功能

  1. pip install flask-mail -i https://pypi.douban.com/simple
  2. 借鉴:http://www.bjhee.com/flask-ext2.html
  3. 导入ext.py注册
  4. settings.py中,配置邮箱基本信息。
    MAIL_SERVER = "smtp.163.com"
    MAIL_PORT = 25
    MAIL_USERNAME = "[email protected]"
    MAIL_PASSWORD = "XXXX"
    MAIL_DEFAULT_SENDER = MAIL_USERNAME
  1. 邮箱基本发送功能
from flask_mail import Message
from App.ext import mail


@blue.route("/sendmail/")
def send_mail():
    msg = Message("FLask Email", recipients=["[email protected]", ])
    msg.body = "哈哈 FLask不过如此"
    msg.html = "<h2>我靠 我真是天才呀</h2>"
    mail.send(message=msg)
    return "邮件发送成功!!"
  1. 邮箱激活的流程
    • 异步发送邮件
    • 在邮件中包含激活地址
      • 激活地址接收一个一次性的token
      • token是用户注册的时候生成的,存在了cache中
      • token是key-value格式的存在
        • key就是我们的 token
        • value用户的 一个唯一标识(id, 用户名,手机,邮箱等)

第四节 手机激活功能

4.1 测试API功能

  1. 手机激活流程 同步的
  2. models加入字段 s_phone = db.Column(db.String(32), unique=True)
  3. 然后去找发送的API。如:阿里云,这里推荐网易云信https://app.yunxin.163.com/index
  4. 推荐一个前端UI免费下载地址。http://www.jq22.com/jquery-info14563
  5. 创建应用,开通短息功能。(可以用来测试)-> 开发手册
  6. 写一个发送验证码的demo
import hashlib

import requests
import time


def send_code():
    url = "https://api.netease.im/sms/sendcode.action"
    # 生成一个随机数
    nonce = hashlib.new("sha512", str(time.time()).encode("utf-8")).hexdigest()
    # 设置当前时间
    curtime = str(int(time.time()))

    # 按照开发文档拼接参数
    sha1 = hashlib.sha1()
    secret = "XXXXXX"
    sha1.update((secret + nonce + curtime).encode("utf-8"))
    # 拼接SHA1(AppSecret + Nonce + CurTime),三个参数拼接的字符串,进行SHA1哈希计算,转化成16进制字符(String,小写)
    check_sum = sha1.hexdigest()

    header = {
        "AppKey": "XXXXXX",
        "Nonce": nonce,
        "CurTime": curtime,
        "CheckSum": check_sum
    }

    post_data = {
        "mobile": "XXXXXXXX" # 发送给的手机号
    }
    resp = requests.post(url, data=post_data, headers=header)
    print(resp.content)


if __name__ == '__main__':
    send_code()

4.2 添加用户激活功能到Flask

  1. 把上面代码复制到utils.py 工具类中。通过views.py的接口调用
#utils.py
import hashlib
import requests
import time


def send_verify_code(phone):
    url = "https://api.netease.im/sms/sendcode.action"
    # 生成一个随机数
    nonce = hashlib.new("sha512", str(time.time()).encode("utf-8")).hexdigest()
    # 设置当前时间
    curtime = str(int(time.time()))

    # 按照开发文档拼接参数
    sha1 = hashlib.sha1()
    secret = "xxxxxxxx" # app secret
    sha1.update((secret + nonce + curtime).encode("utf-8"))
    # 拼接SHA1(AppSecret + Nonce + CurTime),三个参数拼接的字符串,进行SHA1哈希计算,转化成16进制字符(String,小写)
    check_sum = sha1.hexdigest()

    header = {
        "AppKey": "xxxxxxxxxxx", # app key
        "Nonce": nonce,
        "CurTime": curtime,
        "CheckSum": check_sum
    }

    post_data = {
        "mobile": phone # 发送给的手机号
    }
    resp = requests.post(url, data=post_data, headers=header)
    return resp
  1. 添加缓存用来存储验证码。
from flask_caching import Cache


cache = Cache(
    config={
        "CACHE_TYPE":"redis"
    }
)

cache.init_app(app=app)
  1. 完成验证码接口和注册验证接口。
from App.utils import send_verify_code
from App.ext import cache
from flask import jsonify


@blue. route('/sendcode/')
def send_code():
    phone = request.args.get("phone")
    username = request.args.get("username")
    resp = send_verify_code(phone)
    print(resp.json())
    result = resp.json()
    if result.get("code") == 200:
        obj = result.get("obj")
        cache.set(username, obj)
        data = {
            "msg": "ok",
            "status": 200
        }
        return jsonify(data)
    data = {
        "msg": "fail",
        "status": 400
    }
    return jsonify(data)
    
# 注册中加入验证码    
@blue.route('/student/register/', methods=['GET', 'POST'])
def student_register():
    if request.method == "GET":
        return render_template("student_register.html")

    if request.method == "POST":
        username = request.form.get("username")
        password = request.form.get("password")
        # hash_pwd = generate_password_hash(password)
        # 前端输入验证码
        code = request.form.get("code")
        # 缓存中的验证码
        cache_code = cache.get(username)
        if code != cache_code:
            return "验证失败"
        student = Student()
        student.s_name = username
        student.s_password = password
        db.session.add(student)
        db.session.commit()
        return "注册成功"
  1. 前端注册页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>StudentRegister</title>
    <script src="http://libs.baidu.com/jquery/1.11.1/jquery.min.js"></script>
    <script type="text/javascript">
        $(function () {

            $("#get_code").click(function () {

                var username = $("#username").val();

                var phone = $("#phone").val();

                $.getJSON("/sendcode/",{"username":username, "phone": phone}, function (data) {

                    console.log(data);

                })

            })

        })
    </script>
</head>
<body>

<form action="{{ url_for('blue.student_register') }}" method="post">

    <span>用户名:</span><input type="text" id="username" name="username" placeholder="请输入用户名">

    <br>

    <span>手机号:</span><input type="text" id="phone" name="phone" placeholder="请输入手机号">
    <br>

    <span>验证码:</span> <input type="text" name="code" placeholder="请输入验证码">
    <span id="get_code">获取验证码</span>

    <br>
    <span>密码:</span><input type="password" name="password" placeholder="请输入密码">

    <br>

    <button>注册</button>

</form>


</body>
</html>
发布了61 篇原创文章 · 获赞 8 · 访问量 2800

猜你喜欢

转载自blog.csdn.net/aa18855953229/article/details/105188957