1. 程式人生 > >Django基礎04-day19

Django基礎04-day19

一個 2周 size cut http請求 rfi something con www

寫在前面



上課第19天,打卡:

    忠於你的理想,別想生活妥協,讓掙紮變得有意義!



################
# 2017-09-03 - 課上筆記
################



class day19:
	def __init__():
		pass

	def do_homework():
		pass

	def do_my_project():
		pass




‘‘‘Django 回顧‘‘‘

	- http請求周期
		瀏覽器(socket客戶端)
			2.socket.connect(ip,port) 進行連接
			3.socket.send("http://www.qq.com/index.html...")    url + data
				遵循的規則:http協議
					請求頭
					請求體


請求頭和請求體使用 ‘\r\n\r\n‘ 分隔,前面是請求頭,後面是請求體
GET請求: "GET /index.html?key=1... Http/1.1\r\nhost:www.qq.com\r\ncontent-type:application/json\r\n\r\n"

POST請求:"POST /index.html Http/1.1\r\nhost:www.qq.com\r\ncontent-type:application/json\r\n\r\nname=alex&pwd=123"

			6.獲取響應
				響應頭,響應體 = data.split("\r\n\r\n")

				響應頭之間用 ‘\r\n‘ 分隔

			7.斷開連接


		nginx(socket服務端)
			1.server.run(),監聽IP和PORT
			4.server.recv()

				請求頭,請求體 = data.split("\r\n\r\n")
				request.POST.get(‘name‘)  即是從 請求體 裏取值
			5.服務端進行響應:
				conn.send(‘......‘)
				遵循的規則:http協議
					響應頭
					響應體

			7.斷開連接

	總結:
		a.Http請求中本質都是字符串
		b.Http請求是短連接(請求 -> 響應 -> 斷開連接)
		c.請求和響應都有頭和體
			請求:請求頭‘\r\n\r\n‘請求體
			響應:響應頭‘\r\n\r\n‘響應體





由於需要處理繁瑣http的解析和封裝處理工作,所以web框架應運而生


web框架
	- Django
		socket(wsgiref)   django沒有自己寫socket
		解析和封裝http請求

	django-admin startproject mysite
	
	cd mysite
	python manage.py startapp app01

	coding...(*****)

	python manage.py runserver ip:port




‘‘‘寫代碼‘‘‘

	- 路由系統
		/login/   			func   	name=‘f1‘
		/login/\d+/ 		func	name=‘f2‘
		/login/(?P<n>\d+)/	func	name=‘f3‘
		/login/\d+/			include(‘app01.urls‘)


	- 視圖函數
		def index(request):

			request.GET
			request.body	    原生的請求體
			request.POST        轉換後的請求體字典   如果請求頭中content-type=urlencode-form... 才將request.body轉換成字典
				- 可能有值
				- 也可能沒有值
			request.method
			request.Meta

			request.GET.get()
			request.GET.getlist()        前端多選的情況,如多個作者
			request.POST.get()
			request.POST.getlist()



			return HttpResponse(‘字符串/字節‘)
			return render(request,"html路徑",locals())
			return redirect("url")


	- 模板
		for if
		
		繼承

		filter,simple_tag



	- Models操作
		- 創建表
		- models.xxx.objects.create(name="xxxx")
		- models.xxx.objects.create(**dic)
		- models.xxx.objects.filter(id__gt=1).delete()
		- models.xxx.objects.filter(id=1)
		- models.xxx.objects.exclude(id=5)     取出id不等於5的
		- models.xxx.objects.filter(id=1).update(name=‘ddd‘)
		- models.xxx.objects.filter(id=1).update(**dic)


		queryset --> [對象,對象...]
		objs = models.xxx.objects.all()

		queryset --> [{},{}...]
		objs = models.xxx.objects.all().values()

		queryset --> [(),()...]
		objs = models.xxx.objects.all().values_list()


demo1
	業務線表  bussiness_unit
		id name
	主機表 serverinfo
		id host port bs(業務線對象)


	objs = modesl.serverinfo.objects.all()
	for row in objs:
		print(row.id)
		print(row.host)
		print(row.port)
		print(row.bs.name)   外鍵,拿到業務線的名字

	objs = modesl.serverinfo.objects.all().values("id","host","port","bs__name")
	for row in objs:
		print(row[‘host‘])
		print(row[‘bs__name‘])



demo2 (userinfo 和 bussiness_unit 是多對多關系)
	用戶表 userinfo
		id user pwd email mm(ManyToMany)
	業務線表  bussiness_unit
		id name
	主機表
		id host port bs(業務線對象)
	用戶業務線關系表  *****
		id user_id bs_id

	obj = models.user.objects.filter(user=‘alex‘).first()
	obj.mm.add(1)
	obj.mm.add(11)


	- 通過用戶對象查所負責的所有業務線對象
	obj = models.user.objects.filter(user=‘alex‘).first()
	queryset = obj.mm.all()          拿到alex負責的所有業務線  -> [業務線對象,業務線對象...]
	for row in queryset:
		print(row.id)
		print(row.name)


	- 通過業務線反查對應有哪些人負責? (***反查***    表名_set)
	obj = models.bussiness_unit.objects.filter(name=‘二手車‘).first()
	queryset = obj.userinfo_set.all()   拿到負責二手車業務線的用戶對象  ->  [用戶對象,用戶對象...]
	for row in queryset:
		print(row.user)
		print(row.pwd)

總結:
	1.多對多關系建在哪張表上都可以;
	2.如果建在userinfo表上,那麽通過用戶對象查所負責的bs對象列表就直接用 obj.mm.all()
	方便了 userinfo對象,但是bs對象反查userinfo就得用 userinfi_set.all()





==================================================================

今日內容:

1.登錄
	- 密碼加密,對密碼進行比較
	- 用戶登錄之後才能訪問某些頁面


2.cookie是什麽?
	- 保存在客戶端瀏覽器上的鍵值對 {k:v}
	- cookie依附在請求頭或者響應頭中
	- 瀏覽器發送請求時會自動攜帶所訪問網站對應的cookie
	- 應用
		- 實現登錄
		- 投票
		- 每頁顯示10條/20條...

	- 使用
		- 設置
			response =  redirect(‘/index/‘)
            response.set_cookie(‘my_cookie‘,md5.encrypt(‘xxx‘))
            return response

key, 
value=‘‘, 
max_age=None,		超時時間:秒數
expires=None, 		超時時間:截止日期
path=‘/‘,			cookie在哪個url裏生效 :  訪問指定url時才能讀取到cookie,   ‘/‘  表示全部頁面都可以
domain=None, 		當前域名或者二級域名
secure=False, 		https
httponly=False  	


response = redirect(‘/index/‘)
# 設置cookie
response.set_cookie(‘my_cookie‘,md5.encrypt(user))
return response
# 設置cookie過期時間
import datetime
deadline = datetime.datetime.utcnow() + datetime.timedelta(seconds=5)
response.set_cookie(‘my_cookie‘,md5.encrypt(user),expires=deadline)
response.set_cookie(‘my_cookie‘,md5.encrypt(user),max_age=5)



        - 獲取
            ck = request.COOKIES.get(‘my_cookie‘)

# 詳細代碼如下:        
‘‘‘
# models.py

from django.db import models

# Create your models here.

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    email = models.EmailField(null=True)

‘‘‘    

‘‘‘
from django.shortcuts import render,HttpResponse,redirect
from app01 import models
from common import md5


# 判斷用戶是否登錄的裝飾器(通過cookie判斷)
def auth(func):
    def inner(request,*args,**kwargs):
        ck = request.COOKIES.get(‘my_cookie‘)
        if not ck:
            return redirect(‘/login/‘)
        return func(request,*args,**kwargs)
    return inner

@auth
def index(request):
    user = request.COOKIES.get(‘my_cookie‘)
    print(request.COOKIES)
    # return HttpResponse("登錄成功")
    return render(request,‘index.html‘,locals())


def login(request):
    if "GET" == request.method:
        return render(request,‘login.html‘)
    else:
        user = request.POST.get(‘user‘)
        pwd = request.POST.get(‘pwd‘)
        obj = models.UserInfo.objects.filter(username=user,password=md5.encrypt(pwd)).first()
        if obj:

            response = redirect(‘/index/‘)
            # 設置cookie過期時間
            # import datetime
            # deadline = datetime.datetime.utcnow() + datetime.timedelta(seconds=5)
            # response.set_cookie(‘my_cookie‘,md5.encrypt(user),expires=deadline)
            # response.set_cookie(‘my_cookie‘,md5.encrypt(user),max_age=5)

            # 設置cookie
            response.set_cookie(‘my_cookie‘,user)
            return response
        else:
            return render(request,‘login.html‘,{‘msg‘:"用戶名或密碼錯誤"})
‘‘‘

‘‘‘
# login.html

...
<form action="/login/" method="POST">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="text" name="pwd">
    <input type="submit" value="提交"><span style="color: red;">{{ msg }}</span>
</form>
...


# index.html
...
<h1>{{ user }}</h1>
...
‘‘‘
‘‘‘
# md5.py

def encrypt(pwd):
    import hashlib
    obj = hashlib.md5()
    obj.update(pwd.encode(‘utf-8‘))
    data = obj.hexdigest()
    return data

if __name__ == ‘__main__‘:
    print(encrypt(‘123‘))
‘‘‘



3.session
	
	是保存在服務器端的鍵值對 {k:v}

	依賴cookie

	Django默認支持Session,並且默認是將Session數據存儲在數據庫中,即:django_session 表中。
	更多參考:http://www.cnblogs.com/wupeiqi/articles/5246483.html


	生成隨機字符串,並將其當做cookie發送給客戶端
	服務端設置隨機字符串作為key,自己設置一些{}:request.session[‘my_session_key‘] = user


	- 設置session
        request.session[‘yyy‘] = user
        return redirect(‘/index/‘)


    - 獲取session
    	# 裝飾器
		def auth(func):
		    def inner(request,*args,**kwargs):
		        ck = request.session.get(‘yyy‘)
		        if not ck:
		            return redirect(‘/login/‘)
		        return func(request,*args,**kwargs)
		    return inner


    - 清空session
    	request.session.clear()


http://www.cnblogs.com/wupeiqi/articles/5246483.html

SESSION_ENGINE = ‘django.contrib.sessions.backends.db‘  # 引擎(默認)
SESSION_COOKIE_NAME = "sessionid"        # Session的cookie保存在瀏覽器上時的key,即:sessionid=隨機字符串(默認)
SESSION_COOKIE_PATH = "/"                # Session的cookie保存的路徑(默認)
SESSION_COOKIE_DOMAIN = None             # Session的cookie保存的域名(默認)
SESSION_COOKIE_SECURE = False            # 是否Https傳輸cookie(默認)
SESSION_COOKIE_HTTPONLY = True           # 是否Session的cookie只支持http傳輸(默認)
SESSION_COOKIE_AGE = 1209600             # Session的cookie失效日期(2周)(默認)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False  # 是否關閉瀏覽器使得Session過期(默認)
SESSION_SAVE_EVERY_REQUEST = False       # 是否每次請求都保存Session,默認修改之後才保存(默認)



# 詳細代碼如下:
‘‘‘
from django.shortcuts import render,HttpResponse,redirect
from app01 import models
from common import md5


# 判斷用戶是否登錄的裝飾器(通過session判斷)
def auth(func):
    def inner(request,*args,**kwargs):
        ck = request.session.get(‘my_session_key‘)
        if not ck:
            return redirect(‘/login/‘)
        return func(request,*args,**kwargs)
    return inner



@auth
def index(request):
    user = request.session.get(‘my_session_key‘)
    return render(request,‘index.html‘,locals())


@auth
def order(request):
    return render(request,‘order.html‘)


# 登出view
def logout(request):
	# 用戶登出後清空session
    request.session.clear()
    return redirect(‘/index/‘)


def login(request):
if "GET" == request.method:
    return render(request,‘login.html‘)
else:
    user = request.POST.get(‘user‘)
    pwd = request.POST.get(‘pwd‘)
    obj = models.UserInfo.objects.filter(username=user,password=md5.encrypt(pwd)).first()
    if obj:
        # 設置session
        request.session[‘my_session_key‘] = user
        return redirect(‘/index/‘)
    else:
        return render(request,‘login.html‘,{‘msg‘:"用戶名或密碼錯誤"})   
‘‘‘

‘‘‘
# urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r‘^admin/‘, admin.site.urls),
    url(r‘^login/‘, views.login),
    url(r‘^logout/‘, views.logout),
    url(r‘^index/‘, views.index),
    url(r‘^order/‘, views.order),
]
‘‘‘


‘‘‘
# login.html

...
<form action="/login/" method="POST">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="text" name="pwd">
    <input type="submit" value="提交"><span style="color: red;">{{ msg }}</span>
</form>
...


# index.html
...
<h1>{{ user }}</h1>
...



# order.html
...
<body>
<h1>歡迎登錄:{{ request.session.my_session_key }}</h1>
<a href="/logout/">註銷</a>
</body>
...
‘‘‘







4.csrf 跨站請求偽造



<form action="/login/" method="POST">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="text" name="pwd">
    <input type="submit" value="提交"><span style="color: red;">{{ msg }}</span>
</form>

{% csrf_token %}  在瀏覽器裏默認就是一個隱藏的input標簽,如下所示:

<form action="/login/" method="POST">
    <input type=‘hidden‘ name=‘csrfmiddlewaretoken‘ value=‘T2Ub1TacecIsEsKJvoUvB3xNSwrEGT0NajwGeO6y58mp1IseYVLL3FBnXtOT3WgW‘ />
    <input type="text" name="user">
    <input type="text" name="pwd">
    <input type="submit" value="提交"><span style="color: red;"></span>
</form>

而 {{ csrf_token }} 這個就是這個隱藏標簽的value值






跨站請求的漏洞:
<form method="POST" action="http://www.icbc.com.cn/icbc/">
	<input type="text" name="from" style="display: none;" value="A的卡號">
	<input type="text" name="to" style="display: none;" value="黑客的卡號">
	<input type="text" name="money" style="display: none;" value="1000000000">
	<input type="submit" name="" value="點我">
</form>

{% csrf_token %}




# 首先不提交 csrf_token 的情況:報錯403,CSRF verification failed. Request aborted.
<form id="my_form" action="/login/" method="POST">
    <input type="text" name="user">
    <input type="email" name="email">
    <button onclick="ajaxSubmit()">Ajax提交</button>
</form>
<script src="{% static "js/bootstrap.min.js" %}"></script>
<script>
    function ajaxSubmit() {
        $.ajax({
            url:‘/show/‘,
            type:‘POST‘,
            data:{
                ‘user‘:$(‘#my_form input[name="user"]‘).val(),
                ‘email‘:$(‘#my_form input[name="email"]‘).val()
            },
            success:function (data) {
                console.log(data);
            }
        })
    }
</script>

# 註意一點:
# 如果form表單不寫action,則默認提交到當前頁面


ajax提交csrf_token的幾種方式:

# 方式1
<form id="my_form" action="/show/" method="post">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="email" name="email">
    <button onclick="ajaxSubmit()">Ajax提交</button>
</form>
<script src="{% static "js/bootstrap.min.js" %}"></script>
<script>
    function ajaxSubmit() {
        $.ajax({
            url:‘/show/‘,
            type:‘POST‘,
            data:{
                ‘user‘:$(‘#my_form input[name="user"]‘).val(),
                ‘email‘:$(‘#my_form input[name="email"]‘).val(),
                ‘csrfmiddlewaretoken‘:$(‘input[name="csrfmiddlewaretoken"]‘).val()
            },
            success:function (data) {
                console.log(data)
            }
        })
    }
</script>


# 方式2   只能寫在模板裏
<body>
<form id="my_form" action="/show/" method="post">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="email" name="email">
    <button onclick="ajaxSubmit()">Ajax提交</button>
</form>
<script src="{% static "js/bootstrap.min.js" %}"></script>
<script>
    function ajaxSubmit() {
        $.ajaxSetup({
            data: {‘csrfmiddlewaretoken‘:‘{{ csrf_token }}‘}
        });
        $.ajax({
            url:‘/show/‘,
            type:‘POST‘,
            data:{
                ‘user‘:$(‘#my_form input[name="user"]‘).val(),
                ‘email‘:$(‘#my_form input[name="email"]‘).val()
            },
            success:function (data) {
{#                do something...#}
            }
        })
    }
</script>
</body>

# 後端得到的數據:
	類型:<class ‘django.http.request.QueryDict‘>
	數據:<QueryDict: {‘csrfmiddlewaretoken‘: [‘raZNrc77aQn7cr5Wr6gtTgOaTdNWZKF0HmAfN6kqlGzmyrr4Dw7DUcSVQ6ZHcFoQ‘], ‘email‘: [‘[email protected]
	om‘], ‘user‘: [‘borui‘]}>



# 方式3   只能寫在模板裏
<form id="my_form" action="/show/" method="post">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="email" name="email">
    <button onclick="ajaxSubmit()">Ajax提交</button>
</form>
<script src="{% static "js/bootstrap.min.js" %}"></script>
<script>
    function ajaxSubmit() {
        $.ajax({
            url:‘/show/‘,
            type:‘POST‘,
            data:{
                ‘user‘:$(‘#my_form input[name="user"]‘).val(),
                ‘email‘:$(‘#my_form input[name="email"]‘).val(),
                ‘csrfmiddlewaretoken‘:"{{ csrf_token }}"
            },
            success:function (data) {
{#                do something...#}
            }
        })
    }
</script>

這種方法,循環form表單,把很多input值拿出來組成字典,
然而實際上POST請求最後是需要轉換成字符串放到請求體中發給後端的,實際上的字符串如下:
‘csrfmiddlewaretoken=ouyWxV86TJWMttyLwzRkORIcqXjInlDREG9oTPlp4z81PtUTIZIuPNMXnQvtAgmH&user=love&email=love%40qq.com‘

所以,如果ajax裏的data字段如果寫成一個字典,那麽就需要一個轉成字符串的過程;
如果直接寫成字符串,也是可以的;
$(‘#my_form‘).serialize()  這個方法就可以把form表單裏所有的值(包含隱藏的csrf標簽)拼接成一個字符串;



# 方法4:
<form id="my_form" action="/show/" method="post">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="email" name="email">
    <button onclick="ajaxSubmit()">Ajax提交</button>
</form>
<script src="{% static "js/bootstrap.min.js" %}"></script>
<script>
    function ajaxSubmit() {
        $.ajax({
            url:‘/show/‘,
            type:‘POST‘,
            data:$(‘#my_form‘).serialize(),
            success:function (data) {
                console.log(data)
            }
        })
    }
</script>


‘‘‘ 以上4中方法都是把csrf_token放入 請求體 裏傳遞給後端 ‘‘‘


# 方法5   把csrftoken對應的值方到 請求頭 裏,傳遞給後端,這樣也可以通過csrf驗證
	首先引入 jquery.cookie.js 插件
	然後通過 $.cookie(‘csrftoken‘) 則可以獲取到csrftoken對應的值

這種方法,是把csrftoken對應的值放到請求頭裏,必須按照這個格式:headers:{‘X-CSRFToken‘: $.cookie(‘csrftoken‘)}
這種情況,請求體的內容可以隨便寫;

<form id="my_form" action="/show/" method="post">
    {% csrf_token %}
    <input type="text" name="user">
    <input type="email" name="email">
    <button onclick="ajaxSubmit()">Ajax提交</button>
</form>
<script src="{% static "js/jquery-3.2.1.min.js" %}"></script>
<script src="{% static "js/jquery.cookie.js" %}"></script>
<script src="{% static "js/bootstrap.js" %}"></script>

<script>
    function ajaxSubmit() {
        $.ajax({
            url:‘/show/‘,
            type:‘POST‘,
            data:{‘k1‘:‘v1‘,‘k2‘:‘v2‘},
            headers:{‘X-CSRFToken‘: $.cookie(‘csrftoken‘)},
            success:function (data) {
                console.log(data)
            }
        })
    }
</script>



總結:
	基於ajax提交form表單,發送csrf驗證的方式裏
	最常用的就是 data:$(‘#my_form‘).serialize() 和 headers:{‘X-CSRFToken‘: $.cookie(‘csrftoken‘)}


另外:
	關於csrf有兩個裝飾器:from django.views.decorators.csrf import csrf_exempt,csrf_protect
	‘django.middleware.csrf.CsrfViewMiddleware‘, 
	# 開啟則表示全站都使用csrf驗證,而csrf_exempt這個裝飾器則表示哪些view可以不使用csrf
	# 如果不開啟,則表示全站都不使用csrf驗證,而csrf_protect這個裝飾器則表示哪些view可以使用csrf

  

# 自定義分頁模塊


request.path_info

  

Django基礎04-day19