Python之Django实例--可重用注册登录系统(二)

1. 图片验证码(captcha)

1.1 第三方库的设置(验证码插件)

django-simple-captcha官网

1. pip install django-simple-captcha    ##安装插件
2. settings设置app  #loginRegister/setting.py
3. python manage.py migrate
4. 查看是否添加到数据库
5. 添加信息到#loginRegister/urls.py
	urlpatterns = [
	    path('admin/', admin.site.urls),
	    path('', include('login.urls')),
	    path('captcha/', include('captcha.urls')),
	]

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2 git提交

在这里插入图片描述

1.3 Django表单

  • Django的表单给我们提供了下面三个主要功能:
    1.准备和重构数据用于页面渲染;
    2.为数据创建HTML表单元素;
    3.接收和处理用户从表单发送过来的数据。
1.3.1 创建表单模型
# /login/forms.py(新建的文件)
from captcha.fields import CaptchaField
from django import forms
class LoginForm(forms.Form):
    username = forms.CharField(label='用户名', required=True,min_length=4, max_length=128)
    password = forms.CharField(label="密码", required=True, min_length=4, max_length=10)
    captcha = CaptchaField(label="验证码")
1.3.2 视图逻辑优化
# login/views.py
def login(request):
    # 请求方法为POST提交
    if request.method == 'POST':
        # 修改1: 实例化表单对象
        login_form = LoginForm(request.POST)
        # 修改2: 验证表单数据的合法性
        if login_form.is_valid():
            # 修改3:获取表单填写的数据,数据清洗
            username = login_form.cleaned_data.get('username')
            password = login_form.cleaned_data.get('password')
            user = SiteUser.objects.filter(name=username,
                                           password=password).first()
            if user:
                request.session['is_login'] = True
                request.session['user_id'] = user.id
                request.session['username'] = user.name
                return redirect('/index/')
            else:
                message = "用户名或者密码错误"
                # 修改4: locals()以字典方式返回当前所有的变量
                # eg:{'message':'xxxx', 'login_form':'xxx'}
                return render(request, 'login/login.html', locals())
        else:
            message = "填写的登录信息不合法"
            return render(request, 'login/login.html', locals())
    # 请求方法是GET请求
    login_form = LoginForm()
    return render(request, 'login/login.html', locals())
1.3.3 Template页面优化
# 修改1: 不同的报错,提示不同的信息
            {
    
    % if login_form.captcha.errors %}
                 <div class="alert alert-warning" role="alert">
                     <strong>登录失败!</strong>  验证码不正确
                 </div>
            {
    
    % elif message %}
                 <div class="alert alert-warning" role="alert">
                     <strong>登录失败!</strong> {
    
    {
    
     message }}
                 </div>
            {
    
    % endif %}
            <form action="/login/" method="post" >
                {
    
    % csrf_token %}
                <div class="form-group">
                	# 修改2:
                    <label>{
    
    {
    
     login_form.username.label }}</label>
                    <input type="text" class="form-control" name="username">
                </div>
                <div class="form-group">
                	# 修改3:
                    <label>{
    
    {
    
     login_form.password.label }}</label>
                    <input type="password" class="form-control" name="password">
                    <small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成.</small>
                </div>
                # 修改4: 最重要的,添加验证码表单
                <div class="form-group">
                    <label>{
    
    {
    
     login_form.captcha.label }}</label>
                     {
    
    {
    
     login_form.captcha }}
                </div>
1.3.4 验证是否正确

在这里插入图片描述

1.3.5 git 提交

在这里插入图片描述

2. 邮箱注册

2.1 发送邮件功能测试

2.1.1 如何获取授权码?

获取授权码并开启SMTP: 确保smtp服务开启,才能发送邮件
在这里插入图片描述

2.1.2 配置邮件信息
# loginRegister/settings.py
# mail configure(添加信息如下)
EMAIL_HOST = 'smtp.163.com' # 'smtp.qq.com'
EMAIL_PORT = 25   ##qq接收和发送有不同的端口(此处是发送)
EMAIL_HOST_USER = 'xxxx' # 你的邮箱地址
#授权码一个是发送邮件IMAP/SMTP,接收邮件使用的是pop3
EMAIL_HOST_PASSWORD = 'xxxxxxx' # 不是邮箱的登录密码,而是授权码
EMAIL_USE_SSL = False # 不开启ssl(qq设置为True,163设置为False)
2.1.3 交互式环境中测试发送邮件是否成功?
Terminal输入命令> python manage.py shell
In [1]: from django.core.mail import send_mail
In [2]: from loginRegister.settings import EMAIL_HOST_USER
In [3]: send_mail("测试邮件", "content", EMAIL_HOST_USER,['[email protected]'])

在这里插入图片描述
在这里插入图片描述
git 提交
在这里插入图片描述

2.2基本的注册功能实现

2.2.1 注册表单
# login/forms.py
class RegisterForm(forms.Form):
	username = forms.CharField(label="用户名", required=True, max_length=128)
	password1 = forms.CharField(label="密码", max_length=256, required=True)
	password2 = forms.CharField(label="确认密码", max_length=256, required=True)
	email = forms.EmailField(label="邮箱地址")
	captcha = CaptchaField(label='验证码')

在这里插入图片描述

2.2.2 实现注册视图
  • 如果用户已经登录,则不能注册跳转到首页。
  • 如果是GET请求,返回用户注册的html页面。
  • 如果是POST请求, 先验证提交的数据是否通过,清洗数据。 接下来判断用户名和邮箱是否已经被注册, 将注册的信息存储到数据库,跳转到登录界面。
  • 额外功能: 为了数据的安全性注册时,密码存储到数据库不是明文存储,而是先加密再存储。
# login/views.py
from login.forms import LoginForm, RegisterForm
def register(request):
	# 如果用户已经登录,则不能注册跳转到首页。
	if request.session.get('is_login', None):
		return redirect('/index/')
	# 如果是POST请求
	if request.method == 'POST':
		print(request.POST)
		register_form = RegisterForm(request.POST)
		message = "请检查填写的内容!"
		# 先验证提交的数据是否通过
		if register_form.is_valid():
			# 清洗数据
			username = register_form.cleaned_data.get('username')
			password1 = register_form.cleaned_data.get('password1')
			password2 = register_form.cleaned_data.get('password2')
			email = register_form.cleaned_data.get('email')
			#print(locals())
			# 接下来判断用户名和邮箱是否已经被注册
			same_name_user = SiteUser.objects.filter(name=username)
			#print(same_name_user)
			if same_name_user:
				message = '用户名已经存在'
				return render(request, 'login/register.html', locals())
			same_email_user = SiteUser.objects.filter(email=email)
			if same_email_user:
				message = '该邮箱已经被注册了!'
				return render(request, 'login/register.html', locals())
			# 将注册的信息存储到数据库,跳转到登录界面
			new_user = SiteUser(name=username, password=password1, email=email)
			new_user.save()
			return redirect('/login/')
	# 如果是GET请求,返回用户注册的html页面。
	register_form = RegisterForm()
	return render(request, 'login/register.html', locals())
2.2.3 template的修改
# templates/login/register.html
<h3 style="text-align: center">用户注册</h3>
{
    
    % if register_form.captcha.errors %}
<div class="alert alert-warning" role="alert">
	<strong>注册失败!</strong> 验证码不正确
</div>
{
    
    % elif message %}
<div class="alert alert-warning" role="alert">
	<strong>注册失败!</strong> {
    
    {
    
     message }}
</div>
{
    
    % endif %}

<form action="/register/" method="post">
	{
    
    % csrf_token %}
	<div class="form-group">
		<label>{
    
    {
    
     register_form.username.label }}</label>
		<input type="text" class="form-control" name="username">
	</div>
	<div class="form-group">
		<label>{
    
    {
    
     register_form.email.label }}</label>
		<input type="email" class="form-control" name="email">
	</div>
	<div class="form-group">
		<label>{
    
    {
    
     register_form.password1.label }}</label>
		<input type="password" class="form-control" name="password1">
		<small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成.</small>
	</div>
	<div class="form-group">
		<label>{
    
    {
    
     register_form.password2.label }}</label>
		<input type="password" class="form-control" name="password2">
		<small class="form-text text-muted">密码必须是字母、数字或者特殊符号组成.</small>
	</div>
	<div class="form-group">
		<label>{
    
    {
    
     register_form.captcha.label }}</label>{
    
    {
    
     register_form.captcha }}
	</div>
	<a href="/login/" class="text-success">
		<ins>用户登录</ins>
	</a>
	<button type="submit" class="btn btn-primary float-right">注册</button>
</form>
2.2.4 测试是否成功

在这里插入图片描述
注册并登录user2
在这里插入图片描述

2.3 注册添加密码加密功能

#在 login/utils.py 中编写一个hash函数:
def hash_code(s, salt='mysite'):# 加点盐
	h = hashlib.sha256()
	s += salt
	h.update(s.encode()) # update方法只接收bytes类型
	return h.hexdigest()
#在 login/views.py 中修改login和register视图
def register(request):
	# .....省略部分代码
	new_user = SiteUser(name=username, password=hash_code(password1),
	email=email)
	# .....省略部分代码
def login(request):
	# .....省略部分代码
	user = SiteUser.objects.filter(name=username,password=hash_code(password)).first()
	# .....省略部分代码

在这里插入图片描述
在这里插入图片描述

2.4 邮件注册确认

2.4.1 创建模型
# /login/models.py
class SiteUser(models.Model):
	# .......
	has_confirmed = models.BooleanField(default=False, verbose_name="是否邮箱验证")

class ConfirmString(models.Model):
    code = models.CharField(max_length=256, verbose_name="确认码")
    user = models.OneToOneField('SiteUser', on_delete=models.CASCADE)
    create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    def __str__(self):
        return self.user.name + ":" + self.code
    class Meta:
        ordering = ["-create_time"]
        verbose_name = "确认码"
        verbose_name_plural = "确认码"

数据库模型更改,一定要生成迁移脚本和写入数据库。

python manage.py makemigrations 
python manage.py migrate

在这里插入图片描述
顺便修改一下admin.py文件,方便我们在后台修改和观察数据

# login/admin.py
admin.site.register(ConfirmString)

在这里插入图片描述

2.4.2 修改视图
#login/utils.py
#make_confirm_string() 是创建确认码对象的方法,代码如下:

from django.core.mail import send_mail
from login.models import ConfirmString
from loginRegister import settings

import datetime
def make_confirm_string(user):
    """生成确认码"""
    print("生成确认码.....")
    # 获取当前时间
    now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    code = hash_code(user.name, now)
    print('in code:', code)
    ConfirmString.objects.create(code=code, user=user)
    return code

#send_email(email, code) 方法接收两个参数,分别是注册的邮箱和前面生成的哈希值,代码如下:
def send_email(email, code):
    print('send mail.........')
    subject = '注册确认邮件'
    text_content = '''感谢注册,这里是登录注册系统网站!\
                    如果你看到这条消息,说明你的邮箱服务器不提供HTML链接功能,请联系管理员!'''
    html_content = '''
    <p>感谢注册<a href="http://{}/confirm/?code={}" target=blank>点击验证</a>,\
    这里是登录注册系统网站!</p>
    <p>请点击站点链接完成注册确认!</p>
    <p>此链接有效期为{
    
    }天!</p>
    '''.format('127.0.0.1:8000', code, settings.CONFIRM_DAYS)

    send_mail(subject, text_content,settings.EMAIL_HOST_USER, [email, ], 
                html_message=html_content)
##修改的注册视图函数  login/views.py
def register(request):
    # 如果用户已经登录,则不能注册跳转到首页。
    if request.session.get('is_login', None):
        return redirect('/index/')
    # 如果是POST请求
    if request.method == 'POST':
        print(request.POST)
        register_form = RegisterForm(request.POST)
        message = "请检查填写的内容!"
        # 先验证提交的数据是否通过
        if register_form.is_valid():
            # 清洗数据
            username = register_form.cleaned_data.get('username')
            password1 = register_form.cleaned_data.get('password1')
            password2 = register_form.cleaned_data.get('password2')
            email = register_form.cleaned_data.get('email')

            # 接下来判断用户名和邮箱是否已经被注册
            same_name_user = SiteUser.objects.filter(name=username)
            print(same_name_user)
            if same_name_user:
                message = '用户名已经存在'
                return render(request, 'login/register.html', locals())
            same_email_user = SiteUser.objects.filter(email=email)
            if same_email_user:
                message = '该邮箱已经被注册了!'
                return render(request, 'login/register.html', locals())
            try:
                # 将注册的信息存储到数据库,跳转到登录界面
                new_user = SiteUser(name=username, password=hash_code(password1), email=email)
                new_user.save()
                # 生成确认码并发送确认邮件
                code = make_confirm_string(new_user)
                print('code:', code)
                send_email(email, code)
                message = '请前往邮箱进行确认!'
            except Exception:
                new_user.delete()
                message = '发送邮件失败!'
                #print(Exception)
                return render(request, 'login/register.html', locals())
            else:
                return  redirect('/login/')
    # 如果是GET请求,返回用户注册的html页面。
    register_form = RegisterForm()
    return render(request, 'login/register.html', locals())
2.4.3 邮件相关的设置

最后的有效期天数为设置在settings中的 CONFIRM_DAYS 。下面是邮件相关的settings配置:

# 注册有效期天数
CONFIRM_DAYS = 3

在这里插入图片描述

2.4.4 测试

注册一个用户,判断是否能收到确认邮件。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.4.5 处理邮件确认请求

1.在login子应用的 urls.py 中添加一条url

path('confirm/', views.user_confirm,name='confirm'),

在这里插入图片描述
其次,在 login/views.py 中添加一个 user_confirm 视图。

- 	获取确认码信息
	数据库中是否有该确认码,如果没有, 返回说是无效的请求
	数据库中是否有该确认码,如果有, 判断是否过期? 如果过期,删除用户信息,否则更新用户信息。
# login/views.py
def user_confirm(request):
    code = request.GET.get('code', None)
    message = ''
    try:
        confirm = ConfirmString.objects.get(code=code)
    except:
        message = '无效的确认请求!'
        return render(request, 'login/confirm.html', locals())

    create_time = confirm.create_time
    now = datetime.now()
    print(now, create_time, create_time + timedelta(settings.CONFIRM_DAYS))
    if now > create_time + timedelta(settings.CONFIRM_DAYS):
        confirm.user.delete()
        message = '您的邮件已经过期!请重新注册!'
    else:
        confirm.user.has_confirmed = True    ##更新的字段要和数据库存储的字段保持一致,不是is_confirmed,而是has_confirmed
        confirm.user.save()
        confirm.delete()
        message = '感谢确认,请使用账户登录!'
    return render(request, 'login/confirm.html', locals())

设置confirm.html 页面
需要一个 confirm.html 页面,我们将它创建在 /login/templates/login/ 下面:页面中通过JS代码,设置2秒后自动跳转到登录页面,可根据自己的需要去除或者美化。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 style="margin-left: 100px;">{
    
    {
    
     message }}</h1>
<script>
window.setTimeout("window.location='/login/'",2000);
</script>
</body>
</html>

在这里插入图片描述

2.4.6 修改登录规则

既然未进行邮件确认的用户不能登录,那么我们就必须修改登录规则,如下所示:

def login(request):
    if request.method == 'POST':
        login_form = LoginForm(request.POST)
        if login_form.is_valid():
            username = login_form.cleaned_data.get('username')
            password = login_form.cleaned_data.get('password')
            print('------', password)
            users = SiteUser.objects.filter(name=username).first()
            print('-----users:', users.password)
            print(hash_code(password))

            user = SiteUser.objects.filter(name=username, password=hash_code(password)).first()
            print('------------', user)
            if user:
                if not user.has_confirmed:   ##登录规则
                    message = '该用户还未经过邮件确认!'
                    return render(request, 'login/login.html', locals())
                request.session['is_login'] = True
                request.session['user_id'] = user.id
                request.session['username'] = user.name
                return redirect('/index/')
            else:
                message = "用户名或者密码错误"
                return render(request, 'login/login.html', locals())
        else:
            message = "填写的登录信息不合法"
            return render(request, 'login/login.html', locals())
    login_form = LoginForm()
    return render(request, 'login/login.html', locals())
2.4.7 测试

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

3. git 提交

git remote add origin https://gitee.com/xxxx/xxxx'
t  ##绑定邮箱
git add *
git commit -m "可重用注册登录系统"
git log
git push  ## 上传

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45777669/article/details/115004358