目录结构
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: 727803257@qq.com ''' # 请从这行开始编写脚本 # 在这个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: 727803257@qq.com ''' # 请从这行开始编写脚本 # 在这个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")
细节:
- 被加密后的密码存储在数据表里不是明文而是密文了;