【csrf攻击即】:通过第3方网站,伪造请求(前提条件是你已经登录正常网站,并保存了session或cookie登录信息且没有退出),第三方网站即可通过你的session或cookie直接修改正常网站的用户名密码。

def login_required(view_func): #参数即调用它的函数 '''登录判断装饰器''' def wrapper(request, *view_args, **view_kwargs): # 内部函数,包装一下 # 判断用户是否登录 if request.session.has_key('islogin'): #如果登录了,就返回真正页面(调用此装饰器函数的函数) # 用户已登录,调用对应的视图 return view_func(request, *view_args, **view_kwargs) else: # 否则,用户未登录,则跳转到登录页 return redirect('/login') return wrapper@login_required# /change_pwd@login_required #作用:把此页面作为一个参数传到login_required里def change_pwd(request): '''显示修改密码页面''' return render(request, 'booktest/change_pwd.html')MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',]<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>修改密码页面</title></head><body><form method="post" action="/change_pwd_action/"> 新密码:<input type="password" name="pwd"> <input type="submit" value="确认修改"></form></body></html>【1】登录装饰器:用户已登录,调用对应的视图;用户未登录,跳转到登录页
【2】需要登录才能操作的页面调用登录装饰器
【3】需要登录才能操作的页面调用登录装饰器
from django.shortcuts import render,redirectfrom django.template import loader,RequestContextfrom django.http import HttpResponse,JsonResponsefrom app2.models import BookInfodef login(request): '''登录页''' # 判断用户是否登录,用户已登录, 直接跳转到图书列表 if request.session.has_key('islogin'): return redirect('/change_pwd/') else: #如果用户名密码已经在cookie中,则取到它,并做为参数返回给渲染页面 if 'username' in request.COOKIES: #获取cookie中的用户名、密码 username=request.COOKIES['username'] #password=request.COOKIES['password'] else: username='' password='' return render(request,'app2/login.html',{'username':username,'password':password})def login_check(request): '''登录校验''' #1.获取用户名密码 username=request.POST.get('username') password=request.POST.get('password') remember=request.POST.get('remember') #接收remeber #2.进行校验,并返回json数据 if username=='jim' and password=='123': #return redirect('/books') response = JsonResponse({'res':1}) #正确返回1,重写 #如果remember==on,则把用户名,密码设置cookie到cookie if remember=='on': #response.set_cookie('username',username,max_age=7*24*3600) #response.set_cookie('password',password,max_age=7*24*3600) # 记住用户登录状态把用户名设置到session request.session['username'] = username # 返回应答 # 如果用户勾选了remember的条件下,设置session,记住用户登录状态 request.session['islogin'] = True # 只有session中有islogin,就认为用户已登录 return response #不要忘记返回response else: #return redirect('/login') return JsonResponse({'res':0}) #错误返回0#【1】登录装饰器:用户已登录,调用对应的视图;用户未登录,跳转到登录页def login_required(view_func): '''登录判断装饰器''' def wrapper(request, *view_args, **view_kwargs): # 判断用户是否登录 if request.session.has_key('islogin'): # 用户已登录,调用对应的视图 return view_func(request, *view_args, **view_kwargs) else: # 用户未登录,跳转到登录页 return redirect('/login') return wrapper# /change_pwd@login_required #【2】需要登录才能操作的页面调用登录装饰器def change_pwd(request): '''显示修改密码页面''' # # 进行用户是否登录的判断 # if not request.session.has_key('islogin'): # # 用户未登录,跳转到登录 # return redirect('/login') return render(request, 'app2/change_pwd.html')# /change_pwd_action@login_required #【3】需要登录才能操作的页面调用登录装饰器def change_pwd_action(request): '''模拟修改密码处理''' # # 进行用户是否登录的判断 # if not request.session.has_key('islogin'): # # 用户未登录,跳转到登录 # return redirect('/login') # 1.获取新密码 pwd = request.POST.get('pwd') # 获取用户名 username = request.session.get('username') # 2.实际开发的时候: 修改对应数据库中的内容... # 3.返回一个应答 return HttpResponse('%s修改密码为:%s'%(username,pwd)) path('login/',views.login),#登录页 path('login_check',views.login_check),#登录检测 path('change_pwd/', views.change_pwd), # 修改密码页面显示 path('change_pwd_action/', views.change_pwd_action), # 修改密码处理<!DOCTYPE html><html lang="zh"><head> <meta charset="UTF-8"> <!-- 【0】引入jquery --> <script src="/static/js/jquery-1.12.4.min.js"></script> <title>登录页面</title></head><script> // 写ajax处理函数 $(function () { $('#btnLogin').click(function () { //1.获取用户名、密码、是否记住用户名 username=$('#username').val() password=$('#password').val() remember=$('#remember').val() //【2】是否记住用户名 //2.发起ajax--post(username,password)请求验证,地址:/login_check $.ajax({ 'url':'/login_check',//验证地址 'type':'post',//请求类型 'data':{'username':username,'password':password,'remember':remember},//【3】发送数据,加上remember 'dataType':'json',//希望返回数据类型 }).success(function(data){ //成功返回{'res':1},失败{'res':0} if(data.res===0){ $('#msg').show().html('用户名或密码错误请重试!')//登录失败则显示msg,并在里写入信息 }else{//成功跳转到books页面 location.href='/change_pwd' } }) }) })</script><style> /* 信息提示样式 */#msg{ display: none; color:red;}</style><body> <!-- 原form删除,input的name变id,方便jquery操作 --> <!-- 【4】把views页的login()函数传过来的用户名,密码赋值给对应处 --> 用户名:<input type="text" id="username" value="{{username}}"><br/> 密码:<input type="password" id="password" value="{{password}}"><br/> <!-- 加入一个信息提示框,用于密码等错误提示 --> <div id="msg"></div> <!-- 【1】记住用户名,设置cookie用,如果勾选则其value=on --> <input type="checkbox" id="remember">记住用户名<br/> <!-- 按钮type改button,加一个id方便jquery操作 --> <input type="button" id="btnLogin" value="登录"></body></html>

然后提示:jim修改密码为:456
ipconfig /all找到:192.168.1.4公网直接百度IP,查看即可如果在局域网创建服务:py manage.py runserver 192.168.1.4:8000如果在【虚拟机】或【真正服务器】创建服务:py manage.py runserver 公网IP:8000DEBUG = False #TrueALLOWED_HOSTS = ['*']<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>修改密码页面</title></head><body><form method="post" action="http://192.168.1.4:8000/change_pwd_action/"> <input type="hidden" name="pwd" value='789'><!--【1】用了隐藏input,页面只能看到一个按钮,并给新密码定为789--> <input type="submit" value="点我有惊喜!"></form></body></html>
jim修改密码为:789【1】django默认打开csrf防护,它只对post提交有效
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', #【1】django默认打开csrf防护,它只对post提交有效 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',]{% csrf_token %}标签(对应模板本例:change_pwd.html)。
【注意】:需要有post页面提交的页面必须加上{% csrf_token %},否则即使本站页面也会访问失败。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>修改密码页面</title></head><body><form method="post" action="/change_pwd_action/"> {% csrf_token %}<!--【1】本页面要有post提交,所以加上--> 新密码:<input type="password" name="pwd"> <input type="submit" value="确认修改"></form></body></html>【参考】:https://code.ziqiangxuetang.com/django/django-csrf.html?bd_source_light=4317393
$.ajaxPost({ data: {csrfmiddlewaretoken: '{{ csrf_token }}' },});<!DOCTYPE html><html lang="zh"><head> <meta charset="UTF-8"> <!-- 【0】引入jquery --> <script src="/static/js/jquery-1.12.4.min.js"></script> <title>登录页面</title></head><script> // 写ajax处理函数 $(function () { $('#btnLogin').click(function () { //1.获取用户名、密码、是否记住用户名 username=$('#username').val() password=$('#password').val() remember=$('#remember').val() //【2】是否记住用户名 //2.发起ajax--post(username,password)请求验证,地址:/login_check $.ajax({ 'url':'/login_check',//验证地址 'type':'post',//请求类型 'data':{//【3】发送数据,加上remember 'username':username, 'password':password, 'remember':remember, csrfmiddlewaretoken: '{{ csrf_token }}',//【4】csrf防护 }, 'dataType':'json',//希望返回数据类型 }).success(function(data){ //成功返回{'res':1},失败{'res':0} if(data.res===0){ $('#msg').show().html('用户名或密码错误请重试!')//登录失败则显示msg,并在里写入信息 }else{//成功跳转到books页面 location.href='/change_pwd' } }) }) })</script><style> /* 信息提示样式 */#msg{ display: none; color:red;}</style><body> <!-- 原form删除,input的name变id,方便jquery操作 --> <!-- 【4】把views页的login()函数传过来的用户名,密码赋值给对应处 --> 用户名:<input type="text" id="username" value="{{username}}"><br/> 密码:<input type="password" id="password" value="{{password}}"><br/> <!-- 加入一个信息提示框,用于密码等错误提示 --> <div id="msg"></div> <!-- 【1】记住用户名,设置cookie用,如果勾选则其value=on --> <input type="checkbox" id="remember">记住用户名<br/> <!-- 按钮type改button,加一个id方便jquery操作 --> <input type="button" id="btnLogin" value="登录"></body></html><p style="background-color:rgb(255, 255, 204)">禁止访问 (403)CSRF验证失败. 请求被中断.您看到此消息是由于该站点在提交表单时需要一个CSRF cookie。此项是出于安全考虑,以确保您的浏览器没有被第三方劫持。If you have configured your browser to disable cookies, please re-enable them, at least for this site, or for “same-origin” requests.</p>1) 渲染模板文件时在页面生成一个名字叫做csrfmiddlewaretoken的隐藏域。
在网站右键查看源码,可看到这个隐藏域:name=csrfmiddlewaretoken 的input
<form method="post" action="/change_pwd_action/"> <input type="hidden" name="csrfmiddlewaretoken" value="O6lfnrybooqSP9Je0bZyCOr8qGObvK0jgzV7fMUW259X117OkpAm8OjtCsadu9tk"> 新密码:<input type="password" name="pwd"> <input type="submit" value="确认修改"></form>把上例的隐藏域的value传给服务器。
<input type="hidden" name="csrfmiddlewaretoken" value="O6lfnrybooqSP9Je0bZyCOr8qGObvK0jgzV7fMUW259X117OkpAm8OjtCsadu9tk">2) 服务器同时交给浏览器保存一个名字为csrftoken的cookie信息。
3) 提交表单时,两个值都会发给服务器,服务器进行比对,如果一样,则csrf验证通过,否则失败。
【其它生成验证码参考】:https://blog.csdn.net/ding_312/article/details/82258442
【1】存入session,用于做进一步验证
【2】将图片保存在内存中,文件类型为png
【3】将内存中的图片数据返回给客户端,MIME类型为图片png
from PIL import Image, ImageDraw, ImageFontimport io# /verify_codedef verify_code(request): # 引入随机函数模块 import random # 定义变量,用于画面的背景色、宽、高 RGB bgcolor = (random.randrange(20, 100), random.randrange(20, 100), 255) width = 100 height = 25 # 创建画面对象 im = Image.new('RGB', (width, height), bgcolor) # 创建画笔对象 draw = ImageDraw.Draw(im) # 调用画笔的point()函数绘制噪点 for i in range(0, 100): xy = (random.randrange(0, width), random.randrange(0, height)) fill = (random.randrange(0, 255), 255, random.randrange(0, 255)) draw.point(xy, fill=fill) # 定义验证码的备选值 str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0' #qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789 # 随机选取4个值作为验证码 rand_str = '' for i in range(0, 4): rand_str += str1[random.randrange(0, len(str1))] # 构造字体对象,ubuntu的字体路径为“/usr/share/fonts/truetype/freefont” font = ImageFont.truetype('AdobeFanHeitiStd-Bold.otf', 23) # 构造字体颜色 fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255)) # 绘制4个字 draw.text((5, 2), rand_str[0], font=font, fill=fontcolor) draw.text((25, 2), rand_str[1], font=font, fill=fontcolor) draw.text((50, 2), rand_str[2], font=font, fill=fontcolor) draw.text((75, 2), rand_str[3], font=font, fill=fontcolor) # 释放画笔 del draw # 【1】存入session,用于做进一步验证 request.session['verifycode'] = rand_str # 内存文件操作 buf = io.BytesIO() # 【2】将图片保存在内存中,文件类型为png im.save(buf, 'png') # 【3】将内存中的图片数据返回给客户端,MIME类型为图片png return HttpResponse(buf.getvalue(), 'image/png')path('verify_code',views.verify_code),#生成验证码
【1】添加验证码,直接把验证码地址写在src内
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>登录页面</title></head><body><form method="post" action="/login_check"> {% csrf_token %} 用户名:<input type="text" name="username" value="{{ username }}"><br/> 密码:<input type="password" name="password"><br/> <input type="checkbox" name="remember">记住用户名<br/> <!--【1】添加验证码,直接把地址写在src内--> <img src="/verify_code"><br/> <input type="text" name="vcode">输入验证码<br/> <input type="submit" value="登录"></form></body></html>【1】获取用户输入验证码
【2】获取session中保存的验证码(在第1步)
【3】进行验证码校验,如果验证码输入不对直接返回登录页面,下面的密码验证就不再进行操作了
def login(request): '''登录页''' # 判断用户是否登录,用户已登录, 直接跳转到图书列表 if request.session.has_key('islogin'): return redirect('/change_pwd/') else: #如果用户名密码已经在cookie中,则取到它,并做为参数返回给渲染页面 if 'username' in request.COOKIES: #获取cookie中的用户名、密码 username=request.COOKIES['username'] #password=request.COOKIES['password'] else: username='' #password='' return render(request,'app2/login.html',{'username':username}) #,'password':passworddef login_check(request): '''登录校验''' #1.获取用户名密码 username=request.POST.get('username') password=request.POST.get('password') remember=request.POST.get('remember') #接收remeber vcode1 = request.POST.get('vcode') # 【1】获取用户输入验证码 vcode2 = request.session.get('verifycode') # 【2】获取session中保存的验证码 # 【3】进行验证码校验,如果验证码输入不对直接返回登录页面,下面的密码验证就不再进行操作了 if vcode1 != vcode2: # 验证码错误 return redirect('/login') #2.进行校验,并返回json数据 if username=='jim' and password=='123': #return redirect('/books') response = JsonResponse({'res':1,'msg':'login success!'}) #密码正确,登录成功,返回1 #如果remember==on,则把用户名,密码设置cookie到cookie if remember=='on': response.set_cookie('username',username,max_age=7*24*3600) #response.set_cookie('password',password,max_age=7*24*3600) # 记住用户登录状态把用户名设置到session request.session['username'] = username # 返回应答 # 如果用户勾选了remember的条件下,设置session,记住用户登录状态 request.session['islogin'] = True # 只有session中有islogin,就认为用户已登录 return response #不要忘记返回response else: #return redirect('/login') return JsonResponse({'res':0,'msg':'login faild'}) #密码错误返回0
【参考】https://blog.csdn.net/weixin_43883625/article/details/100545439
【1】配置namespace,注意写法:re_path(r‘^‘,include((‘app2.urls‘,‘booktest‘),namespace="booktest"))
from django.contrib import adminfrom django.urls import path,include,re_pathurlpatterns = [ path('admin/', admin.site.urls), # 【1】配置namespace,注意写法:include(('app2.urls','booktest'),namespace="booktest") re_path(r'^',include(('app2.urls','booktest'),namespace="booktest")),]【1】2.0之前写法:url(r‘^‘, include(‘app2.urls‘, namespace=‘booktest‘)),
"""from django.conf.urls import include, urlfrom django.contrib import adminurlpatterns = [ url(r'^admin/', include(admin.site.urls)), #【1】2.0之前写法:include('app2.urls', namespace='booktest') url(r'^', include('app2.urls', namespace='booktest')),]【1】 配置name反向解析配置path(‘index2/‘, views.index, name=‘index‘)
from django.contrib import adminfrom django.urls import path,re_pathfrom . import viewsurlpatterns = [ # 【1】 配置name反向解析配置path('index2/', views.index, name='index') path('index/', views.index, name='index'), path('url_reverse/', views.url_reverse),]# /url_reversedef url_reverse (request): return render(request,'app2/url_reverse.html')【1】反向解析模板写法 <a href="{% url ‘booktest:index‘ %}">反向解析</a>
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>反向解析</title></head><body><a href="/index">写死地址</a><br/><!--【1】反向解析模板写法--><a href="{% url 'booktest:index' %}">反向解析</a></body></html>写死地址 http://127.0.0.1:8000/index
反向解析 http://127.0.0.1:8000/index
path(‘index/‘, views.index, name=‘index‘) 变 path(‘index2/‘, views.index, name=‘index‘)时。from django.shortcuts import render,redirectfrom django.http import HttpResponse,JsonResponse# show_argsdef show_args(request, a, b): return HttpResponse(a+':'+b)# show_kwargsdef show_kwargs(request, c, d): return HttpResponse(c+':'+d)【1】反向解析+分组传参数
【2】反向解析+字典传参数(以下两种写法都可)
from django.contrib import adminfrom django.urls import path,re_pathfrom . import viewsurlpatterns = [ re_path(r'^show_args/(\d+)/(\d+)', views.show_args,name='show_args'),#【1】反向解析+分组传参数 #【2】反向解析+字典传参数(以下两种写法都可) #path(r'show_kwargs/<str:c>/<str:d>',views.show_kwargs,name='show_kwargs'), path(r'show_kwargs/<c>/<d>',views.show_kwargs,name='show_kwargs'), ]【1】反向解析+分组传参:href="{% url ‘booktest:show_args‘ 1 2 %}"
【2】反向解析+字典传参:href="{% url ‘booktest:show_kwargs‘ c=3 d=4 %}"
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>反向解析</title></head><body><a href="/index">写死地址</a><br/><a href="{% url 'booktest:index' %}">反向解析</a><hr/><br/><a href="/show_args/1/2">写死+分组传参:/show_args/1/2</a><br/><br/><a href="{% url 'booktest:show_args' 1 2 %}">【1】反向解析+分组传参:/show_args/1/2</a><br/>{% url 'booktest:show_args' 1 2 %}<hr/><br/><a href="/show_kwargs/3/4">写死+字典传参:/show_kwargs/3/4</a><br/><br/><a href="{% url 'booktest:show_kwargs' c=3 d=4 %}">【2】反向解析+字典传参:/show_kwargs/3/4</a><br/>{% url 'booktest:show_kwargs' c=3 d=4 %}</body></html>【1】写死+分组传参:/show_args/1/2
【2】反向解析+分组传参:/show_args/1/2
【3】写死+字典传参:/show_kwargs/3/4
【4】反向解析+字典传参:/show_kwargs/3/4
urlpatterns = [ re_path(r'^show_args2/(\d+)/(\d+)', views.show_args,name='show_args'),#【1】反向解析+分组传参数 #【2】反向解析+字典传参数(以下两种写法都可) #path(r'show_kwargs/<str:c>/<str:d>',views.show_kwargs,name='show_kwargs'), path(r'show_kwargs2/<c>/<d>',views.show_kwargs,name='show_kwargs'), ]redirect(‘/index‘)reverse(‘booktest:index‘)reverse(‘booktest:show_args‘, args=(1,2))reverse(‘booktest:show_kwargs‘, kwargs={‘c‘:3, ‘d‘:4})# from django.core.urlresolvers import reverse #【0-1】2.0之前导入reversefrom django.urls import reverse #【0-2】2.0之后导入reverse# /test_redirectdef test_redirect(request): # 【0写死】重定向到/index # return redirect('/index') # 【1反向解析】 # url = reverse('booktest:index') # 【2反向解析-分组传参】重定向到/show_args/1/2 url = reverse('booktest:show_args', args=(1,2)) # 【3反向解析-字典(关键字)传参】重定向到/show_kwargs/3/4 #url = reverse('booktest:show_kwargs', kwargs={'c':3, 'd':4}) return redirect(url)path('test_redirect/',views.test_redirect),访问:http://127.0.0.1:8000/test_redirect 会动态反向解析到对应的网址。3.2切换也没办法