Django+Vue生鲜电商

【Vue+DRF生鲜电商】13.JWT用户认证原理配置,Vue登

2019-05-04  本文已影响68人  吾星喵

欢迎访问我的博客专题

源码可访问 Github 查看

JSON Web Token(JWT)用户认证原理

JSON Web Token Authentication

JSON Web Token是一个相当新的标准,可以用于基于令牌的身份验证。与内置的TokenAuthentication方案不同,JWT身份验证不需要使用数据库来验证token。一个用于JWT身份验证的包是djangorestframework-simplejwt,它提供了一些特性以及一个可扩展的token黑名单应用程序。

可以访问 前后端分离之JWT用户认证 了解其原理。

Session方式存储用户id的最大弊病在于Session是存储在服务器端的,所以需要占用大量服务器内存,对于较大型应用而言可能还要保存许多的状态。一般而言,大型应用还需要借助一些KV数据库和一系列缓存机制来实现Session的存储。

而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。除了用户id之外,还可以存储其他的和用户相关的信息,例如该用户是否是管理员、用户所在的分组等。虽说JWT方式让服务器有一些计算压力(例如加密、编码和解码),但是这些压力相比磁盘存储而言可能就不算什么了。

JWT安装配置

由于视频中使用的django-rest-framework-jwt不支持新版的Django和Django Restful Framework,现使用django-rest-framework-simplejwt,2019年4月23日支持的版本如下

安装配置djangorestframework_simplejwt

首先安装包

pip install djangorestframework_simplejwt

配置jwt认证类

然后,必须将django项目配置为使用该库。在 settings.py, 添加rest_framework_simplejwt.authentication.JWTAuthentication到认证类中。

# DRF配置
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    # 'PAGE_SIZE': 5,
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',  # 上面两个用于DRF基本验证
        # 'rest_framework.authentication.TokenAuthentication',  # TokenAuthentication,取消全局token,放在视图中进行
        'rest_framework_simplejwt.authentication.JWTAuthentication',  # djangorestframework_simplejwt JWT认证
    )
}

添加jwt认证URL

此外,在主 url .py 文件(或任何其他url配置)中,包括 Simple JWTTokenObtainPairViewTokenRefreshView视图的路由:

from rest_framework_simplejwt import views as simplejwt_views  # 引入simplejwt


urlpatterns = [
    path('admin/', admin.site.urls),
    path('api-auth/', include('rest_framework.urls')),  # drf 认证url
    path('api-token-auth/', views.obtain_auth_token),  # drf token获取的url
    path('api/token/', simplejwt_views.TokenObtainPairView.as_view(), name='token_obtain_pair'),  # simplejwt认证接口
    path('api/token/refresh/', simplejwt_views.TokenRefreshView.as_view(), name='token_refresh'),  # simplejwt认证接口
    path('ckeditor/', include('ckeditor_uploader.urls')),  # 配置富文本编辑器url

    path('', include(router.urls)),  # API url现在由路由器自动确定。

    # DRF文档
    path('docs/', include_docs_urls(title='DRF文档')),
]

JWT使用

验证获取jwt access

验证jwt接口是否生效,请求 http://127.0.0.1:8000/api/token/

image.png

可以得到请求的结果

{
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU1NjA4MzUyNiwianRpIjoiYTFjMzA4NWJkYzM0NGVlYTlhNDhlMmYyYTZkMTc3OWUiLCJ1c2VyX2lkIjoxfQ.xWFW0xTwsO47HOASaPxsT8Tich8BwP1SJZJjBBU36jg",
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTU1OTk3NDI2LCJqdGkiOiI1YmVlYzEzM2ZiMzM0MDY5OWQ1NzJiMjFhNGU3MzIzZSIsInVzZXJfaWQiOjF9.tKqJRlrb_DQsuw31xtwYW0i1fTc-7ZhGUqLk1ZiHyI0"
}

以点号.做分隔,前两部分(Header、Payload)都是使用 Base64 进行编码,即前端可以解开知道里面的信息。后一部分(Signature )需要使用编码后的 Header 和 Payload 以及我们提供的一个密钥,然后使用 header 中指定的签名算法(HS256)进行签名。签名的作用是保证 JWT 没有被篡改过。
三个部分通过.连接在一起就是我们的 JWT 了。

可以使用返回的accesstoken来验证受保护视图的身份验证。格式为:Authorization: Bearer [access对应的值]

当这个短暂的accesstoken过期时,可以使用较长时间的refreshtoken获得另一个accesstoken。

页面上测试使用这个access请求,同样在list函数中打上断点。进入调试状态。

jwt access过期

如果中途等待时间过长,那么access就会过期,这时候访问 http://127.0.0.1:8000/goods/ ,就会出现401错误

image.png

使用refresh获取新的access

这时候可以使用refresh来获取新的accesshttp://127.0.0.1:8000/api/token/refresh/ 需要将refresh的值传入后POST提交得到新的access

image.png
{
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTU1OTk4NTY5LCJqdGkiOiI2ODA0Y2Y1ZGJhZWQ0MGFiYjlkNDlmZjY0MjMxZWRhMSIsInVzZXJfaWQiOjF9.is3sU4yEs69ARFlTKnCEXNVXByfxvibSPRZdHSW8EJI"
}

使用jwt access获取认证用户

获取新的access后再次添加认证请求 http://127.0.0.1:8000/goods/

image.png

可以在后端得到登录用户user的信息

image.png

可以访问 JWT配置 查看自定义配置,比如可以自定义过期时间等。

自定义jwt配置

设置过期时间为7天,在 settings.py 中添加

# JWT自定义配置
from datetime import timedelta

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(days=7),  # 配置过期时间
    'REFRESH_TOKEN_LIFETIME': timedelta(days=15),
}

Vue登录和JWT接口调试

查看Vue登录逻辑

在 src/api/api.js 中,登录的接口是

//登录
export const login = params => {
    return axios.post(`${local_host}/login/`, params)
};

而现在DRF登录url是api/token/,有两种修改,一是修改Vue中的登录接口,二是修改Django的URL。这采用修改后台的方式,修改主 urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api-auth/', include('rest_framework.urls')),  # drf 认证url
    path('api-token-auth/', views.obtain_auth_token),  # drf token获取的url
    # path('api/token/', simplejwt_views.TokenObtainPairView.as_view(), name='token_obtain_pair'),  # simplejwt认证接口
    path('login/', simplejwt_views.TokenObtainPairView.as_view(), name='token_obtain_pair'),  # 登录一般是login
    path('api/token/refresh/', simplejwt_views.TokenRefreshView.as_view(), name='token_refresh'),  # simplejwt认证接口
    path('ckeditor/', include('ckeditor_uploader.urls')),  # 配置富文本编辑器url

    path('', include(router.urls)),  # API url现在由路由器自动确定。

    # DRF文档
    path('docs/', include_docs_urls(title='DRF文档')),
]

访问前端 http://127.0.0.1:8080/#/app/login 输入用户名、密码点击登录

image.png

登陆后就可以在页面看到登录用户

image.png

src/views/login/login.vue 中有登录的逻辑

login({
    username: this.userName, //当前用户名
    password: this.parseWord
}).then((response) => {
    //console.log(response);
    //本地存储用户信息
    console.log('用户登录信息:');
    console.log(response.data);
    cookie.setCookie('name', this.userName, 7);  // 设置过期时间7天
    // cookie.setCookie('token', response.data.token, 7);
    cookie.setCookie('token', response.data.access, 7);
    //存储在store
    // 更新store数据
    that.$store.dispatch('setInfo');
    //跳转到首页页面
    this.$router.push({name: 'index'})
})

当用户登录将可以获得response.data的信息

image.png

然后将用户名保存到cookie的name中,access保存在cookie的token中。

通过Vue的浏览器插件可以看到,当用户没有登录时name值为空

image.png

点击登录,输入帐密登录后,显示当前的nametoken的值

image.png

that.$store.dispatch('setInfo');通过这个向Vuex发起一个setInfo,(按住Ctrl点击setInfo),可以跳转到 src/store/action.js

export const setInfo = makeAction(types.SET_INFO);

按住Ctrl点击SET_INFO可以跳转到 src/store/mutation-types.js

export const SET_INFO = 'SET_INFO';

在 src/store/mutations.js 有下面内容

    [types.SET_INFO](state) {
        state.userInfo = {
            name: cookie.getCookie('name'),
            token: cookie.getCookie('token')
        }
        // console.log(state.userInfo);
    },

mutations就会将nametoken取出来,放在组件中,所以组件中的内容就会实时做一个更新。

增加用户名和手机号登录功能

修改 users/views.py 增加自定义后台认证类

from django.shortcuts import render
from django.contrib.auth import get_user_model
from django.db.models import Q
from django.contrib.auth.backends import ModelBackend

User = get_user_model()


class CustomBackend(ModelBackend):
    """
    自定义用户登录,可以使用用户名和手机登录,重写authenticate方法
    """
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
            user = User.objects.get(Q(username=username) | Q(mobile=username))
            if user.check_password(password):
                return user
        except Exception as e:
            return None

配置到 settings.py ,添加以下内容

AUTHENTICATION_BACKENDS = ('users.views.CustomBackend',)  # 指定认证后台

CustomBackend打上断点

image.png

通过请求 http://127.0.0.1:8000/login/ 测试是否能进入自定义认证后台中

image.png

这样就可以在后端看到请求的用户信息了,说明这个认证后台是正确的。

image.png
上一篇下一篇

猜你喜欢

热点阅读