数据库优化

  1. 数据库其实存在连接池
  2. 连接池维护着一定数量的数据库连接

current_app

current_app只有在项目启动之后才能使用 , 项目没有启动时不能使用

CSRF攻击示意图

CSRF攻击过程

防止CSRF攻击

  1. 在客户端向后端请求界面数据的时候,后端会往响应中的 cookie 中设置 csrf_token 的值
  2. 在 Form 表单中添加一个隐藏的的字段,值也是 csrf_token
  3. 在用户点击提交的时候,会带上这两个值向后台发起请求
  4. 后端接受到请求,以会以下几件事件:
    • 从 cookie中取出 csrf_token
    • 从 表单数据中取出来隐藏的 csrf_token 的值
    • 进行对比
  5. 如果比较之后两值一样,那么代表是正常的请求,如果没取到或者比较不一样,代表不是正常的请求,不执行下一步操作

使用内置的密码加密解密

1
2
3
4
5
6
7
8
9
10
11
from werkzeug.security import generate_password_hash, check_password_hash

# 设置密码
psw = request.form.get('password')
hash_psw = generate_password_hash(psw)

# 检查密码
student = Student.query.filter_by(id=123)
psw = request.form.get('password')
if checkout_password_hash(student.password,psw):
return 'ok'

上面两个方法应该封装到StudentModel上

1
2
3
4
5
6
7
8
9
10
11
12
13
class UserOauthModel(db.Model):
_password = db.Column(db.String(54), nullable=False)

@property
def password(self):
return self._password

@password.setter
def password(self,psw):
self._password = generate_password_hash(psw)

def check_password(self):
return checkout_password_hash(self._password,psw)

代码复用的方法

  • 装饰器
  • 钩子函数
  • 封装函数
  • 创建父类

flash()方法的本质

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@index_router.route('/login', methods=["GET", "POST", ])
def login():
form = UserOauthForm()
if form.validate_on_submit():
form = UserOauthForm(formdata=request.form)
user = Cache.get_user(form.data.get('emp_id'))
if user:
# 登录用户
login_user(user)
flash(f"欢迎登陆: 工号{user.emp_id}", category="success")
return redirect(request.args.get('next') or url_for("user.emp_detail"))
else:
Cache.update_user(user)
return render_template('user/login.html', form=form)
1
2
3
4
5
6
7
8
9
10
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-success" style="text-align: center;">
<button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>
<strong>{{ message }}</strong>
</div>
{% endfor %}
{% endif %}
{% endwith %}
  • flash本质就是session
  • 后台的flash函数将信息写入session中 , 前台再从session取出,输出到相应的位置 , 然后再次传入后台的时候就将这个session去除
  • 这就能解释flash()的功能 : 将信息输出到下一个页面,而且只会输出一次

UserModel关于密码的设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class User(db.Model):
id = db.Column(db.Integer , primerry_key=True)
username = db.Column(db.String(32),unique=True)
_password = db.Column(db.String(256))
permission = db.Column(db.Integer , default=PERMISSION_NONE)

@property
def password(self):
raise Exception("cant access")

@password.setter
def password(self,psw):
self._password = genrate_password_hash(psw)

def check_password(self,psw):
return check_password_hash(self._password,psw)

def check_permission(self,permission):
"""
检测是否有某个权限
"""
# 如果是黑名单用户,则没有权限
if BLACK_USER & self.permission == BLACK_USER:
return False
return permission & self.permission == permission
1
2
3
4
PERMISSION_NONE = 0
BLACK_USER = 1
COMMON_USER = 2
VIP_USER = 4

权限的两种设计

单个字段实现所有功能

类似于Linux权限设计 , 一个字段可以代表多种权限 : rwx

方案一 : Linux权限设计模式

  • 完全和Linux中一样 , 使用二进制
  • 每一种权限使用一个不重复的二进制确定
  • 所有的初始权限值都是2的n次幂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
BLACK_USER = 1
COMMON_USER = 2
VIP_USER = 4


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


me = User(permission=4)


def check_permissin(user,permission):
"""
检测user是否拥有某个权限
"""
return user.permission & permission == permission


res = check_permissin(me,VIP_USER)
print(res)

方案二 : 包含模式

  • 只是给一个数值
  • 数值越大权限越高 , 包含数值小的所有权限

多表设计权限

  • 用户表
  • 权限表
  • 用户权限表(用户表和权限表多对多的关系)
1
2
3
4
5
6
7
8
9
class User(db.Model):
id = db.Column(db.Integer, primerry_key=True)
username = db.Column(db.String(32), unique=True)
_password = db.Column(db.String(256))


class Permission(db.Model):
p_name = db.Column(db.Integer,db.ForerginKey(User.id))
c_persmission_id = db.Column(db.Integer,db.ForerginKey(Permission.id))

使用事务

1
2
3
4
5
6
7
8
9
# flask
try:
# 这里写sql语句组 一旦发生异常执行rollback() 相当于什么都没执行
connect.commit()
return 'OK'
except Exception as error:
print(error)
connect.rollback() # 发生异常时执行回滚
return 'Err'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# django
def hello_transaction(request):
try:
with transaction.atomic():
pass
except Exception as e:
transaction.rollback()
else:
transaction.commit()

return HttpResponse('hello')

# or
def welcome_transaction(request):
save_point = transaction.savepoint()
try:
pass
except Exception as e:
transaction.savepoint_rollback(save_point)
else:
transaction.savepoint_commit(save_point)

return HttpResponse('welcome')