Python的Web框架之Flask(5)登录,csrf,数据库

登录小例子

后端,创建webA.py

import base64

from flask import Flask, request, render_template, redirect, url_for, flash, make_response
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField,SubmitField
from wtforms.validators import DataRequired,EqualTo
import os
app = Flask(__name__)
app.config['WTF_CSRF_ENABLED'] = False


app.secret_key = 'dasdasdsadas'

@app.route('/', methods=["get", "post"])
def index():
    if request.method =='POST':
        # 取出表单数据
        username = request.form.get("username")
        password = request.form.get("password", "")
        if not all([username, password]):
            print('参数错误')
        else:
            print(username, password)
            if username == 'admin' and password == '1234':
                # 登录成功跳转到转账页面
                response = redirect(url_for('transfer'))
                # 因为转账要求用户登录,所以我们进行状态保持,设置用户名到cookie中
                response.set_cookie('username',username)
                return response
            else:
                print('密码错误')

    return render_template("login2.html")

@app.route('/transfer', methods=["get", "post"])
def transfer():
    # 取出cookie,确保是登录的
    username =  request.cookies.get('username',None)
    if not username:
        # 没有代表没登录,跳转到登录页面
        return redirect(url_for('index'))
    if request.method == 'POST':
        to_account = request.form.get("to_account")
        money = request.form.get("money")
        # 取出表单的 crsf_token
        form_crsf_token = request.form.get("crsf_token")
        # 取出 cookie的crsf_token
        cookie_crsf_token = request.cookies.get('crsf_token')
        if form_crsf_token != cookie_crsf_token:
            return "token校验失败,可能是非法操作"


        print('假装执行转账')
        return '转账{}元到{}账户成功!'.format(to_account, money)
    #  使用 make_response, 相当于 django的httpresponse
    # 生成 crsf_token
    crsf_token = generate_crsf()
    response = make_response(render_template('transfer.html', crsf_token= crsf_token))
    # 用于提交验证
    response.set_cookie('crsf_token',crsf_token)
    return response

def generate_crsf():
    return bytes.decode(base64.b64encode(os.urandom(48)))



if __name__ == '__main__':
    app.run(debug=True)

前段,login2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>用户登录页面</h1>
<form action="" method="post">
    <label> 用户名</label> <input type="text" name="username" placeholder="请输入用户名"> <br>
    <label> 密码</label><input type="password" name="password" placeholder="请输入密码"> <br>
    <input type="submit" value="登录">
</form>


{% for message in get_flashed_messages() %}
    {{ message }}
{% endfor %}
</body>
</html>

transfer.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>我是网站A,转账页面</h1>
<form action="" method="post">
    <input type="hidden" name="crsf_token" value="{{crsf_token}}"> >

    <label> 账户</label> <input type="text" name="to_account" placeholder="请输入要转账的账户"> <br>
    <label> 金额</label><input type="number" name="money" placeholder="请输入转账金额"> <br>
    <input type="submit" value="转账">
</form>
</body>
</html>

csrf跨站请求伪造

跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的

例子

假如一家银行用以运行转账操作的URL地址如下:http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName

那么,一个恶意攻击者可以在另一个网站上放置如下代码: <img src="http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman">

如果有账户名为Alice的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失1000资金。

这种恶意的网址可以有很多种形式,藏身于网页中的许多地方。此外,攻击者也不需要控制放置恶意网址的网站。例如他可以将这种地址藏在论坛,博客等任何用户生成内容的网站中。这意味着如果服务端没有合适的防御措施的话,用户即使访问熟悉的可信网站也有受攻击的危险

透过例子能够看出,攻击者并不能通过CSRF攻击来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。他们能做到的,是欺骗用户浏览器,让其以用户的名义运行操作

添加校验token






这两个crsf_token数值相同时就会校验成功,正常运行,因为是随机生成的,当被攻击时,就会校验错误



简单写法,
#后端部分
from flask_wtf import CSRFProtect
CSRFProtect(app)

前段添加

{{ form.csrf_token() }}

数据库

安装扩展

pip install flask_sqlalchemy
pip install mysqlclient
pip install pymysql
数据库操作案例

数据库基本操作
在Flask-SQLAlchemy中,插入、修改、删除操作,均由数据库会话管理。会话用db.session表示。在准备把数据写入数据库前,要先将数据添加到会话中然后调用commit()方法提交会话。

数据库会话是为了保证数据的一致性,避免因部分更新导致数据不一致。提交操作把会话对象全部写入数据库,如果写入过程发生错误,整个会话都会失效。

数据库会话也可以回滚,通过db.session.rollback()方法,实现会话提交数据前的状态。

在Flask-SQLAlchemy中,查询操作是通过query对象操作数据。最基本的查询是返回表中所有数据,可以通过过滤器进行更精确的数据库查询。
将数据添加到会话中示例:
创建一个简单的表:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
# 设置数据库连接
app.config['SQLALCHEMY_DATABASE_URI'] ='mysql://root:root@127.0.0.1:3306/flasktest'
# 动态追踪设置
app.config['SQLALCHEMY_TRACK_MODUFICATIONS'] = True
# 显示原始sql
app.config['SQLALCHEMY_ECHO'] = True


# 数据库要和 app关联
db = SQLAlchemy(app)

class Role(db.Model):
    # 定义表名
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)




@app.route('/')
def index():
    return 'index'
if __name__ == '__main__':
    # 删除表
    db.drop_all()
    # 创建表
    db.create_all()
    ro1 = Role(name='admin')
    ro2 = Role(name='user')
    db.session.add_all([ro1, ro2])
    db.session.commit()
    app.run()
# 设置数据库连接
app.config['SQLALCHEMY_DATABASE_URI'] ='mysql://root:root@127.0.0.1:3306/flasktest'
# 动态追踪设置
app.config['SQLALCHEMY_TRACK_MODUFICATIONS'] = True
# 显示原始sql
app.config['SQLALCHEMY_ECHO'] = True
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。