您现在的位置是: 网站首页 >Django >Vue+Django REST framework前后端分离生鲜电商 Django

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

admin2019年5月4日 22:03 Django | Python | Vue 3441人已围观

Vue+Django REST framework前后端分离生鲜电商简介 Vue+Django REST framework 打造前后端分离的生鲜电商项目(慕课网视频)。 Github地址:https://github.com/xyliurui/DjangoOnlineFreshSupermarket ; Django版本:2.2、djangorestframework:3.9.2。 前端Vue模板可以直接联系我拿。

## JSON Web Token(JWT)用户认证原理 JSON Web Token Authentication JSON Web Token是一个相当新的标准,可以用于基于令牌的身份验证。与内置的`TokenAuthentication`方案不同,JWT身份验证不需要使用数据库来验证`token`。一个用于JWT身份验证的包是[djangorestframework-simplejwt](https://github.com/davesque/django-rest-framework-simplejwt),它提供了一些特性以及一个可扩展的token黑名单应用程序。 可以访问 [前后端分离之JWT用户认证](https://lion1ou.win/2017/01/18/) 了解其原理。 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 首先安装包 ```bash pip install djangorestframework_simplejwt ``` #### 配置jwt认证类 然后,必须将django项目配置为使用该库。在 settings.py, 添加`rest_framework_simplejwt.authentication.JWTAuthentication`到认证类中。 ```python # 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 JWT` 的`TokenObtainPairView`和`TokenRefreshView`视图的路由: ```python 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/ ![BLOG_20190504_220855_45](/media/blog/images/2019/05/BLOG_20190504_220855_45.png "博客图集BLOG_20190504_220855_45.png") 可以得到请求的结果 ```json { "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU1NjA4MzUyNiwianRpIjoiYTFjMzA4NWJkYzM0NGVlYTlhNDhlMmYyYTZkMTc3OWUiLCJ1c2VyX2lkIjoxfQ.xWFW0xTwsO47HOASaPxsT8Tich8BwP1SJZJjBBU36jg", "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTU1OTk3NDI2LCJqdGkiOiI1YmVlYzEzM2ZiMzM0MDY5OWQ1NzJiMjFhNGU3MzIzZSIsInVzZXJfaWQiOjF9.tKqJRlrb_DQsuw31xtwYW0i1fTc-7ZhGUqLk1ZiHyI0" } ``` 以点号`.`做分隔,前两部分(Header、Payload)都是使用 Base64 进行编码,即前端可以解开知道里面的信息。后一部分(Signature )需要使用编码后的 Header 和 Payload 以及我们提供的一个密钥,然后使用 header 中指定的签名算法(HS256)进行签名。签名的作用是保证 JWT 没有被篡改过。 三个部分通过.连接在一起就是我们的 JWT 了。 可以使用返回的`access`token来验证受保护视图的身份验证。格式为:**`Authorization: Bearer [access对应的值]`** **重点来了,请注意**在 `src/axios/index.js` http request拦截器中一定要注意将`JWT`修改为`Bearer`,也就是``Bearer ${store.state.userInfo.token}``,否则之后获取个人信息类的页面肯定会出错。 ```JavaScript // http request 拦截器 axios.interceptors.request.use( config => { if (store.state.userInfo.token) { // 判断是否存在token,如果存在的话,则每个http header都加上token config.headers.Authorization = `Bearer ${store.state.userInfo.token}`; } return config; }, err => { return Promise.reject(err); }); ``` 当这个短暂的`access`token过期时,可以使用较长时间的`refresh`token获得另一个`access`token。 页面上测试使用这个`access`请求,同样在`list`函数中打上断点。进入调试状态。 #### jwt access过期 如果中途等待时间过长,那么`access`就会过期,这时候访问 http://127.0.0.1:8000/goods/ ,就会出现401错误 ![BLOG_20190504_220844_34](/media/blog/images/2019/05/BLOG_20190504_220844_34.png "博客图集BLOG_20190504_220844_34.png") #### 使用refresh获取新的access 这时候可以使用`refresh`来获取新的`access`,http://127.0.0.1:8000/api/token/refresh/ 需要将`refresh`的值传入后POST提交得到新的`access` ![BLOG_20190504_220828_24](/media/blog/images/2019/05/BLOG_20190504_220828_24.png "博客图集BLOG_20190504_220828_24.png") ```json { "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTU1OTk4NTY5LCJqdGkiOiI2ODA0Y2Y1ZGJhZWQ0MGFiYjlkNDlmZjY0MjMxZWRhMSIsInVzZXJfaWQiOjF9.is3sU4yEs69ARFlTKnCEXNVXByfxvibSPRZdHSW8EJI" } ``` #### 使用jwt access获取认证用户 获取新的`access`后再次添加认证请求 http://127.0.0.1:8000/goods/ ![BLOG_20190504_220818_16](/media/blog/images/2019/05/BLOG_20190504_220818_16.png "博客图集BLOG_20190504_220818_16.png") 可以在后端得到登录用户`user`的信息 ![BLOG_20190504_220740_34](/media/blog/images/2019/05/BLOG_20190504_220740_34.png "博客图集BLOG_20190504_220740_34.png") 可以访问 [JWT配置](https://github.com/davesque/django-rest-framework-simplejwt#settings) 查看自定义配置,比如可以自定义过期时间等。 #### 自定义jwt配置 设置过期时间为7天,在 settings.py 中添加 ```python # 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 中,登录的接口是 ```js //登录 export const login = params => { return axios.post(`${local_host}/login/`, params) }; ``` 而现在DRF登录url是`api/token/`,有两种修改,一是修改Vue中的登录接口,二是修改Django的URL。这采用修改后台的方式,修改主 urls.py ```python 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 输入用户名、密码点击登录 ![BLOG_20190504_220723_35](/media/blog/images/2019/05/BLOG_20190504_220723_35.png "博客图集BLOG_20190504_220723_35.png") 登陆后就可以在页面看到登录用户 ![BLOG_20190504_220713_89](/media/blog/images/2019/05/BLOG_20190504_220713_89.png "博客图集BLOG_20190504_220713_89.png") src/views/login/login.vue 中有登录的逻辑 ```JavaScript 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`的信息 ![BLOG_20190504_220658_24](/media/blog/images/2019/05/BLOG_20190504_220658_24.png "博客图集BLOG_20190504_220658_24.png") 然后将用户名保存到cookie的`name`中,`access`保存在cookie的`token`中。 通过Vue的浏览器插件可以看到,当用户没有登录时`name`值为空 ![BLOG_20190504_220645_97](/media/blog/images/2019/05/BLOG_20190504_220645_97.png "博客图集BLOG_20190504_220645_97.png") 点击登录,输入帐密登录后,显示当前的`name`和`token`的值 ![BLOG_20190504_220639_61](/media/blog/images/2019/05/BLOG_20190504_220639_61.png "博客图集BLOG_20190504_220639_61.png") `that.$store.dispatch('setInfo');`通过这个向Vuex发起一个`setInfo`,(按住Ctrl点击`setInfo`),可以跳转到 src/store/action.js ```JavaScript export const setInfo = makeAction(types.SET_INFO); ``` 按住Ctrl点击`SET_INFO`可以跳转到 src/store/mutation-types.js ```JavaScript export const SET_INFO = 'SET_INFO'; ``` 在 src/store/mutations.js 有下面内容 ```JavaScript [types.SET_INFO](state) { state.userInfo = { name: cookie.getCookie('name'), token: cookie.getCookie('token') } // console.log(state.userInfo); }, ``` mutations就会将`name`和`token`取出来,放在组件中,所以组件中的内容就会实时做一个更新。 ### 增加用户名和手机号登录功能 修改 users/views.py 增加自定义后台认证类 ```python 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 ,添加以下内容 ```python AUTHENTICATION_BACKENDS = ('users.views.CustomBackend',) # 指定认证后台 ``` 将`CustomBackend`打上断点 ![BLOG_20190504_220621_25](/media/blog/images/2019/05/BLOG_20190504_220621_25.png "博客图集BLOG_20190504_220621_25.png") 通过请求 http://127.0.0.1:8000/login/ 测试是否能进入自定义认证后台中 ![BLOG_20190504_220558_68](/media/blog/images/2019/05/BLOG_20190504_220558_68.png "博客图集BLOG_20190504_220558_68.png") 这样就可以在后端看到请求的用户信息了,说明这个认证后台是正确的。 ![BLOG_20190504_220550_45](/media/blog/images/2019/05/BLOG_20190504_220550_45.png "博客图集BLOG_20190504_220550_45.png")

很赞哦! (3)

文章交流

  • emoji
0人参与,0条评论

当前用户

未登录,点击   登录

站点信息

  • 建站时间:网站已运行2077天
  • 系统信息:Linux
  • 后台程序:Python: 3.8.10
  • 网站框架:Django: 3.2.6
  • 文章统计:256 篇
  • 文章评论:60 条
  • 腾讯分析网站概况-腾讯分析
  • 百度统计网站概况-百度统计
  • 公众号:微信扫描二维码,关注我们
  • QQ群:QQ加群,下载网站的学习源码
返回
顶部
标题 换行 登录
网站