Django回顾【六 】
目录
一、Cookie和Session
【1】Cookie
【2】Session
二、中间件
【1】 什么是中间件
【2】作用
【3】自定义中间件
三、CSRF认证相关
【1】CSRF是什么
【2】CSRF攻击原理
【3】CSRF攻击防范
四、auth的使用
【2】auth模块常用方法
authenticate()
login(HttpRequest, user)
logout(request)
is_authenticated()
login_requierd
create_user
create_superuser
check_password
set_password
User对象的属性
补充
【3】扩写auth的user表
方案一:通过一对一扩展
方案二:通过继承 AbstractUser表来扩写(推荐)
五、缓存
缓存的具体使用(三种粒度)
全站缓存
视图缓存
局部缓存
一、Cookie和Session
【1】Cookie
Cookie是客户端浏览器上的键值对 ----> 为了做会话保持
怎么来的?
- 服务端写入的
- 服务端在返回的响应头中写入
- 浏览器会自动取出来
- 存起来
key value 形式
- 过期时间
- path
- http only
- 。。。
只要浏览器中有cookie,再次向当前域发送请求,都会自动携带
- 携带在请求头中的cookie字段中
- cookie:"name=lqz;age=19"
不安全问题:cookie中发了敏感数据 ---> 客户能看到
cookie设置:obj.set_cookie()
cookie取值:request.COOKIES.get()
清空: request.COOKIES.clear()
【2】Session
我们需要让cookie变的安全 ---> 敏感数据不在cookie中方法,而放在session中
- session是服务端的键值对
- session跟cookie有什么关系呢?
- {111:{name:lqz,age:19,password:123},222:{name:zs,age:19,password:666}}
- 把key,以cookie的形式,存到浏览器中
- sessionid:111
- 当前浏览器以后再发请。就会携带 过来
- 我们根据带过来的cookie:111 ----> 从 session中取出对应的数据
- 把key,以cookie的形式,存到浏览器中
- {111:{name:lqz,age:19,password:123},222:{name:zs,age:19,password:666}}
session的使用 ---> 必须要先迁移表 ---> django-session表不存在
- session存在服务端的 ---> 默认情况下存在 ---> django-session表中
- 配置文件
- django项目有两套配置文件:内置一套,项目自己一套
- SESSION_ENGINE = 'django.contrib.sessions.backends.db'
# django-session表的字段
session_key: sessionid:随机字符串
session_data: 真正的数据--->加密了
expire_date: 过期时间
session的使用
取值:request.session.get()
赋值:request.session['name']='l
session的本质执行原理
'''
1 咱么写了request.session['name']='lqz',本质就是向session 对象中放入了name=lqz
2 当前视图函数结束----> 经过 【中间件】 ---> 返回给了前端
- django内置了一个session中间件
- 判断:request.session有没有变化,如果有变化
- 情况一:django-session表中没有数据
在表中创建出一条数据,随机生成一个字符串[随机字符串session_key],
把数据存入django-session表
session_key: adsfasd
session_data: name=lqz 加密后存到里面
把随机字符串写入到cookie中: sessionid:adsfasd
- 情况二:django-session表中有数据
把session中所有的值--》加密后--》更新到django-session表的session_data中,其他不变
3 下次再发请求进入任意视图函数---> 又会经过 【中间件】---> 视图函数
- 视图函数中取session:request.session.get('age')
- 浏览器发请求---> 携带cookie过来---> 到了中间件---> 根据sessionid取出随机字符串
- 拿着随机字符串去django-session中查【session_key】---> 能查到就把 session_data的数据解密
---> 放到request.session中
- 后续视图函数中,才能取出值
'''
session的中间件把上述内容完成了:procee_request process_response
django.contrib.sessions.middleware.SessionMiddleware
二、中间件
【1】 什么是中间件
中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。
【2】作用
- 全局的请求拦截 ---> 如果它没有登录 ---> 就不允许访问
- 拦截所有请求,获取请求的ip地址
- 记录所有用户的访问日志
- 统一在响应头中加数据
【3】自定义中间件
中间件中主要有几个方法:就是一个类,类中有几个方法
process_request(self,request)
# 请求来了就会走
process_response(self, request, response)
# 请求走了就会走
process_view(self, request, callback, callback_args, callback_kwargs)
# 视图函数执行之前调用
process_template_response(self,request,response)
# 渲染模板之前会走
process_exception(self, request, exception)
# 视图函数中出现异常了才执行
django内置一些中间件 ---> 增强了djagno的功能
- request.session
- request.user
post请求提交数据,拦截了---> csrf认证
process_request和process_response
当用户发起请求的时候会依次经过所有的的中间件,这个时候的请求时process_request,最后到达views的函数中,views函数处理后,在依次穿过中间件,这个时候是process_response,最后返回给请求者。
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
# session相关的中间件
'django.contrib.sessions.middleware.SessionMiddleware',
# 公共中间件---> 访问不带 / 路径,如果有带 / 的路径,他会让你重定向到这个地址
'django.middleware.common.CommonMiddleware',
# csrf认证 xss cors
'django.middleware.csrf.CsrfViewMiddleware',
# 认证:request.user---> 这个中间件做的
'django.contrib.auth.middleware.AuthenticationMiddleware',
# django的消息框架---> flask--> 闪现
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
自定义中间件来使用,记录用户的请求地址和user-agent
class SaveRemoteAddr(MiddlewareMixin):
def process_request(self, request):
# request是WSGIRequest 的对象
# print(request.session) # 一定要保证,session的中间件要在上面
# 这个request就是当次请求的request
# 取出ip
ip = request.META.get('REMOTE_ADDR')
user_agent = request.META.get('HTTP_USER_AGENT')
print(ip)
print(user_agent)
# return HttpResponse('不让你看了') # 不会再走视图函数了
'''
能返回的情况:
1 None,表示执行完这个代码,继续往后执行---> 还有中间件,继续执行--> 最后进视图函数
2 四件套,后续不走了,中间件的process_response---> 直接返回给浏览器了
'''
中间件,在响应头加入访问时间
import datetime
class AddHeaderMiddleWare(MiddlewareMixin):
def process_response(self, request, response):
# request中有没有session? 有
# request 如果在视图函数中,往request中放了值,在这里,就可以取出来request.xxx
# print(request.xxx)
# 所有cookie中都带
# response.set_cookie('xxxxx', 'asdfds')
# 写入到响应头,访问服务端的时间
response['ttt'] = datetime.datetime.now()
return response # 一定要返回response对象
三、CSRF认证相关
【1】CSRF是什么
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。
【2】CSRF攻击原理
在同一个浏览器中,如果登录了A网站,没有退出,在B网站中,向A网站发送请求,浏览器会自动携带A网站的cookie,对于A网站后端来讲, 它就分辨不清到底是用户真实发的请求,还是黑客网站发的请求【都会携带用户真实的cookie】。
【3】CSRF攻击防范
Django解决了这个问题 ---> 只要发送post请求,必须携带一个csrf_token 随机字符串(后端给的)。
这个随机字符串可以带的位置
1 请求体中(urlencoded,form-data):{csrfmiddlewaretoken:asdfasdf}
2 放在请求头中:'X-CSRFToken':asdfasdfasd
3 ajax提交数据:默认是urlencoded,放在请求体中没有任何问题
$.ajax({
method: 'post',
data: {username, password, csrfmiddlewaretoken},
success: function (res) {
console.log(res)
}
})
4 ajax提交,使用json格式 ---> 就不能放在请求体中,只能放在请求头中:
$.ajax({
method: 'post',
headers:{'X-CSRFToken':csrfmiddlewaretoken},
contentType: 'application/json',
data: JSON.stringify({username, password}),
success: function (res) {
console.log(res)
}
})
注:
- post 提交的数据,都是从request.POST中取,前提是:必须是urlencoded和form-data格式
- 如果是json是取不到的
- 如果使用ajax发送请求
- redirect render就用不了了
- 尽量使用JsonResponse
四、auth的使用
【1】author是什么
Auth模块是Django自带的用户认证模块:
我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统。此时我们需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能,这还真是个麻烦的事情呢。
Django作为一个完美主义者的终极框架,当然也会想到用户的这些痛点。它内置了强大的用户认证系统–auth,它默认使用 auth_user 表来存储用户数据。
默认的用户表示auth_user
创建一个用户:可以用代码,可以用命令
# 配置文件中配置:---> 表会被迁移
INSTALLED_APPS = [
'django.contrib.auth',
]
auth有哪些表 ---> 权限控制
-Permission:auth_permission
-Group:auth_group
-User:auth_user --> 密码加密--> 密文
-auth_group_permissions
-auth_user_groups
-auth_user_user_permissions
如果用户没登录
request.user取出的是匿名用户:AnonymousUser类的对象---> 也有pk,name,is_authenticated
【2】auth模块常用方法
from django.contrib import auth
authenticate()
提供了用户认证功能,即验证用户名以及密码是否正确,一般需要username 、password两个关键字参数。
如果认证成功(用户名和密码正确有效),便会返回一个 User 对象。
authenticate()会在该 User 对象上设置一个属性来标识后端已经认证了该用户,且该信息在后续的登录过程中是需要的。
用法:
user = authenticate(username='usernamer',password='password')
校验用户:必须传username和password
user = authenticate(username='usernamer',password='password')
from django.contrib.auth.models import User
user = User.objects.filter(username=username).first()
if user and user.check_password(password):
print('用户名密码正确')
else:
print('用户名密码错误')
login(HttpRequest, user)
用户校验通过,让它登录,执行它
- 当前登录用户写入到session中,
- 后续request.user就能取出当前登录用户。
用法:
from django.contrib.auth import authenticate, login
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
# Redirect to a success page.
...
else:
# Return an 'invalid login' error message.
...
logout(request)
退出 ---> 当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错。
用法:
from django.contrib.auth import logout
def logout_view(request):
logout(request)
# Redirect to a success page.
is_authenticated()
判断当前用户是否登录
- 不能使用request.user 是否有值来判断,因为他一直有值
- request.user.is_authenticated() ----> 返回True或False
用法:
def my_view(request):
if not request.user.is_authenticated():
return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
login_requierd
登录认证装饰器 ---> 放在视图函数上
用法:
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
...
如果需要自定义登录的URL,则需要在settings.py文件中通过LOGIN_URL进行修改。
LOGIN_URL = '/login/' # 这里配置成你项目登录页面的路由
create_user
auth 提供的一个创建普通用户的方法,需要提供必要参数(username、password)等。
from django.contrib.auth.models import User
user = User.objects.create_user()--密码是加密的---> 这样存密码是明文的
create_superuser
auth 提供的一个创建新的超级用户的方法,需要提供必要参数(username、password)等。
python manage.py createsuperuser
check_password
通过明文密码校验密码是否正确;密码正确返回True,否则返回False。
用法:
ok = user.check_password('密码')
set_password
修改密码,接收要设置的新密码作为参数。
注意:修改完一定要调用用户对象的save方法!!!
user.set_password(new_password)
user.save()
User对象的属性
User对象属性:username, password
is_staff : 用户是否拥有网站的管理权限,能不能登录admin后台管理。
is_active : 是否允许用户登录, 设置为 False,可以在不删除用户的前提下禁止用户登录。
- is_active是False ----> authenticate也查不出来
- is_superuser:是否是超级管理员,admin中权限最高
补充
auth模块的密码加密
- 同样的密码 ---> 再次加密 ----> 密文也不一样
如何实现
pbkdf2_sha256$ # 加密方式
260000$ # 过期时间
H93ubuUFw6FbYc6B8ojzKA$ # 随机串,即秘钥
H0ZnaiJOm/pI4K802Y2TcO5SQ7iWDcx5E+mb/hdABd8= # 明文加密后的
如果我们写了User表,但是想用人家的密码加密,就可以使用
res = make_password('123456')
check_password(明文,密文)
【3】扩写auth的user表
方案一:通过一对一扩展
from django.contrib.auth.models import User
class UserDetail(models.Model):
user=models.OneToOneField(to=User)
phone=models.CharField(max_length=32)
方案二:通过继承 AbstractUser表来扩写(推荐)
1、在models.py中写用户表
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
# 原来有的字段就不需要写了,只需要扩写你想写的字段
mobile = models.CharField(max_length=32)
icon = models.ImageField(upload_to='/icon/',default='default.png')
2、在settings.py 配置
AUTH_USER_MODEL='app01.UserInfo'
3、之前不要迁移数据,一旦迁移过,就不行了
- 一旦迁移过了,按这个步骤操作
- 删库
- 删迁移文件(所有你写的app都删)
- 删除源码中 auth和admin的迁移文件 ---> 卸载djagno重装
五、缓存
页面静态化
缓存 ---> 本身数据在数据库中
- 如果访问量较大
- 每次都需要去数据库查询 ---> 影响效率
- 我们可以对数据做缓存 ---> 以后先从缓存中取数据
- 如果取到直接返回 ---> 不需要查数据库
- 如果取不到 ---> 再查数据库 ---> 查完放到缓存中
Django默认就支持缓存 ---> 缓存到的位置
- 内存缓存(演示)
- 文件缓存
- 数据库缓存
- redis缓存(后期会用)
默认情况,缓存到内存中
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
}
}
缓存到文件中
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', # 指定缓存使用的引擎
'LOCATION': 'D:\Python27\django_05\cache', # 指定缓存的路径
'TIMEOUT':300, # 缓存超时时间(默认为300秒,None表示永不过期)
'OPTIONS': {
'MAX_ENTRIES': 300, # 最大缓存记录的数量(默认300)
'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
}
}
}
缓存的具体使用(三种粒度)
全站缓存
使用方式,如下:只需要配置两个中间件即可
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
......
'django.middleware.cache.FetchFromCacheMiddleware'
]
视图缓存
from django.views.decorators.cache import cache_page
@cache_page(timeout=10)
def demo09(request):
print('来了老弟')
book_list = Book.objects.all()
return render(request, 'books.html', {'books': book_list})
局部缓存
{% load cache %}
{% cache 10 'name' %}
可以能有很多代码
{% endcache %}