vlambda博客
学习文章列表

Vue结合Django-Rest-Framework实现登录认证(上)

前言

最近需要做一个登录认证的功能,所以想将整个的过程做一个记录,方便以后回头查看,同时希望能给和我遇到同样问题的同学一些参考。

因为登录认证的核心其实在后端,我这个不专业的前端Developer在实现这个功能的时候不会深究太多。所以在整个的记录过程中没有过多的原理,着重记录实现过程和这个过程中遇到的问题以及解决方案

如果对登录认证这块的流程原理不太清楚,可以先做一点功课,或者直接看我的整个操作结果,看完之后会从结果现象出发,在去理解学习登录认证原理就会更容易。

初始的环境介绍

这里先介绍一下这个项目的一些基本情况,建议大家理性参考。

前端

框架:Vue
HTTP库:Axios

后端

后端框架:基于pythondjango框架
WEB API:rest-framework
服务器: centos
数据库:PostgreSQL

其中,python版本为2.7.5django版本的版本为1.10.1

后端数据库

后端数据库使用的是PostgreSQL,并且已经对数据库表进行了同步,命令为:python manage.py migrateVue结合Django-Rest-Framework实现登录认证(上)

数据库进行同步以后,会生成10相关的数据表

Vue结合Django-Rest-Framework实现登录认证(上)

需要关注的就是auth_user这个表,它是django框架生成的用户表,后面就使用这个表保存用户的信息。

我们先来看一下这个表的结构。

Vue结合Django-Rest-Framework实现登录认证(上)

目前表里面是没有什么数据的。

Vue结合Django-Rest-Framework实现登录认证(上)

接着我们来创建一个用户,用于后期的登录测试python manage.py createsuperuser

Vue结合Django-Rest-Framework实现登录认证(上)

用户创建成功,此时在去查询数据表,就能看到刚才新增的用户信息了。

Vue结合Django-Rest-Framework实现登录认证(上)

表中password字段的值是经过加密处理的,还有一些字段的值是默认生成的。这些大家了解即可,不是本次内容的重点。

后端项目配置

接下来我们先对后端项目进行配置。

登录认证配置

首先我们需要在后端Django项目的settings.py文件中配置登录认证的APP

INSTALLED_APPS = [
    ...
    'rest_framework.authtoken'
]

设置全局的认证方案

接着在settings.py中配置全局身份认证方案。

# 设置全局身份认证方案
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',  # token认证
    )
}

数据库同步

上面的配置完成需要进行数据库同步:python manage.py migrate

Vue结合Django-Rest-Framework实现登录认证(上)

完成后我们再次查看数据库中的表。

Vue结合Django-Rest-Framework实现登录认证(上)

发现会多出来一个名为authtoken_token的数据表,这个表就是和用户登录认证相关的数据表。

简单测试

前面我们已经配置好了全局的认证方案,因为我们并没有进行登录操作,所以正常情况下此时任意访问后端的某个API,应该就会出现一些错误,那我们来试一试。

Vue结合Django-Rest-Framework实现登录认证(上)

事与愿违哈,我们发现请求还是可以正常发出的。

关于这个问题我在网上搜索了很久,看到有网友说关于settings.py设置的全局身份认证方案有问题,正确的应该是下面这样的配置。

# 设置全局身份认证方案
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',  # token认证
    )
}

这里我们多添加了一个配置DEFAULT_PERMISSION_CLASSES,那这个配置项是关于用户的权限配置,如果没有配置该项的话就是允许用户无限制访问,不管我们的请求是被认证未认证的。

那加上这个认证以后呢,在去请求前面的API

Vue结合Django-Rest-Framework实现登录认证(上)

可以看到这次终于有反应了,服务器端返回了401 Unauthorized,也就是未经认证的用户。

思考后的再测试

此时我想起前面新增的DEFAULT_PERMISSION_CLASSES权限配置,想在了解一下这个配置项,于是将settings进行了修改。

# 设置全局身份认证方案
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    # 'DEFAULT_AUTHENTICATION_CLASSES': (
    #     'rest_framework.authentication.TokenAuthentication',  # token认证
    # )
}

即注释了全局的用户认证,保留了权限配置。现在我们再来测试一下这个全局的权限配置会对请求产生什么影响。

Vue结合Django-Rest-Framework实现登录认证(上)

我们发现这条API返回了403,也就是访问的权限出现了问题,所以说这两个配置本身是有各自的作用。

那总而言之,在单独配置TokenAuthentication的情况下我们的 用户认证是没有生效的,这个点其实官方文档也有明确说明:如果没有配置DEFAULT_PERMISSION_CLASSES就是允许用户无限制访问,不管我们的请求是被认证或未认证的。

所以如果需要进行用户认证,同时必须有权限配置用户认证才会生效。

后端登录接口实现

接着我们来实现后端的登录接口。

创建一个新的app

关于用户的登录认证我们新建一个appdjango-admin startapp userAuth

编写登录逻辑

这里我们编写一个简单的登录逻辑即可。

# -*- coding: utf-8 -*-
# Create your views here.

from django.contrib import auth

from rest_framework.permissions import AllowAny
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from rest_framework.decorators import api_view, authentication_classes, permission_classes

@api_view(['POST'])
@permission_classes((AllowAny,))
@authentication_classes(())
def login(request):
    """登录"""
    result = True
    errorInfo = u''
    detail = {}
    data = request.data
    username = data.get('username')
    password = data.get('password')
    
    # 调用django进行用户认证 
    # 验证成功 user返回<class 'django.contrib.auth.models.User'>
    # 验证失败 user返回None
    user = auth.authenticate(username=username, password=password)
    print "user",user
    if user == None:
        result = False
        errorInfo = u'用户名或密码错误'
        return Response({"result": result, "detail": detail, "errorInfo": errorInfo})
    
    # 用户名和密码验证成功
    # 获取用户的token 如果没有token ,表示时用户首次登录,则进行创建,并且返回token
    try:
        tokenObj = Token.objects.get(user_id=user.id)
    except Exception as e:
        # token 不存在 说明是首次登录
        tokenObj = Token.objects.create(user=user, token=token)
    # 获取token字符串
    token = tokenObj.key
    return Response({"result": result, "detail": {'token': token}, "errorInfo": errorInfo})

登录逻辑在views.py中实现。

关于这段逻辑我简单画了一个图,方便大家理解。

Vue结合Django-Rest-Framework实现登录认证(上)

测试后端登录接口

在测试后端登录接口之前,我们先看下前面生成的关于用户认证的数据表authtoken_token

Vue结合Django-Rest-Framework实现登录认证(上)

暂时是没有任何记录,然后我们在产品的登录界面输入用户名密码

Vue结合Django-Rest-Framework实现登录认证(上)

这里的用户名密码就是前面使用python manage.py createsuperuser命令创建的admin账户。

因为是首次登录,所以会为该用户创建token,即authtoken_token会产生一条记录。这里给大家看一看我登录成功以后的authtoken_token数据表。

Vue结合Django-Rest-Framework实现登录认证(上)

前端的登录逻辑

最后就是关于前端vue处的逻辑,我们先看一下登录页面的methods逻辑。

login: function(){
      axios.post(url, this.loginForm).then(response =>{
          const {result, detail, errorInfo}  = response.data;
          if(result == true){
              // 登录成功
              // 设置token
              localStorage.setItem('token', detail.token);
              // 跳转页面
              this.$router.push('/certMake');
          }else{
              this.$message({
                  showClosetrue,
                  message: errorInfo,
                  type'error'
              });
          }
      });
  }

可以看到登录成功以后我使用localStoragetoken进行了本地存储。接着我们需要在访问其他API时将这个token设置到请求头部。

在这之前呢,我们先看一下API请求头部没有添加token时的结果。

Vue结合Django-Rest-Framework实现登录认证(上)

可以看到响应是401,然后将token的信息添加到请求头。

// 别的模块的请求
getCertListfunction(){
      const url = '/api/cert/certManage/certList';
      // 从localStorage获取到登录时保持的token
      const auth = 'Token ' + localStorage.getItem('token');
      const header = {'Authorization':auth}
      axios.get(url, {'headers': header}).then(response =>{
          const {result, detail, errorInfo}  = response.data;
          if(result == true){
             this.certList = detail.certList;
          }else{
              this.$message({
                  showClosetrue,
                  message: errorInfo,
                  type'error'
              });
          }
      });
}

然后在重新请求上面的API

Vue结合Django-Rest-Framework实现登录认证(上)

可以看到请求头部已经添加上了token,而且响应的状态码也是200

那说明我们的登录认证功能已经简单的实现了。

结语

那本篇文章是不是非常简单呢,不过仔细想来以上的实现还有很多的槽点,比如登录成功以后,前端在访问别的API时需要在请求头部添加token信息,那一般项目中会编写很多的API,这样岂不是每个API都要编写下面这么一段重复的代码。

const auth = 'Token ' + localStorage.getItem('token');
const header = {'Authorization':auth}
axios.get(url, {'headers': header}).then(response =>{
   // ......
});

再比如,当用户第一次登录成功之后,之后好像再也不需要进行登录了。那如果用户不小心泄露了自己的密码,那不就麻烦了。

所以,下一篇文章会针对以下几个点进一步的完善这个登录认证功能。

1.axios优化
2.用户注销
3.设置登录过期时间

参考文章

✍ django-rest-framework官方文档#权限篇(https://www.django-rest-framework.org/api-guide/permissions/)

✍ django-rest-framework官方文档#授权认证篇(https://www.django-rest-framework.org/api-guide/authentication/)

Vue结合Django-Rest-Framework实现登录认证(上)