Django視圖
一.登錄系統
1.寫一個表格
對於一個登錄系統來說,首頁是一個登錄頁面
<html>
<head>
<title>Django Page</title>
</head>
<body>
<h1>發佈會管理</h1>
<form>
<input name="username" type="text" placeholder="username" ><br>
<input name="password" type="password" placeholder="password"><br>
<button id="btn" type="submit">登錄</button>
</form>
</body>
</html>
- 增加get方法
<html>
<head>
<title>Django Page</title>
</head>
<body>
<h1>發佈會管理</h1>
<form method="get">
<input name="username" type="text" placeholder="username" ><br>
<input name="password" type="password" placeholder="password"><br>
<button id="btn" type="submit">登錄</button>
</form>
</body>
</html>
但是get方法對於信息傳遞是沒有加密的,所以應該使用post方法
- 增加post方法
把表格請求的方法換成post
“CSRF verification failed. Request aborted.”
但是之後請求的時候會出現報錯,這個是CSRF攻擊,通過僞裝成受信任用戶的請求來利用受信任的網站,如果攻擊者針對站點僞造鏈接,當該站點的用戶點擊該鏈接,且該站點使用的是持久化的cookies策略,攻擊者就會獲取用戶認證信息,進行攻擊,當然,這裏也可以不用管。
- Django對CSRF的保護
Django 針對CSRF 的保護措施是在生成的每個表單中放置一個自動生成的令牌,通過這個令牌判斷POST請求是否來自同一個網站。
所以這裏使用了Django的模板標籤(template tag)添加CSRF令牌,在form表單裏面添加{% csrf_token %}
<html>
<head>
<title>Django Page</title>
</head>
<body>
<h1>發佈會管理</h1>
<form action="/login_action/">
<input name="username" type="text" placeholder="username" ><br>
<input name="password" type="password" placeholder="password"><br>
<button id="btn" type="submit">登錄</button>
{% csrf_token %}
</form>
</body>
</html>
這樣在頁面想Django服務器發送Post請求的時候,客戶端會加上一個srfmiddlewaretoken字段,給當前會話ID加上密鑰
2.處理登錄請求
- 指定提交路徑
對於form表單提交的數據,我們需要指定提交路徑,修改form的路徑
<form method="post" action="/login_action/">
- 新增login_action路由
打開urls.py文件
from blog import views
urlpatterns = [
……
path(r'login_action/', views.login_action),
]
- 新增視圖函數
在view文件中增加login_action函數
def login_action(request):
if request.method == 'POST':
username = request.POST.get('username', '')
password = request.POST.get('password', '')
if username == 'test' and password == 'test123':
return HttpResponse('login success!')
else:
return render(request,'index.html', {'error': 'username or password error!'})
else:
return render(request, 'index.html', {'error': 'username or password error!'})
這裏通過HttpResponse類返回一個登錄成功的信息;然後通過render方法給login頁面返回一個錯誤信息
- 在index頁面增加錯誤提示
<form method="post" action="/login_action/">
<input name="username" type="text" placeholder="username" ><br>
<input name="password" type="password" placeholder="password"><br>
{{ error }}<br>
<button id="btn" type="submit">登錄</button>
{% csrf_token %}
</form>
這裏使用了Django的模板語言 {{ error }},對應着之前render返回的字典中error的value
3.登錄成功頁
- 修改login sucess
這裏要修改成一個新的函數來處理登錄成功的信息,修改登錄成功的操作
def login_action(request):
if request.method == 'POST':
username = request.POST.get('username', '')
password = request.POST.get('password', '')
if username == 'test' and password == 'test123':
return HttpResponseRedirect('/event_manage/')
else:
return render(request,'index.html', {'error': 'username or password error!'})
else:
return render(request, 'index.html', {'error': 'username or password error!'})
這裏使用的是HttpResponseRedirect類,對頁面進行重定向,可以將之前登錄成功的結果重定向到/event_manage/路徑,然後可以創建event_manage 函數,用於返回發佈會管理event_manage.html 面頁。
- 在view中新增函數
def event_manage(request):
return render(request,"event_manage.html")
- 在urls中新增event_manage路由
path(r'event_manage/', views.login_action),
4.代碼更新
這裏項目的源碼更新到了github
二.Cookie 和Session
1.Cookie 和Session基本概念
這裏可以看http協議基礎
2.使用Cookie增加一個用戶名
本節使用Cookie和Session來實現一個功能——
假如用戶通過“zhangsan”登錄,然後,在登錄成功頁顯示“嘿,zhangsan 你好!
- 獲取暱稱
這裏是修改login_action,傳遞數據
def login_action(request):
if request.method == 'POST':
username = request.POST.get('username', '')
password = request.POST.get('password', '')
if username == 'test' and password == 'test123':
response = HttpResponseRedirect('/event_manage/')
# 添加瀏覽器cookie(key value 過期時間)
response.set_cookie('user', username, 3600)
return response
else:
return render(request,'index.html', {'error': 'username or password error!'})
- 得到暱稱
修改event_manage來獲取數據
def event_manage(request):
username = request.COOKIES.get('user', '') # 讀取瀏覽器cookie(key)停留時間
return render(request, "event_manage.html", {"user": username})
- 展示暱稱
這裏需要修改獲取的數據的模板event_manage.html
<html>
<head>
<title>Event Manage Page</title>
</head>
<body>
<h1>Login Success!</h1>
<div style="float:right;">
<a>嘿!{{ user }} 歡迎</a><hr/>
</div>
</body>
</html>
然後登錄成功的時候就可以展示登錄成功的用戶暱稱
3.修改成Session展示
使用Cookie來存儲數據顯而易見是不夠安全的,所以我們需要使用Session,把數據存在服務端,以防攻擊者修改數據
login_action修改
修改
response.set_cookie('user', username, 3600)
成
request.session['user'] = username # 將session 信息記錄到瀏覽器
event_manage修改
修改
username = request.COOKIES.get('user', '')
成
username = request.session.get('user', '') # 讀取瀏覽器session
但是還沒完,需要補充數據表
創建django_session表
對於session來說,服務端來存儲用戶的sessionId等數據,Django是一個服務端,所以我們需要一個django_session表來存儲數據
- 生成數據表,通過“migrate”命令進行數據遷移
python manage.py migrate
這裏在setting文件中有一個默認配置DATABASES。
然後重新登錄即可
通過Chrome瀏覽器的抓包工具可以看到,在登錄的時候接口傳遞的由cookie變成了session
三.Django認證系統
之前只是對登錄的功能實現了驗證,但是用戶名密碼是在代碼中寫死的,這裏我們需要增加用戶數據
1.配置用戶
首先先新建一個超級管理員帳戶
python manage.py createsuperuser
Username (leave blank to use 'fnngj'): admin #輸入用戶名
Email address: admin #輸入郵箱
Password: #輸入密碼
Password (again): #重複輸入密碼
Superuser created successfully.
然後就可以登錄到admin頁面
然後使用超級用戶登錄到對應的admin頁面,增加用戶組,增加用戶,並設置用戶狀態,最後這個用戶可以登錄併到該頁面
2.引用Django的認證登錄
修改登錄邏輯
from django.contrib import auth
def login_action(request):
if request.method == 'POST':
username = request.POST.get('username', '')
password = request.POST.get('password', '')
user = auth.authenticate(username=username,password=password)
if user is not None:
# 新增登錄驗證邏輯
auth.login(request, user)
request.session['user'] = username
response = HttpResponseRedirect('/event_manage/')
return response
else:
return render(request,'index.html', {'error': 'username or password error!'})
使用authenticate方法,用戶名存在的話返回一個User對象,不存在會返回None;如果返回的不是就會返回None
3.加上登錄限制
在之前的設計中,可以直接通過路徑跳轉到登錄成功的頁面,我們需要對路徑進行限制
下面是對登錄頁限制
from django.contrib.auth.decorators import login_required
@login_required
def event_manage(request):
username = request.session.get('user', '')
return render(request,"event_manage.html",{"user":username})
增加之後,之間通過瀏覽器請求http://127.0.0.1:8000/event_manage/頁面將會返回一個失敗提示。
可以看到這裏配置的accounts/login是一個默認路徑跳轉
http://localhost:8000/accounts/login/?next=/event_manage/
然後把這個跳轉等價
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/',views.archive),
path('index/',views.index),
path('accounts/login/',views.index),
path('login_action/', views.login_action),
path('event_manage/', views.event_manage),
]