目錄結構
2.1.3.新增一個【register_html.html】
2.1.4.新增一個【register_success_html.html】
2.1.5.新增一個【update_password_html.html】
2.1.6.新增一個【update_password_success_html.html】
1.寫這篇博客的目的
主要記錄表單提交方式post的常見的具體使用;
因爲廈門大多數公司項目接口用的都是post表單提交方式,剛好註冊接口/登錄接口/修改登錄密碼接口/忘記密碼接口的表單提交方式基本都是用post,所以這篇博客裏拿註冊接口/登錄接口/修改登錄密碼接口這三個接口串聯起來講完整的操作流程;
我們要實現的完整操作流程的步驟大致如下:
- 用戶通過訪問【註冊地址】,進到了【註冊頁面】;
- 用戶在【註冊頁面】進行註冊,會校驗每個提交字段的字段值規則:註冊失敗會停留在【註冊頁面】並給出對應錯誤提示語;註冊成功後會跳轉到【註冊成功提示頁面】;
- 用戶在【註冊成功提示頁面】點擊【返回登錄頁面的一個鏈接】,會跳轉到【登錄頁面】;
- 用戶在【登錄頁面】進行登錄,會校驗每個提交字段的字段值規則:登錄失敗會停留在【登錄頁面】並給出對應錯誤提示語;登錄成功後會跳轉到【後臺主頁頁面】;
- 用戶在【後臺主頁頁面】點擊【退出登錄頁面的一個鏈接】,會跳轉到【登錄頁面】;
- 用戶在【登錄頁面】進行登錄,登錄成功後跳轉到【後臺主頁頁面】;
- 用戶在【後臺主頁頁面】點擊【修改登錄密碼的一個鏈接】,會跳轉到【修改登錄密碼頁面】;
- 用戶在【修改登錄密碼頁面】,會校驗每個提交字段的字段值規則:修改登錄密碼失敗會停留在【修改登錄密碼頁面】並給出對應錯誤提示語;修改登錄密碼成功後會跳轉到【修改登錄密碼成功頁面】;
- 用戶在【修改登錄密碼成功頁面】點擊【返回後臺主頁頁面的一個鏈接】,會跳轉到【後臺主頁頁面】;
細節:
- 按照產品文檔寫的具體需求規則來進行對應流程的操作纔是正確的操作;
- 要讓用戶在瀏覽器輸入一個指定的url地址後能訪問到一個指定的html頁面,結合django的MTV思想,就是這樣大致的實現主邏輯:先創建一個html頁面,再創建一個視圖函數,再創建一個url匹配規則,最後進行調試;
- 表單提交方式get和post的各自作用和使用場景,可自行百度,大致瞭解即可;
完整操作流程可以看接下來的內容;
2.完整操作流程
2.1.第一步:新增相關html頁面
按照這樣的文件夾分層,新增兩個新文件夾【index】和【user】,並新增5個html頁面;
2.1.1.新增一個【index_html.html】
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>後臺主頁</title> </head> <body> <p> 這是後臺主頁,您可以進行任意業務操作! <br> <br> <a href="{% url 'urlName_of_login' %}">點擊這個鏈接,可以成功退出登錄並跳轉到登錄頁面</a> <br> <a href="{% url 'urlName_of_update_password' %}">點擊這個鏈接,可以跳轉到修改登錄密碼頁面</a> </p> </body> </html>
2.1.2.新增一個【login_html.html】
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登錄頁面</title> </head> <body> <h1>歡迎登錄!</h1> <form action="" method="post"> {% csrf_token %} <p> 用戶名:<input type="text" id="id_username" name="username" required="required"> * {{ error_of_username }} </p> <p> 密碼:<input type="text" id="id_password" name="password" required="required"> * {{ error_of_password }} </p> <p> <input type="submit" value="登錄"> </p> </form> </body> </html>
2.1.3.新增一個【register_html.html】
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>註冊頁面</title> </head> <body> <h1>新用戶註冊!</h1> <form action="" method="post"> {% csrf_token %} <p> 用戶名:<input type="text" id="id_username" name="username" required="required"> * {{ rename }} </p> <p> 密碼:<input type="text" id="id_password" name="password" required="required"> * </p> <p> 註冊郵箱:<input type="text" id="id_mail" name="mail"> </p> <p> <input type="submit" value="提交註冊信息" > </p> </form> </body> </html>
2.1.4.新增一個【register_success_html.html】
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>註冊成功頁面</title> </head> <body> <p> 親愛的用戶,恭喜您註冊成功! <br> <br> <a href="{% url 'urlName_of_login' %}">點擊這個鏈接,可以跳轉到登錄頁面</a> </p> </body> </html>
2.1.5.新增一個【update_password_html.html】
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>修改登錄密碼頁面</title> </head> <body> <h1>請修改您的登錄密碼!</h1> <form action="" method="post"> {% csrf_token %} <p> 用戶名:<input type="text" id="id_username" name="username" required="required"> * {{ error_of_username }} </p> <p> 舊密碼:<input type="text" id="id_old_password" name="old_password" required="required"> * {{ error_of_old_password }} </p> <p> 新密碼:<input type="text" id="id_new_password" name="new_password" required="required"> * {{ error_of_new_password }} </p> <p> <input type="submit" value="確定"> </p> </form> </body> </html>
2.1.6.新增一個【update_password_success_html.html】
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>修改登錄密碼成功頁面</title> </head> <body> <p> 親愛的用戶,恭喜您修改登錄密碼成功! <br> <br> <a href="{% url 'urlName_of_index' %}">點擊這個鏈接,可以跳轉到後臺主頁頁面</a> </p> </body> </html>
2.2.第二步:新增相關視圖函數
按照這樣的文件夾分層,新增一個新文件夾【views_of_hello】,並新增2個存儲視圖函數的py文件,兩個py文件分別爲【indexViews.py】和【userViews.py】;
2.2.1.新增一個【indexViews.py】
# coding:utf-8 ''' @file: indexViews.py @author: 洪景盛 @ide: PyCharm @createTime: 2021年02月23日 14點59分 @contactInformation: [email protected] ''' # 請從這行開始編寫腳本 # 在這個py文件裏,只存儲操作後臺主頁的所有視圖函數 from django.shortcuts import render from django.http import HttpResponse,Http404 from hello.models import User def index(request): '''用戶登錄成功後跳轉到的後臺主頁對應的視圖函數''' return render(request,'index/index_html.html')
2.2.2.新增一個【userViews.py】
# coding:utf-8 ''' @file: userViews.py @author: 洪景盛 @ide: PyCharm @createTime: 2021年02月23日 11點02分 @contactInformation: [email protected] ''' # 請從這行開始編寫腳本 # 在這個py文件裏,只存儲操作數據表user的所有視圖函數 from django.shortcuts import render from django.http import HttpResponse,Http404,JsonResponse from hello.models import User from django.contrib.auth.hashers import make_password,check_password import re def register(request): '''用戶註冊頁面對應的視圖函數''' res = '' if request.method == 'POST': username = request.POST.get('username') # 用戶名 password = request.POST.get('password') # 密碼 mail = request.POST.get('mail') # 註冊郵箱 # 先查詢數據庫是否已經有這個用戶名 user_list = User.objects.filter(user_name=username) if len(user_list) !=0 or user_list: # 如果這個用戶名已存在,就給個提示 res = '用戶名爲【%s】的用戶已被註冊!'%username return render(request, 'user/register_html.html', {"rename":res}) # 如果這個用戶名不存在即還沒被註冊,就執行新增數據到數據表的操作(涉及到sql新增語句) else: # 第一種寫法(推薦以後都用這種寫法) user = User() user.user_name = username user.user_psw = make_password(password) user.user_email = mail user.save() # 第二種寫法(不推薦) # user = User(user_name=username,user_psw=password,user_email=mail) # user.save() return render(request,'user/register_success_html.html') return render(request, "user/register_html.html") def register_success(request): '''用戶註冊成功後跳轉到的註冊成功頁面對應的視圖函數''' return render(request,'user/register_success_html.html') def login(request): '''登錄頁面對應的視圖函數''' # 第1步:判斷請求方式是不是post if request.method == 'POST': # 第2歩:獲取登錄頁面傳給這個視圖函數的每個提交字段的字段值 username = request.POST.get("username") password = request.POST.get("password") # 第3步:查詢用戶名是否存在:如果不存在則返回一個用戶名相關的錯誤提示語 user_list_1 = User.objects.filter(user_name=username) if len(user_list_1) == 0: error_of_username = '用戶名爲【%s】的用戶不存在,請重新輸入!'%username return render(request,'user/login_html.html',{"error_of_username":error_of_username}) # 第4步:校驗密碼:如果校驗不通過則返回一個密碼相關的錯誤提示語;如果校驗通過則允許登錄成功並跳轉到後臺主頁; user_list_2 = User.objects.filter(user_name=username).first() is_password_true = check_password(password,user_list_2.user_psw) if is_password_true: return render(request,'index/index_html.html') else: error_of_password = '密碼錯誤,請重新輸入!' return render(request,'user/login_html.html',{"error_of_password":error_of_password}) else: return render(request,"user/login_html.html") def update_password(request): '''修改登錄密碼頁面對應的視圖函數''' if request.method == 'GET': return render(request,"user/update_password_html.html") if request.method == 'POST': username = request.POST.get("username") # 用戶名 old_password = request.POST.get("old_password") # 舊密碼 new_password = request.POST.get("new_password") # 舊密碼 if len(username)>10: error_of_username = '用戶名的長度不能超過10' # data = {} # data["code"] = 100 # data["success"] = False # data["error_of_username"] = error_of_username # return JsonResponse(data,json_dumps_params={"ensure_ascii":False}) # 返回一個json串 return render(request,"user/update_password_html.html",{"error_of_username":error_of_username}) if len(old_password)>10: error_of_old_password = '舊密碼的長度不能超過10' return render(request,"user/update_password_html.html",{"error_of_old_password":error_of_old_password}) if len(new_password)>10: error_of_new_password = '新密碼的長度不能超過10' return render(request,"user/update_password_html.html",{"error_of_new_password":error_of_new_password}) if old_password == new_password: error_of_new_password = '新密碼不能跟舊密碼一樣' return render(request,"user/update_password_html.html",{"error_of_new_password":error_of_new_password}) regex = "^[0-9a-zA-Z_]{1,}$" if not re.search(regex,old_password): error_of_old_password = '舊密碼的值只能由數字和英文字母和下劃線組成' return render(request,"user/update_password_html.html",{"error_of_old_password":error_of_old_password}) if not re.search(regex,new_password): error_of_new_password = '新密碼的值只能由數字和英文字母和下劃線組成' return render(request,"user/update_password_html.html",{"error_of_new_password":error_of_new_password}) user_list1 = User.objects.filter(user_name=username).values() if len(user_list1) == 0: error_of_username = '不存在用戶名爲【%s】的用戶'%username return render(request,"user/update_password_html.html",{"error_of_username":error_of_username}) user_list_2 = User.objects.filter(user_name=username).first() is_password_true = check_password(old_password,user_list_2.user_psw) if not is_password_true: error_of_old_password = '舊密碼錯誤,請重新輸入' return render(request,"user/update_password_html.html",{"error_of_old_password":error_of_old_password}) else: user = User.objects.get(user_name=username) user.user_psw = make_password(new_password) user.save() # return HttpResponse("密碼更新成功!") return render(request,"user/update_password_success_html.html")
2.3.第三步:新增url匹配規則
在【helloworld/helloworld/urls.py】裏新增url匹配規則
#這部分是針對數據表user的所有url匹配規則的彙總===================================================== url(r"^user_register_001/$",userViews.register), url(r"^user_register_success_001/$",userViews.register_success), url(r"^user_login_001/$",userViews.login,name="urlName_of_login"), url(r"^user_update_password_001/$",userViews.update_password,name="urlName_of_update_password"), #這部分是針對後臺主頁的所有url匹配規則的彙總===================================================== url(r"^index_001/$",indexViews.index, name="urlName_of_index"),
2.4.第四步:重啓服務
2.5.第五步:在任一瀏覽器上面進行相關操作
細節:
- 在第五步,只記錄個人認爲重要的相關重要截圖,具體調試細節就不具體展開描述;
3.相關知識點
3.1.模板標籤【{% csrf_token %}】的作用
模板標籤【{% csrf_token %}】的作用:一個html頁面裏的form標籤裏的屬性action的屬性值如果爲post,則form標籤的內容裏必須加上這個模板標籤【{% csrf_token %}】,因爲可以防止跨站點僞造請求;
CSRF:Cross Site Request Forgery(英文全稱),跨站點僞造請求(中文全稱)。
舉例來講,某個惡意的網站上有一個指向你的網站的鏈接,如果某個用戶已經登錄到這個惡意的網站上了,那麼當這個用戶點擊這個惡意網站上的那個鏈接時,就會向你的網站發來一個請求,你的網站會以爲這個請求是用戶自己發來的,其實呢,這個請求是那個惡意網站僞造的。
django第一次響應來自某個客戶端的請求時,會在服務器端隨機生成一個token,把這個token放在cookie裏。然後每次POST請求都會帶上這個token,這樣就能避免被CSRF攻擊。
CSRF具體內容大致有這幾點(只需要瞭解概念即可):
- 在返回的HTTP響應的cookie裏,django會爲你添加一個csrftoken字段,其值爲一個自動生成的token;
- 一個html頁面裏的form標籤裏的屬性action的屬性值如果爲post,必須包含一個csrfmiddlewaretoken 字段(只需要在form標籤的內容里加上這個模板標籤【{% csrf_token %}】,django就會自動幫你生成);
- 在處理POST請求之前,django會驗證這個請求的cookie裏的csrftoken字段的值和提交的表單裏的csrfmiddlewaretoken字段的值是否一樣:如果一樣,則表明這是一個合法的請求;否則,這個請求可能是來自於別人的csrf攻擊,返回【403 Forbidden】;
- 在所有ajax方式的POST請求裏,添加一個【X-CSRFTOKEN header】,其值爲cookie裏的csrftoken字段的字段值;
3.2.註冊密碼的加密,和對登錄密碼和舊密碼的密碼校驗
出於安全考慮,一般我們都會對註冊密碼進行加密後才存儲到數據表,對登錄密碼進行密碼校驗,修改密碼時會對舊密碼進行密碼校驗;
django提供了兩個方法:make_password,check_password,密碼加密用make_password,密碼校驗用check_password;
涉及到這兩個方法的具體相關使用可以看下面的代碼內容:
from django.contrib.auth.hashers import make_password,check_password
user = User() user.user_name = username user.user_psw = make_password(password) user.user_email = mail user.save()
user_list_2 = User.objects.filter(user_name=username).first() is_password_true = check_password(old_password,user_list_2.user_psw) if not is_password_true: error_of_old_password = '舊密碼錯誤,請重新輸入' return render(request,"user/update_password_html.html",{"error_of_old_password":error_of_old_password}) else: user = User.objects.get(user_name=username) user.user_psw = make_password(new_password) user.save() # return HttpResponse("密碼更新成功!") return render(request,"user/update_password_success_html.html")
細節:
- 被加密後的密碼存儲在數據表裏不是明文而是密文了;