美文网首页
注册功能实现 -- 8.用户登录登出功能

注册功能实现 -- 8.用户登录登出功能

作者: 爱修仙的道友 | 来源:发表于2019-03-01 19:00 被阅读0次

    一、用户登录功能实现

    1.分析

    业务处理流程:

    • 判断用户输入的账号是否为空
    • 判断用户输入的密码是否为空,格式是否正确
    • 判断用户输入的账号与密码是否正确

    请求方法POST

    url定义/users/login/

    请求参数:url路径参数

    参数 类型 前端是否必须传 描述
    user_account 字符串 用户输入的账号可以是手机号也可以是用户名
    password 字符串 用户输入的密码
    remember_me 字符串 用户输入的“是否记住我”

    注:由于是post请求,在向后端发起请求时,需要附带csrf token

    2.后端代码实现

    # views.py
    import json
    import logging
    
    from django.views import View
    from django.shortcuts import render
    from django.contrib.auth import login
    
    from .models import Users
    from .forms import RegisterForms, LoginForm
    from utils.res_code.rescode import Code, error_map
    from utils.json_translate.json_fun import to_json_data
    
    
    logger = logging.getLogger('django')
    
    
    
    class LoginView(View):
        """
    
        """
        def get(self, request):
            """
            渲染登陆界面
            :param request:
            :return:
            """
            return render(request, 'users/login.html')
    
        def post(self, request):
            """
            1.创建一个类视图  LoginView
            2.明确请求方式:
                -- 请求类型: Ajax post
                -- 传参方式: 请求体  user_account  password  remember_me
                -- url定义: '/users/login/'
            3.获取前端参数:
                -- json_data = request.body
            4.业务处理:
                -- 1.是否需要校验              -- 需要  form表单校验
                -- 2.是否需要储存以及储存方式   -- 不需要
                -- 3.其他:
                    -- form表单登陆
            5.返回前端数据:
                -- to_json_data(errorno= , errormsg= , data= )
            :param request:
            :return:
            """
            json_data = request.body
    
            if not json_data:
                return to_json_data(errno=Code.PARAMERR, errmsg=error_map[Code.PARAMERR])
    
            dict_data = json.loads(json_data.decode('utf-8'))
    
            form = LoginForm(data=dict_data, request=request)
    
            if form.is_valid():
                return to_json_data(errno=Code.OK, errmsg='恭喜你登陆成功')
            else:
                # 定义一个接收错误信息的列表
                form_errormsg_list = []
    
                for msg in form.errors.get_json_data().values():
                    form_errormsg_list.append(msg[0].get('message'))
                form_errormsg = '/'.join(form_errormsg_list)
    
                return to_json_data(errno=Code.PARAMERR, errmsg=form_errormsg)
    
    # urls.py
    
    from django.urls import path
    from . import views
    
    app_name = 'users'
    
    urlpatterns = [
        path('login/', views.LoginView.as_view(), name='user_login'),
        path('register/', views.RegisterView.as_view(), name='user_register'),
    ]
    
    # forms.py
    import re
    
    from django import forms
    from django.db.models import Q
    from django.contrib.auth import login
    from django_redis import get_redis_connection
    from django.core.validators import RegexValidator
    
    from .models import Users
    from .constants import USER_SESSION_EXPIRES
    from verifications.constants import SMS_CODE_NUMS
    
    password_Validator=RegexValidator(r'^\w{6,20}$', '密码格式不正确,请重新输入')
    
    class LoginForm(forms.Form):
        """
        获取参数
            -- user_account(username/mobile) password remember_me
        业务逻辑
            -- user_account password 联合登陆
            -- 实现自动登陆(session)
        """
        user_account = forms.CharField()
        password = forms.CharField(max_length=20, min_length=6, label='密码', validators=[password_Validator,],
                                   error_messages={
                                       'max_length':'密码长度要小于20',
                                       'min_length':'密码长度要大于6',
                                       'required':'密码不能为空',
                                   })
        remember_me = forms.BooleanField(required=False)
    
        # 本项目在form表单里实现用户登陆,须将login的request参数传入
        # 需要__init__ 导入视图当中的request post(self, request
        def __init__(self, *args, **kwargs):  # *args接收request  **kwargs接收request=request  *args 没有用到也需要加 固定用法
            # 定义实例属性
            self.request = kwargs.pop('request', None)   # 字典方法pop() 有key则返回 value 无则返回 None
            # 继承父类
            super().__init__(*args, **kwargs)
    
    
        def clean_user_account(self):
            cleaned_user_account = self.cleaned_data.get('user_account')
            if not cleaned_user_account:
                raise forms.ValidationError('用户账号名不能为空')
            if not re.match(r'^\w{5,20}$', cleaned_user_account) and not re.match(r'^1[3-9]\d{9}$', cleaned_user_account):
                raise forms.ValidationError('用户账号不存在')
    
            return cleaned_user_account
    
        def clean(self):
            cleaned_data = super().clean()
            cleaned_user_account = cleaned_data.get('user_account')
            cleaned_password = cleaned_data.get('password')
            cleaned_remember_me = cleaned_data.get('remember_me')
    
            try:
                user = Users.objects.filter(Q(username=cleaned_user_account)|Q(mobile=cleaned_user_account))
            except Exception as e:
                raise forms.ValidationError('内部错误')
    
            if user:
                user = user.first()
                if user.check_password(cleaned_password):
                    if cleaned_remember_me:
                        self.request.session.set_expiry(USER_SESSION_EXPIRES)
                    else:
                        self.request.session.set_expiry(0) # None--默认两周 0--默认关闭浏览器就删除
                    # 登陆  {% if user.is_authenticated %} 此处user传入,可以将其渲染到模板中  因为它:'django.contrib.auth.context_processors.auth',
                    login(self.request, user)
                else:
                    raise forms.ValidationError('密码输入不正确')
            else:
                raise forms.ValidationError('用户账号不存在')
    
    # 在users目录下的constants.py文件中定义如下常量:
    
    # 用户session信息过期时间,单位秒,这是设置为5天
    USER_SESSION_EXPIRES = 5 * 24 * 60 * 60
    

    3.前端代码实现

    # 在static/js/users中创建一个login.js文件
    
    $(function () {
      let $login = $('.form-contain');  // 获取登录表单元素
    
      // for test
      // console.log(document.referrer);   // 将referrer url 打印到终端
    
      // 登录逻辑
      $login.submit(function (e) {
        // 阻止默认提交操作
        e.preventDefault();
    
        // 获取用户输入的账号信息
        let sUserAccount = $("input[name=telephone]").val();  // 获取用户输入的用户名或者手机号
        // 判断用户输入的账号信息是否为空
        if (sUserAccount === "") {
          message.showError('用户账号不能为空');
          return
        }
        // 判断输入手机号格式或者用户名格式是否正确
        if (!(/^1[3-9]\d{9}$/).test(sUserAccount) && !(/^\w{5,20}$/).test(sUserAccount)) {
          message.showError('请输入合法的用户账号:5-20个字符的用户名或者11位手机号');
          return
        }
    
        // 获取用户输入的密码
        let sPassword = $("input[name=password]").val();  // 获取用户输入的密码
        // 判断用户输入的密码是否为空
        if (!sPassword) {
          message.showError('密码不能为空');
          return
        }
        // 判断用户输入的密码是否为6-20位
        if (sPassword.length < 6 || sPassword.length > 20) {
          message.showError('密码的长度需在6~20位以内');
          return
        }
    
        // 获取用户是否勾许"记住我",勾许为true,不勾许为false
        let bStatus = $("input[type='checkbox']").is(":checked");  // 获取用户是否选择记住我,勾上代表true,没勾上代码false
    
        // 发起登录请求
        // 创建请求参数
        let SdataParams = {
          "user_account": sUserAccount,
          "password": sPassword,
          "remember_me": bStatus
        };
    
        // 创建ajax请求
        $.ajax({
          // 请求地址
          url: "/users/login/",  // url尾部需要添加/
          // 请求方式
          type: "POST",
          data: JSON.stringify(SdataParams),
          // 请求内容的数据类型(前端发给后端的格式)
          contentType: "application/json; charset=utf-8",
          // 响应数据的格式(后端返回给前端的格式)
          dataType: "json"
        })
          .done(function (res) {
            if (res.errno === "200") {
              // 注册成功
              message.showSuccess('恭喜你,登录成功!');
              setTimeout(function () {
                // 注册成功之后重定向到打开登录页面之前的页面
                window.location.href = document.referrer;
              }, 1000)
            } else {
              // 登录失败,打印错误信息
              message.showError(res.errmsg);
            }
          })
          .fail(function(){
            message.showError('服务器超时,请重试!');
          });
      });
    
      // get cookie using jQuery
      function getCookie(name) {
        let cookieValue = null;
        if (document.cookie && document.cookie !== '') {
          let cookies = document.cookie.split(';');
          for (let i = 0; i < cookies.length; i++) {
            let cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
              cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
              break;
            }
          }
        }
        return cookieValue;
      }
    
      function csrfSafeMethod(method) {
        // these HTTP methods do not require CSRF protection
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
      }
    
      // Setting the token on the AJAX request
      $.ajaxSetup({
        beforeSend: function (xhr, settings) {
          if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
          }
        }
      });
    
    });
    
    // login.html
    {% block script %}
      <script src="{% static 'js/users/login.js' %}"></script>
      <script src="{% static 'js/base/message.js' %}"></script>
    {% endblock %}
    

    二、用户登出功能实现

    1.分析

    请求方法GET

    url定义/users/logout/

    实现:调用Django自带的logout(request)函数即可

    2.后端代码实现

    # 在users目录下的views.py文件中定义如下类:
    
    class LogoutView(View):
        """
        """
        def get(self, request):
            logout(request)
    
            return redirect(reverse("users:user_login"))
    
    # 在users目录下的urls.py文件中定义如下路由:
    
    from django.urls import path
    from . import views
    
    app_name = 'users'
    
    urlpatterns = [
        path('logout/', views.LogoutView.as_view(), name='user_logout'),
    
    ]
    

    3.前端代码实现

    # 在templates/base下的base.html中修改如下代码:
    
    <!-- login start -->
        <div class="login-box">
          <!-- is_authenticated request -->
          {% if user.is_authenticated %}
            <div class="author">
              <i class="PyWhich py-user"></i>
              <span>{{ user.username }}</span>
              <ul class="author-menu">
    
                {% if user.is_staff %}
                  <li><a href="#">后台管理</a></li>
                {% endif %}
    
                <li><a href="{% url 'users:user_logout' %}">退出登录</a></li>
              </ul>
            </div>
          {% else %}
            <div>
              <i class="PyWhich py-user"></i>
              <span>
                <a href="{% url 'users:user_login' %}" class="login">登录</a> / <a href="{% url 'users:user_register' %}" class="reg">注册</a>
              </span>
            </div>
          {% endif %}
          <!-- login end -->
        </div>
    

    相关文章

      网友评论

          本文标题:注册功能实现 -- 8.用户登录登出功能

          本文链接:https://www.haomeiwen.com/subject/xovduqtx.html