第六课 Flask用户注册数据加密手机邮箱激活和内置对象
tags:
- Flask
- 2019千锋教育
categories:
- flask
- 钩子函数
- 内置对象q和Config
- 用户注册数据加密
- 邮箱发送
- 手机激活
文章目录
第一节 钩子函数
1.1 钩子函数介绍
- 钩子函数面向切面编程,动态介入请求流程。
- 请求钩子(相当于中间件)
- before first request
- before request
- after reguest
- teardown request 请求关闭
- 可以使用蓝图的,也可以直接使用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请求流程
- 普通回答(连60都不能给你呀兄弟)
- urls -> views
- views -> models
- models -> views
- views -> response
- 添加中间件回答
- 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 把它写成中间件
- 创建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
- 改__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))
- cookie和session已经在前面讲过了。
- g 可以用来跨函数传递数据的。
- 无论哪种服务器,为每个用户的请求创建一个线程(内部维护一个线程池)。变量数据是隔离开的。
@blue.before_request
def before():
g.msg = "hhd"
print(request.url)
@blue.after_request
def after(res):
print(g.msg)
return g.msg
- 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 注册登录基础设计
- 前端的加密是为了数据传输安全,后台加密为了防家贼(后台开发人员)。
- 注册登录基础设计如下。
# 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 注册登录加密
- 上述登录功能可以实现基本功能。但是,这里显式的对代码加了密, 并不友好。
- hash_pwd = generate_password_hash(password)
- 使用property干预数据的设置过程。property 将函数编程一个属性
- 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)
- 应用到我们登陆时候的数据安全上。
# 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"))
- 修改好数据库进行数据迁移。(这里可能会出现问题,数据库可能不是你想的那样)
- python manage.py db migrate
- python manage.py db upgrade
- 到数据库观察一下,发现之前的s_password没了,多了_s_password字段。且之前的密码也都没了。所以迁移细节并不细致。
- 删除数据库中的脏数据
- 重新运行服务器添加
第三节 邮箱激活功能
- pip install flask-mail -i https://pypi.douban.com/simple
- 借鉴:http://www.bjhee.com/flask-ext2.html
- 导入ext.py注册
- settings.py中,配置邮箱基本信息。
MAIL_SERVER = "smtp.163.com"
MAIL_PORT = 25
MAIL_USERNAME = "XXX@163.com"
MAIL_PASSWORD = "XXXX"
MAIL_DEFAULT_SENDER = MAIL_USERNAME
- 邮箱基本发送功能
from flask_mail import Message
from App.ext import mail
@blue.route("/sendmail/")
def send_mail():
msg = Message("FLask Email", recipients=["18855953229@163.com", ])
msg.body = "哈哈 FLask不过如此"
msg.html = "<h2>我靠 我真是天才呀</h2>"
mail.send(message=msg)
return "邮件发送成功!!"
- 邮箱激活的流程
- 异步发送邮件
- 在邮件中包含激活地址
- 激活地址接收一个一次性的token
- token是用户注册的时候生成的,存在了cache中
- token是key-value格式的存在
- key就是我们的 token
- value用户的 一个唯一标识(id, 用户名,手机,邮箱等)
第四节 手机激活功能
4.1 测试API功能
- 手机激活流程 同步的
- models加入字段 s_phone = db.Column(db.String(32), unique=True)
- 然后去找发送的API。如:阿里云,这里推荐网易云信https://app.yunxin.163.com/index
- 推荐一个前端UI免费下载地址。http://www.jq22.com/jquery-info14563
- 创建应用,开通短息功能。(可以用来测试)-> 开发手册
- 写一个发送验证码的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
- 把上面代码复制到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
- 添加缓存用来存储验证码。
from flask_caching import Cache
cache = Cache(
config={
"CACHE_TYPE":"redis"
}
)
cache.init_app(app=app)
- 完成验证码接口和注册验证接口。
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 "注册成功"
- 前端注册页面
<!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>