美文网首页Django+Vue生鲜电商
【Vue+DRF生鲜电商】13.JWT用户认证原理配置,Vue登

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

作者: 吾星喵 | 来源:发表于2019-05-04 22:09 被阅读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日支持的版本如下

    • Python (3.5, 3.6, 3.7)
    • Django (1.11, 2.0, 2.1, 2.2)
    • Django REST Framework (3.5, 3.6, 3.7, 3.8, 3.9)

    安装配置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

    相关文章

      网友评论

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

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