用户注册功能实现

首页和登录页面的配置

导入index.html,建立static目录,在settings.py中配置静态文件static所在目录,配置urls.py,处理index页面

同理配置login.html页面

setting中配置静态文件目录

# 说明静态文件放在哪个目录

STATIC_URL = '/static/'

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "static"),
)

在static中加入静态文件:css image js 等

配置点击登录之后的跳转页面url

 # 基于类方法实现登录,这里是调用它的方法
 url('^login/$', LoginView.as_view(), name="login"),

用户登录

基于函数来完成登录

配置users/views.py中login函数,urls.py中login 的URL指向该函数

login.html中添加,这是Django的一种安全机制,防止一些攻击对服务器造成的压力

{% csrf_token %}

user/view.py

Request为post时,取到前端页面的用户名和密码,用Django自带的authenticate方法进行验证,在user/views.py中import该方法,如果用户名、密码正确,直接调用login方法,进行登录,登陆成功后跳转到Index.html页面中,在index页面中根据登录的user的is_authenticated方法进行判断,从而显示不同页面。

自定义authentic方法,实现邮箱、用户名均可登录

# 当我们配置url被这个view处理时,自动传入request对象.
def user_login(request):
    # 前端向后端发送的请求方式: get 或post

    # 登录提交表单为post
    if request.method == "POST":
        # 取不到时为空,username,password为前端页面name值
        user_name = request.POST.get("username", "")
        pass_word = request.POST.get("password", "")

        # 成功返回user对象,失败返回null
        user = authenticate(username=user_name, password=pass_word)

        # 如果不是null说明验证成功
        if user is not None:
            # login_in 两参数:request, user
            # 实际是对request写了一部分东西进去,然后在render的时候:
            # request是要render回去的。这些信息也就随着返回浏览器。完成登录
            login(request, user)
            # 跳转到首页 user request会被带回到首页
            return render(request, "index.html")
        # 没有成功说明里面的值是None,并再次跳转回主页面
        else:
            return render(request, "login.html", {"msg": "用户名或密码错误! "})

    # 获取登录页面为get
    elif request.method == "GET":
        # render就是渲染html返回用户
        # render三变量: request 模板名称 一个字典写明传给前端的值
        return render(request, "login.html", {})

如果密码验证失败,将错误信息{"msg": "用户名或密码错误! "}返回到前台页面

{{ msg }}

配置可以通过邮箱登录

user/views.py中定义一个类CustomBackend继承ModelBackend类

from .models import UserProfile

# 并集运算
from django.db.models import Q

from django.contrib.auth.backends import ModelBackend

# 实现用户名邮箱均可登录
# 继承ModelBackend类,因为它有方法authenticate,可点进源码查看

class CustomBackend(ModelBackend):
    def authenticate(self, username=None, password=None, **kwargs):
        try:
            # 不希望用户存在两个,get只能有一个。两个是get失败的一种原因 Q为使用并集查询
            user = UserProfile.objects.get(
                Q(username=username) | Q(email=username))
            # django的后台中密码加密:所以不能password==password
            # UserProfile继承的AbstractUser中有def check_password(self,
            # raw_password):
            if user.check_password(password):
                return user
        except Exception as e:
            return None

在settings.py文件中重载变量BACKENDS指定views中的类

# 设置邮箱和用户名均可登录
AUTHENTICATION_BACKENDS = (
    'users.views.CustomBackend',

)

也可以使用基于类的方式实现登录,views中定义LoginView类,继承View类,并在urls.py中进行更改,这种方法更为简便

基于类的方式登录

user/view.py

# 基于类实现需要继承的view
from django.views.generic.base import View

class LoginView(View):
    # 直接调用get方法免去判断
    def get(self, request):
        # render就是渲染html返回用户
        # render三变量: request 模板名称 一个字典写明传给前端的值
        redirect_url = request.GET.get('next', '')
        return render(request, "login.html", {
            "redirect_url": redirect_url
        })

    def post(self, request):
        # 类实例化需要一个字典参数dict:request.POST就是一个QueryDict所以直接传入
        # POST中的usernamepassword,会对应到form中
        login_form = LoginForm(request.POST)

        # is_valid判断我们字段是否有错执行我们原有逻辑,验证失败跳回login页面
        if login_form.is_valid():
            # 取不到时为空,username,password为前端页面name值
            user_name = request.POST.get("username", "")
            pass_word = request.POST.get("password", "")

            # 成功返回user对象,失败返回null
            user = authenticate(username=user_name, password=pass_word)

            # 如果不是null说明验证成功
            if user is not None:
                # 只有当用户**时才给登录
                if user.is_active:
                    # login_in 两参数:request, user
                    # 实际是对request写了一部分东西进去,然后在render的时候:
                    # request是要render回去的。这些信息也就随着返回浏览器。完成登录
                    login(request, user)
                    # 跳转到首页 user request会被带回到首页
                    # 跳转到首页 user request会被带回到首页
                    # 增加重定向回原网页。
                    redirect_url = request.POST.get('next', '')
                    if redirect_url:
                        return HttpResponseRedirect(redirect_url)
                    return HttpResponseRedirect(reverse("index"))
                # 即用户未**跳转登录,提示未**
                else:
                    return render(
                        request, "login.html", {
                            "msg": "用户名未**! 请前往邮箱进行**"})
            # 仅当用户真的密码出错时
            else:
                return render(request, "login.html", {"msg": "用户名或密码错误!"})
        # 验证不成功跳回登录页面
        # 没有成功说明里面的值是None,并再次跳转回主页面
        else:
            return render(
                request, "login.html", {
                    "login_form": login_form})

相应改变url.py

from users.views import LoginView

# 基于类方法实现登录,这里是调用它的方法
url('^login/$', LoginView.as_view(), name="login"),

用form实现登录

新建user/forms.py

保证form中的用户输入信息名称与前端html文件一致,这样才可以进行form的验证

# 引入Django表单
from  django import forms

# 登录表单验证
class LoginForm(forms.Form):
    # 用户名密码不能为空
    username = forms.CharField(required=True)
    # 密码不能小于5位
    password = forms.CharField(required=True, min_length=5)

将form的逻辑加入到view.py文件中,处理数据login_form、login_form.is_valid()用来验证

将form验证后产生的信息,显示在前端页面

{% if login_form.username.errors %} errorput{% endif %}

{% if login_form.password.errors %} errorput{% endif %}

session和cookie自动登录机制

http的请求本身很单一,如下:

用户注册功能实现

因而我们引出了cookie:实质上就是浏览器的一种本地存储机制

Cookie通过在客户端记录信息确定用户身份,客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。

用户注册功能实现

但是cookie也就有了一些安全问题,比如把用户信息存在本地,容易被窃取。因而服务器在返回cookie时用到了一种session(存储于服务器端的)的机制,生成一段随机字符串发送给用户,用户存储在cookie当中,在下一次请求时,用户把session值带回到服务器,服务器通过session id 在数据库中查询,看是哪个用户,这样就可以对该用户进行标记。如此,Django利用cookie和session机制实现了自动登录

解决cookie放在本地不安全的问题(session),用户在第一次请求后,浏览器回复的id既可以是用户的user id,也可以一段任意的字符串,我们把它叫做session id根据用户名和密码,服务器会采用自己的规则生成session id。这个session id保存在本地cookie。浏览器请求服务器会携带。Django的默认表中的session表就记录了用户登录时,后端我们Django为用户生成的sessionid。通过session id 用户访问任何一个页面都会携带,服务器就会认识。Django的APP之一session会拦截我们每次的request请求,在request中找到session id,然后去数据表中进行查询,然后通过session key 去找到session data。此时直接为我们取出了user。

用户注册

注册页面templates/register.html

urls.py文件中定义

from users.views import RegisterView

# 注册url
url("^register/", RegisterView.as_view(), name="register"),

在html文件中使用url指定跳转

{% url 'register' %}

修改静态文件的路径
GitHub上下载验证码库,实现验证码 captcha,添加到settings.py的INSTALLED_APPS中,配置URL

from django.conf.urls import url, include
# 验证码 url配置
url(r'^captcha/', include('captcha.urls')),

Makemigrations & migrate生成对应表。

user/forms.py中定义form

# 引入验证码field
from captcha.fields import CaptchaField

# 验证码form & 注册表单form
class RegisterForm(forms.Form):
    # 此处email与前端name需保持一致。
    email = forms.EmailField(required=True)
    # 密码不能小于5位
    password = forms.CharField(required=True, min_length=5)
    # 应用验证码 自定义错误输出key必须与异常一样
    captcha = CaptchaField(error_messages={"invalid": u"验证码错误"})

user/views.py

# 进行密码加密
from django.contrib.auth.hashers import make_password
from .forms import LoginForm, RegisterForm

# 发送邮件
from utils.email_send import send_register_eamil

# 注册功能的view
class RegisterView(View):
    # get方法直接返回页面
    def get(self, request):
        # 添加验证码
        register_form = RegisterForm()
        return render(
            request, "register.html", {
                'register_form': register_form})

    def post(self, request):
        # 实例化form
        register_form = RegisterForm(request.POST)
        if register_form.is_valid():
            # 这里注册时前端的name为email
            user_name = request.POST.get("email", "")
            if UserProfile.objects.filter(email=user_name):
                return render(
                    request, "register.html", {
                        "register_form": register_form, "msg": "用户已经存在"})
            pass_word = request.POST.get("password", "")

            # 实例化一个user_profile对象,将前台值存入
            user_profile = UserProfile()
            user_profile.username = user_name
            user_profile.email = user_name

            # 默认**状态为false
            user_profile.is_active = False

            # 加密password进行保存
            user_profile.password = make_password(pass_word)
            user_profile.save()

            # 写入欢迎注册消息
            user_message = UserMessage()
            user_message.user = user_profile.id
            user_message.message = "欢迎注册VictoriaJiang云教育平台!!"
            user_message.save()

            # 发送注册**邮件
            send_register_eamil(user_name, "register")

            # 跳转到登录页面
            return render(request, "login.html", )
        # 注册邮箱form验证失败
        else:
            return render(
                request, "register.html", {
                    "register_form": register_form})

 在html文件中,传入验证码

 {{ register_form.captcha }}

注册邮件发送

apps/utils/send_email.py中定义发送邮件的函数(其他地方也会用到,放在这里可以引用)

邮箱验证码功能如何完成:在用户的链接中添加随机字符串,字符串由后台生成,即EmailVerifyRecord中定义的code,用户在点击这个URL链接时,我们把这个code取出来,再回来查询数据库,查看这个随机字符串是否存在,如果存在,则**,否则报错。

from  users.models import EmailVerifyRecord
# 导入Django自带的邮件模块
from django.core.mail import send_mail,EmailMessage
# 导入setting中发送邮件的配置
from Mxoline2.settings import EMAIL_FROM
# 发送html格式的邮件:
from django.template import loader


# 生成随机字符串
def random_str(random_length=8):
    str = ''
    # 生成字符串的可选字符串
    chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
    length = len(chars) - 1
    random = Random()
    for i in range(random_length):
        str += chars[random.randint(0, length)]
    return str


# 发送注册邮件
def send_register_eamil(email, send_type="register"):
    # 发送之前先保存到数据库,到时候查询链接是否存在

    # 实例化一个EmailVerifyRecord对象
    email_record = EmailVerifyRecord()
    # 生成随机的code放入链接
    if send_type == "update_email":
        code = random_str(4)
    else:
        code = random_str(16)
    email_record.code = code
    email_record.email = email
    email_record.send_type = send_type

    email_record.save()

    # 定义邮件内容:
    email_title = ""
    email_body = ""

    if send_type == "register":
        email_title = "VictoriaJiang云教育平台 注册**链接"

        email_body = loader.render_to_string(
                    "email_register.html",  # 需要渲染的html模板
                    {
        "active_code":code  # 参数
        }
        )

        msg = EmailMessage(email_title, email_body, EMAIL_FROM, [email])
        msg.content_subtype = "html"
        send_status = msg.send()
        # 使用Django内置函数完成邮件发送。四个参数:主题,邮件内容,从哪里发,接受者list
        # send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])

        # 如果发送成功
        if send_status:
            pass
    elif send_type == "forget":
        email_title = "VictoriaJiang云教育平台 找回密码链接"
        email_body = loader.render_to_string(
            "email_forget.html",  # 需要渲染的html模板
            {
                "active_code": code  # 参数
            }
        )
        msg = EmailMessage(email_title, email_body, EMAIL_FROM, [email])
        msg.content_subtype = "html"
        send_status = msg.send()
    elif send_type == "update_email":
        email_title = "VictoriaJiang云教育平台 修改邮箱验证码"
        email_body = loader.render_to_string(
            "email_update_email.html",  # 需要渲染的html模板
            {
                "active_code": code  # 参数
            }
        )
        msg = EmailMessage(email_title, email_body, EMAIL_FROM, [email])
        msg.content_subtype = "html"
        send_status = msg.send()

setting中配置发送邮件参数

# 发送邮件的setting设置

EMAIL_HOST = "smtp.163.com"
EMAIL_PORT = 25
EMAIL_HOST_USER = "[email protected]"
EMAIL_HOST_PASSWORD = "xxxxxxxxxx"
EMAIL_USE_TLS= True
EMAIL_FROM = "[email protected]"

邮箱**

urls.py

from users.views import LoginView, RegisterView, ActiveUserView

# **用户
url(r'^active/(?P<active_code>.*)/$',ActiveUserView.as_view(), name= "user_active"),

user/view.py

# **用户的view
class ActiveUserView(View):
    def get(self, request, active_code):
        # 查询箱验证记录是否存在
        all_record = EmailVerifyRecord.objects.filter(code=active_code)
        # 如果不为空也就是有用户
        active_form = ActiveForm(request.GET)
        if all_record:
            for record in all_record:
                # 获取到对应的邮箱
                email = record.email
                # 查找到邮箱对应的user
                user = UserProfile.objects.get(email=email)
                user.is_active = True
                user.save()
                # **成功跳转到登录页面
                return render(request, "login.html", )
        # 自己瞎输的验证码
        else:
            return render(
                request, "register.html", {
                    "msg": "您的**链接无效", "active_form": active_form})

找回密码

忘记密码

定义url

# 忘记密码
url(r'^forget/$', ForgetPwdView.as_view(), name="forget_pwd"),

新建forgetpsw.html

user/forms.py 定义form

# 忘记密码实现
class ForgetForm(forms.Form):
    # 此处email与前端name需保持一致。
    email = forms.EmailField(required=True)
    # 应用验证码 自定义错误输出key必须与异常一样
    captcha = CaptchaField(error_messages={"invalid": u"验证码错误"})

user/view.py

class ForgetPwdView(View):
    # get方法直接返回页面
    def get(self, request):
        forget_from = ForgetForm()
        return render(request, "forgetpwd.html", {"forget_from": forget_from})
        # post方法实现

    def post(self, request):
        forget_form = ForgetForm(request.POST)
        # form验证合法情况下取出email
        if forget_form.is_valid():
            email = request.POST.get("email", "")
            # 发送找回密码邮件
            send_register_eamil(email, "forget")
            # 发送完毕返回登录页面并显示发送邮件成功。
            return render(request, "login.html", {"msg": "重置密码邮件已发送,请注意查收"})
        # 如果表单验证失败也就是他验证码输错等。
        else:
            return render(
                request, "forgetpwd.html", {
                    "forget_from": forget_form})

在send_email.py文件中定义forget类型的发送

重置密码

urls.py

# 重置密码urlc :用来接收来自邮箱的重置链
url(r'^reset/(?P<active_code>.*)/$', ResetView.as_view(), name="reset_pwd"),

views.py

# 重置密码的view
class ResetView(View):
    def get(self, request, active_code):
        # 查询邮箱验证记录是否存在
        all_record = EmailVerifyRecord.objects.filter(code=active_code)
        # 如果不为空也就是有用户
        active_form = ActiveForm(request.GET)
        if all_record:
            for record in all_record:
                # 获取到对应的邮箱
                email = record.email
                # 将email传回来
                return render(request, "password_reset.html", {"email": email})
        # 自己瞎输的验证码
        else:
            return render(
                request, "forgetpwd.html", {
                    "msg": "您的重置密码链接无效,请重新请求", "active_form": active_form})

forms.py

# 重置密码form实现
class ModifyPwdForm(forms.Form):
    # 密码不能小于5位
    password1 = forms.CharField(required=True, min_length=5)
    # 密码不能小于5位
    password2 = forms.CharField(required=True, min_length=5)

修改密码

view.py:

# 改变密码的view
class ModifyPwdView(View):
    def post(self, request):
        modiypwd_form = ModifyPwdForm(request.POST)
        if modiypwd_form.is_valid():
            pwd1 = request.POST.get("password1", "")
            pwd2 = request.POST.get("password2", "")
            email = request.POST.get("email", "")
            # 如果两次密码不相等,返回错误信息
            if pwd1 != pwd2:
                return render(
                    request, "password_reset.html", {
                        "email": email, "msg": "密码不一致"})
            # 如果密码一致
            user = UserProfile.objects.get(email=email)
            # 加密成密文
            user.password = make_password(pwd2)
            # save保存到数据库
            user.save()
            return render(request, "login.html", {"msg": "密码修改成功,请登录"})
        # 验证失败说明密码位数不够。
        else:
            email = request.POST.get("email", "")
            return render(
                request, "password_reset.html", {
                    "email": email, "modiypwd_form": modiypwd_form})

urls.py

# 修改密码url; 用于passwordreset页面提交表
url(r'^modify_pwd/$', ModifyPwdView.as_view(), name="modify_pwd"),