django10/csrf/auth
CSRF跨站请求伪造
CSRF操作
CSRF相关装饰器
auth认证模块
auth_user表切换
基于django中间设计项目功能
1.跨站请求伪造(CSRF)
什么是CSRF?、
Cross site Request Forgery 跨站请求伪造,是一种网络的攻击方式,它在2007年曾被列为互联网20大安全隐患之一,也被称为"One Click Attack " 或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用,尽管听起来像跨站脚本(xss),但是它与XSS攻击相左,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求利用受信任的网站,与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比xss更具有危险性
CRSF模拟
1.简介
钓鱼网站:假设是一个跟银行一模一样的网址页面,用户在该页面上转账,用户的账户钱少了,但是转账的地址出了问题。
2.模拟
一台计算机两个服务端不同端口启动,钓鱼网站提交地址改为正规网站的地址
3.预防
csrf策略: 通过返回的页面上添加独一无二的标识信息,从而区分正规网站和钓鱼网站
CSRF简单代码操作演示
演示要求
1.创建两个django项目,一个模仿正常网站A,一个是钓鱼网站B
2.两个网站前端展示一样
3.测试
正常网站A代码
视图层 views.py
from django.shortcuts import render
# Create your views here.
def transfer(request):
if request.method == 'POST':
username = request.POST.get('username')
target_user = request.POST.get('target_user')
money = request.POST.get('money')
print(f'{username}给{target_user}转了{money}')
return render(request,'transfer.html')
路由层 url.py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('transfer/',views.transfer)
]
模板层
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h1>这是钓鱼网站</h1>
<form action="http://127.0.0.1:8000/transfer/" method="post">
<p>username:
<input type="text" name="username">
</p>
<p>target_user:
<input type="text">
<input type="text" name="target_user" value="周瑜" style="display: none">
</p>
<p>money:
<input type="text" name="money">
<input type="submit">
</p>
</form>
</body>
</html>
钓鱼网站B
视图层 views.py
from django.shortcuts import render
# Create your views here.
def transfer(request):
return render(request, 'transfer.html')
路由层、
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('transfer/',views.transfer)
]
模板层
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h1>这是钓鱼网站</h1>
<form action="http://127.0.0.1:8000/transfer/" method="post">
<p>username:
<input type="text" name="username">
</p>
<p>target_user:
<input type="text">
<input type="text" name="target_user" value="周瑜" style="display: none">
</p>
<p>money:
<input type="text" name="money">
<input type="submit">
</p>
</form>
</body>
</html>
效果展示:#钓鱼网站中,输入的目标账户其实是不存在的,已经指定写好了,周瑜的属性值已经被隐藏了。所以转账的时候默认转账给周瑜/
2.jango项目post请求提交403 [CSRF机制开启]
如何处理抵御区别跨伪造站点攻击
开启csrf机制;
非真正的客户端的post请求一律转为403.
真正的客户端如何通过后端的校验呢,参考以下
真正的客户端如何通过CSRF机制请求呢?
1.form表单如何通过csrf机制?
2.ajax表单如何通过csrf机制?
方式1.form表单
<form action="" method="post">
{% csrf_token %}
</form>
----------------------------------------------------------------------------------------------------
方式2.ajax
方式1:先编写csrf模板语法 然后利用标签查找和值获取 手动添加
'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val()
方式2:直接利用模板语法即可
'csrfmiddlewaretoken':'{{ csrf_token }}'
------------------------------------------------------------------------------------------------
方式3:通用方式(js脚本)
扩展性最高
详解方式一
{% csrf_token %} 在form表单中,添加此代码
如果前后端分离不推荐使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h1>这是真正的网站</h1>
<form action="" method="post">
{% csrf_token %}
<p>username:
<input type="text" name = "username">
</p>
<p>target_user:
<input type="text" name = "target_user">
</p>
<p>money:
<input type="text" name="money">
<input type="submit">
</p>
</form>
</body>
</html>
详解方式二
ajax语法 也会报403错误
{% csrf_token %} 不能少哦
方式1:先编写csrf模板语法 然后利用标签查找和值获取 手动添加
'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val()
方式2:直接利用模板语法即可
'csrfmiddlewaretoken':'{{ csrf_token }}'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h1>这是真正的网站</h1>
{% csrf_token %}
<button id="d1">奥巴马点击ajax请求查看</button>
<script>
$('#d1').click(function () {
$.ajax({
url: '',
type: 'post',
data: {'username': 'jason', 'csrfmiddlewaretoken': $('[name="csrfmiddlewaretoken"]').val()},
success: function (args) {
}
})
})
</script>
</form>
</body>
</html>
-------------------------------------- 【 'csrfmiddlewaretoken':'{{ csrf_token }}'】----------------------------------------------------
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h1>这是真正的网站</h1>
{% csrf_token %}
<button id="d1">奥巴马再次点击ajax请求查看</button>
<script>
$('#d1').click(function () {
$.ajax({
url: '',
type: 'post',
data: {'username': 'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},
success: function (args) {
}
})
})
</script>
</form>
</body>
</html>
详解方式三
通用性扩展性强
1.创建一个static文件夹
1.自己创建一个js脚本,放置在static中方便管理,每次使用调用即可。
csrf.js
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
前端html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h1>这是真正的网站</h1>
<button id="d1">ajax查看</button>
<script src="/app01/static/csrf.js"></script>
<script>
$('#d1').click(function () {
$.ajax({
url: '',
type: 'post',
data:{'username':'jason'},
success: function (args) {
}
})
})
</script>
</form>
</body>
</html>
3.CSRF相关装饰器
django中,settings中的这项csrf机制('django.middleware.csrf.CsrfViewMiddleware',)配置是全局的,要么全局校验,要么不校验,我们想局部校验如何呢?想那块校验就那块校验能否办到?
FBV
from django.views.decorators.csrf import csrf_protect,csrf_exempt
1.当整个网站默认都不校验csrf 但是局部视图函数需要校验 如何处理
-----------------------------------------------------------------------------
@csrf_protect
def home(request):
return HttpResponse('哈哈哈')
2.当整个网站默认都校验csrf 但是局部视图函数不需要校验 如何处理
--------------------------------------------------------------------------
@csrf_exempt
def home(request):
return HttpResponse('哈哈哈')
CBV
针对csrf_exempt只有方式3有效 针对其他装饰器上述三种方式都有效
针对CBV不能直接在方法上添加装饰器 需要借助于专门添加装饰器的方法
#@method_decorator(csrf_protect, name='post') # 方式2:指名道姓的添加
class MyHome(views.View):
#@method_decorator(csrf_protect) # 方式3:影响类中所有的方法
def dispatch(self, request, *args, **kwargs):
super(MyHome, self).dispatch(request, *args, **kwargs)
def get(self, request):
return HttpResponse('Home Get view')
# @method_decorator(csrf_protect) # 方式1:指名道姓的添加
def post(self, request):
return HttpResponse('Home Post view')
auth模块
auth模块是什么?
我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统。此时我们需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能,这还真是个麻烦的事情呢。
Django作为一个完美主义者的终极框架,当然也会想到用户的这些痛点。它内置了强大的用户认证系统--auth,它默认使用 auth_user 表来存储用户数据。
django执行数据库迁移命令之后会产生一个auth_user表
该表可以配合auth模块做用户相关的功能:注册 登录 修改密码 注销 ...
该表还是django admin后台管理默认的表
django admin后台管理员账号创建
python manage.py createsuperuser
2.auth模块常用方法
auth认证模块
我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统。此时我们需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能,这还真是个麻烦的事情呢。
Django作为一个完美主义者的终极框架,当然也会想到用户的这些痛点。它内置了强大的用户认证系统--auth,它默认使用 auth_user 表来存储用户数据。
django自带的admin后台管理用户登录参考的就是auth_user表。
创建admin后台管理员用户
tools---run manage.py task---执行createsuperuser
自动对用户密码进行加密处理并保存
auth模块方法大全
1.验证用户名和密码是否正确
auth.authenticate() # 校验正确返回的是用户对象 错误返回的是None
2.保存用户登录状态
auth.login() # 自动帮你操作session相关
3.获取当前用户对象
request.user # 获取当前登录的用户对象 或者是 匿名用户
4.判断当前用户是否登录
request.user.is_authenticated() # 判断用户是否登录 返回布尔值
5.校验登录装饰器
from django.contrib.auth.decorators import login_required
@login_required(login_url='/lg/') # 局部配置
@login_required # 全局配置
LOGIN_URL = '/lg/' # 需要在配置文件中添加配置
6.修改密码
request.user.check_password() # 自动加密再比对
request.user.set_password() # 临时修改密码
request.user.save() # 将修改操作同步到数据库中
7.注销登录
auth.logout(request)
8.注册用户
from django.contrib.auth.models import User
User.objects.create_superuser(username='admin',password='123',email='123@qq.com') # 管理员
User.objects.create_user(username='oscar',password='123') # 普通用户
auth扩展表字段
方式1:编写一对一表关系(了解)
方式2:类继承(推荐)
# settings
'''告诉auth模块 不再使用auth_user 而是使用自定义的表'''
AUTH_USER_MODEL = 'app01.Users'
# models
from django.contrib.auth.models import AbstractUser
class Users(AbstractUser):
# 编写AbstractUser类中没有的字段 不能冲突!!!
phone = models.BigIntegerField()
addr = models.CharField(max_length=32)
注意:
- 类继承之后,需要重新执行数据库迁移命令,并且库里面是第一次操作才可以
- auth模块所有的方法都可以直接在自定义模型类上面使用,自动切换参照表
代码:
def login(request):
print(request.user)
"""
当用户登录成功之后(执行了auth.login) 该方法返回当前登录用户对象
当用户没有登录成功(没有执行auth.login) 该方法返回匿名用户对象
"""
print(request.user.is_authenticated) # 判读当前用户是否登录
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# 查询数据库校验数据(密码自动加密再比对)
is_user_obj = auth.authenticate(request,username=username,password=password)
'''
数据正确的情况下返回的是数据对象
数据错误的情况下返回的是None
'''
if is_user_obj:
auth.login(request,is_user_obj) # 自动操作cookie和session
return render(request, 'login.html')
# @login_required(login_url='/login/') # 校验用户是否登录 默认跳转的登录地址比较复杂 我们可以自定义跳转的登录地址
@login_required
def index(request):
return HttpResponse('index view')
# @login_required(login_url='/login/') # 局部配置 每次都需要自己写 量大的情况下不方便
@login_required
def func(request):
return HttpResponse('func view')
@login_required
def set_password(request):
if request.method == 'POST':
old_password = request.POST.get('old_password')
new_password = request.POST.get('new_password')
# 1.校验原密码是否正确
is_right = request.user.check_password(old_password) # 自动加密并校验
if is_right:
# 2.修改密码
request.user.set_password(new_password)
# 3.保存数据
request.user.save()
return render(request,'set_password.html')
@login_required
def logout(request):
auth.logout(request) # 自动清除cookie和session
return HttpResponse('注销')
from django.contrib.auth.models import User
def register(request):
# User.objects.create(username='jason',password=123) # 不能使用(密码不加密)
# User.objects.create_user(username='jason',password=123) # 创建普通用户
User.objects.create_superuser(username='tony',password=321,email='666@qq.com')
return HttpResponse('注册功能')