购物车案例——登录

登录功能

登录流程

vue前端

  1. 在页面中点击登录,会触发对应的函数。可以在App组件中找到如下代码:
<li v-if="!this.$store.getters.getUser">
	<el-button type="text" @click="login">登录</el-button>
	<span class="sep">|</span>
	<el-button type="text" @click="register = true">注册</el-button>
</li>

//触发的login函数
login() {
      // 点击登录按钮, 通过更改vuex的showLogin值显示登录组件
      this.setShowLogin(true); //从vuex中映射过来的方法
    },

通过分析可知,login方法更改了vuex中的showLogin的值,从而控制登录组件的显示。

  1. MyLogin.vue组件
  • v-model获取用户信息
  • 点击登录按钮,触发Login方法
  • 在Login方法中,使用axios的post方法提交参数,并接收响应结果
// 点击登录触发
Login() {
  // 通过element自定义表单校验规则,校验用户输入的用户信息
  this.$refs["ruleForm"].validate(valid => {
    //如果通过校验开始登录
    if (valid) {
      // 发送ajax
      this.$axios.post("/users/login/", {
          user: this.LoginUser.name,
          pwd: this.LoginUser.pass,
        })
        .then(res => {
          console.log("@@登录的响应:", res)
          // 200代表登录成功,其他的均为失败
          if (res.data.code == 200) {
            // res.data为后端响应的json
            // 隐藏登录组件
            this.isLogin = false;
            // 登录信息存到本地缓存
            let user = JSON.stringify(res.data.user);
            //要求后台返回的数据
            //{
            //     "code":200, 
            //     'msg': "欢迎user",
            //     "user":{
            //       userName:"xxx",
            //     },
            //}
            // 前端存储用户信息,表示登录成功
            localStorage.setItem("user", user);
            // sessionStorage.setItem("")
            // 登录信息存到vuex,控制页面欢迎信息
            this.setUser(res.data.user);
            // 弹出通知框提示登录成功信息
            this.notifySucceed(res.data.msg);
          } else {//响应不是200
            // 清空输入框的校验状态
            this.$refs["ruleForm"].resetFields();
            // 弹出通知框提示登录失败信息
            this.notifyError(res.data.msg);
          }
        })
        .catch(err => {
          console.log(err)
          return Promise.reject(err);
        });
    } else {//未通过用户校验
      return false;
    }
  });
}

django后端

  1. 路由匹配
# /users/login/
path("login/", Login.as_view()),
  1. 视图接口
from django.contrib.auth.hashers import check_password 
from django.contrib.auth import authenticate
from rest_framework.views import APIView
from rest_framework.response import Response
from user.models import *

class Login(APIView):
    def post(self, request):
        # 1. 获取前端数据
        # 前端发送post 数据{user:xxx, pwd:xxx} 
        username = request.data.get("user")
        password = request.data.get("pwd")

        #2. 验证用户
        # user = authenticate(username=username,password=password)
        try:
            user = User.objects.get(username=username)
        except:
            return Response({"code":204,"msg":"用户不存在!"})

        # 用户存在,验证密码
        if check_password(password, user.password):
            #密码正确
            #会话保持,django后端只需要简单设置session,返回的响应会带着sessionid---->Vue代理处理cookie存储
            request.session["username"] = username
            
            return Response({"code":200, "msg":"欢迎%s"%username, 'user':{"userName":username, 'uid':user.id}})
        # 验证未通过
        res = Response({"code":204, "msg":"用户名或密码不正确!"})
        return res

JWT认证

json web token

形式:一长串字符串,只不过字符串有特殊的规则

完整形式如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjpbeyJ1c2VybmFtZSI6InpoYW5nc2FuIn1dLCJpYXQiOjE2NTM0NDMyNzIsImV4cCI6MTY1MzkyNjM5OSwiYXVkIjoiIiwiaXNzIjoiIiwic3ViIjoiIn0.UIlYy4D2VrWJZhkc6KCBHbD0es4Knm-o7HrO6XVoIaQ

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

eyJkYXRhIjpbeyJ1c2VybmFtZSI6InpoYW5nc2FuIn1dLCJpYXQiOjE2NTM0NDMyNzIsImV4cCI6MTY1MzkyNjM5OSwiYXVkIjoiIiwiaXNzIjoiIiwic3ViIjoiIn0

UIlYy4D2VrWJZhkc6KCBHbD0es4Knm-o7HrO6XVoIaQ

第一部分

{"alg":"HS256","typ":"JWT"}

# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

第二部分

{
    "id":"编号",
    "username":"元宝",
    "age":"18",
    "balance":"0",
}

# 编码之后 = ewogICAgImlkIjoiNDEwMDAwMjAwMDAyMDIwMjEyIiwKICAgICJ1c2VybmFtZSI6InpoYW5nc2FuIiwKICAgICJhZ2UiOiIxOCIsCiAgICAiYmFsYW5jZSI6IjAiLAp9

第三部分

# 把前两部分进行整体的签名

# 1. 把第一部分和第二部分编码之后的字符串用 "."拼接,生成一个新的字符串
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ewogICAgImlkIjoiNDEwMDAwMjAwMDAyMDIwMjEyIiwKICAgICJ1c2VybmFtZSI6InpoYW5nc2FuIiwKICAgICJhZ2UiOiIxOCIsCiAgICAiYmFsYW5jZSI6IjAiLAp9

# 2. 把新生成的字符串,通过HS256这种加密方式进行加密,加密的时候,需要提供一个加密密钥
加密(新字符串+加密密钥)
# 生成一个签名内容 
# MDkzZDQ2M2JjOGVlMTJmNzQxOTNkZGJmZDYxODY3NzVmYWI4ZGY1ZDFmNmVmOWU5MzU2OTUwMDIyN2E0ZGVmZA

最终的JWT

# 第一部分.第二部分.第三部分
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ewogICAgImlkIjoiNDEwMDAwMjAwMDAyMDIwMjEyIiwKICAgICJ1c2VybmFtZSI6InpoYW5nc2FuIiwKICAgICJhZ2UiOiIxOCIsCiAgICAiYmFsYW5jZSI6IjAiLAp9.MDkzZDQ2M2JjOGVlMTJmNzQxOTNkZGJmZDYxODY3NzVmYWI4ZGY1ZDFmNmVmOWU5MzU2OTUwMDIyN2E0ZGVmZA

第一部分:header

#一般固定如下
{"alg":"HS256","typ":"JWT"}

base64编码 ,并替换=--->    "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"

第二部分: payload

{
   'id': user.id,
   'username': user.username,
   ‘exp’: time.time() + 300,  #过期时间
}

base64编码-> "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZvaG4gRGdWV9"

第一部分和第二部分, 可以被其他人使用base64解密,不建议存储敏感信息

第三部分:
前两部分的编码串通过 ‘.’ 连接,得到如下:

temp = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZvaG4gRGdWV9"

然后使用第一部分 声明的算法HS256进行加密,得到如下:

import hmac
h = hmac.new(b'key', temp.encode(), digestmod='SHA256')
sign = base64.urlsafe_b64encode(h.digest())

最后三者拼接:1.2.3

django方案一(采用)

自定义登录视图,使用pyjwt 自己生成 token, 此类方案支持所有的python框架

pip install pyjwt

生成token

import datetime
import jwt  #需要安装pip install pyjwt
from django.conf import settings

def generate_token(user):
    """
     :param user: 用户对象
     :return: 生成的token
    """
    #自己组织payload
    payload = {
        'user_id': user.id,
        'username': user.username,
        'exp': datetime.datetime.now() + datetime.timedelta(seconds=300)
    }
    # token 要自己生成: 第二部分的载荷,盐部分, 算法
    token = jwt.encode(payload=payload, key=settings.SECRET_KEY, algorithm='HS256')
    return token

登录视图

from django.contrib.auth import authenticate
from rest_framework.views import APIView
from rest_framework.response import Response
from user.models import *

class Login(APIView):
    def post(self, request):
    	#获取前端数据
        username = request.data.get('user')
        password = request.data.get('pwd')

        user = authenticate(username=username, password=password)

        if user:
            token = generate_token(user)
            return Response({
            	'code':200,
                'msg': '登录成功!',
                'token': token,
                'user': {
                    'userName': user.username,
                    'uid': user.id
                }
            })
        else:
            return Response({"code":204, 'msg': '用户名或密码错误'})

路由配置

urlpatterns = [
   path('login/', Login.as_view()),
]

Vue前端存储token

localStorage.setItem("token", res.data.token)
#后续的请求 携带token
let token = localStorage.getItem("token")

django方案二

自定义登录视图,使用 djangorestframework-jwt提供的 方法生成token,此类方案只能支持django框架

pip install djangorestframework-jwt

登录视图

from rest_framework_jwt.utils import jwt_payload_handler, jwt_encode_handler 
from django.contrib.auth import authenticate
from rest_framework.views import APIView
from rest_framework.response import Response
from user.models import *


class Login(APIView):
    def post(self, request):
        username = request.data.get('user')
        password = request.data.get('pwd')

        user = authenticate(username=username, password=password)

        if user:
            payload = jwt_payload_handler(user)
			token = jwt_encode_handler(payload)
            return Response({
            	"code":200,
                'msg': '登录成功!',
                'token': token,
                'user': {
                    'userName': user.username,
                    'uid': user.id
                }
            })
        else:
            return Response({"code":200, 'msg': '用户名或密码错误!'})

路由配置

urlpatterns = [
   path('login/', Login.as_view()),
]

WEB STORAGE

存数据/改数据

localStorage.setItem(“age”,“29”)

取数据

var age = localStorage.getItem(“age”)

删除数据

localStorage.removeItem(“age”)

多方式登录

django内置的用户认证方式只支持 用户名和密码,而实际的项目开发中,登录方式往往不止一种

因此,需要重写用户认证类,指明新的 认证方式,添加手机号登录方式。

django

官方文档

自定义身份认证类

utils/auth.py,在项目中创建一个utils目录,自定义认证类

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.hashers import check_password
from django.db.models import Q
from user.models import User


# 自定义 认证类,需要在 settings.py 中指明配置项
class AuthBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        """需要继承django内置的用户认证类,并重写认证方法,可以支持多种方式进行登录"""
        try:
            user = User.objects.get(Q(username=username) | Q(mobile=username))
        except Exception as e:
            return None
        # 校验密码: 先写 需要校验的密码, 后写 加密后的密码
        if check_password(password, user.password):
            return user
        else:
            return None

指明配置项

在配置文件中,指明认证类

# 指定用户认证引擎
AUTHENTICATION_BACKENDS = [
    'utils.auth.AuthBackend' , # 自己定义的认证类路径
]

退出登录

使用session会话保持的退出:

Vue前端

// 退出登录
logout() {
  // 不显示退出登录框
  this.visible = false;

  this.$axios.post("/users/logout/").then((res) => {
    console.log("@@退出登录的响应:", res);

    if (res.data.code == 200) {
      // 清空本地登录信息
      // localStorage.setItem("user", "");
      localStorage.removeItem("user");

      // 清空vuex登录信息
      this.setUser("");
      this.notifySucceed("成功退出登录");
    }else{
      console.log("退出请求失败")
    }
  }).catch(err=>{return Promise.reject(err)})
},

django后端

#/users/logout/
path("logout/", Logout.as_view()),

#视图接口
class Logout(APIView):
    def post(self, request):
        print("会话保持:", request.session.items())
        #前端Vue代理会处理sessionid的存储及Cookie信息的携带
        #删除会话
        if request.session.get("username"):
            del request.session["username"]
        print("再次查看session:", request.session.items())

        return Response({"code":200, "msg":"ok"})

使用jwt会话保持的退出:

只需要 删除 localStorage中存储的token即可

也可以用中间件来做会话保持

在根目录下新建middleware 文件夹->middleware.py文件
from django.utils.deprecation import MiddlewareMixin
import jwt
from django.conf import settings

class CheckUserMiddleware(MiddlewareMixin):
    def process_request(self, request):
        token = request.headers.get('token')
        if not token:
            request.user_info = None
        else:
            payload = jwt.decode(jwt=token, key=settings.SECRET_KEY, algorithms='HS256')
            request.user_info = payload
记得在项目配置里导入自定义的中间件
MIDDLEWARE = [
    'middleware.middleware.CheckUserMiddleware'
]

之后在验证用户是否登录时

		try:
            user_info = request.user_info
            user = User.objects.get(id=user_info.get('id'))
        except Exception as e:
            print(e)
            return Response({
                'code': 204,
                'msg': '用户不存在或未登录!'
            })
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值