simplejwt token前后端验证详解

  1. 前后端分离场景:

    django

    djangorestframework

    djangorestframework-simplejwt

    element-ui

  2. 范例说明:

    后端部分代码:

    • serializers.py

      from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
      
      # Custom Token ObtainPairSerializer
      class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
       '''
       自定义jwt认证返回值
       '''
       def validate(self, attrs):
           data = super().validate(attrs)
           refresh = self.get_token(self.user)
           data['refresh'] = str(refresh)
           data['access'] = str(refresh.access_token)
           data['user_id'] = self.user.id
           data['user_name'] = self.user.username
           return data
      
    • views.py

      from backend.serializers import *
      from rest_framework_simplejwt.views import TokenObtainPairView
      
      # custom token view
      class CustomTokenObtainPairView(TokenObtainPairView):
       serializer_class = CustomTokenObtainPairSerializer
      
    • urls.py

      from django.contrib import admin
      from django.views.generic import TemplateView
      from django.urls import path, include
      from rest_framework_simplejwt.views import TokenRefreshView, TokenVerifyView
      from rest_framework.routers import DefaultRouter
      from backend import views
      
      router = DefaultRouter()
      router.register(r'users', views.UserViewSet)
      router.register(r'groups', views.GroupViewSet)
      
      # 使用自动url路由连接我们的API
      # 另外,我们还包括支持浏览器浏览API的登录URL
      app_name = 'backend'
      urlpatterns = [
       path('', TemplateView.as_view(template_name='index.html'), name='index'),
       path('admin/', admin.site.urls),  # django 管理员视图
       path('alterpassword/', views.AlterPassword.as_view()),
       path('search/', views.GlobalSearchAPIView.as_view(), name="search"),
       path('api//', include(router.urls)),  # 自定义rest api
       path('api-token-auth/', views.CustomTokenObtainPairView.as_view(),
            name='custom_token_obtain_pair'),  # rest_framework_simplejwt 生成
       path('api-token-refresh/', TokenRefreshView.as_view(),
            name='token_refresh'),  # rest_framework_simplejwt 刷新
       path('api-token-verify/', TokenVerifyView.as_view(),
            name='token_verify'),  # rest_framework_simplejwt 验证
       path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),  # rest framework api 可视化
      ]

    前端部分代码:

    • axios-filter.js(拦截器)

      
      import axios from 'axios'
      import { Message } from 'element-ui'
      import router from '@/router'
      
      // 读取cookie中的csrftoken
      axios.defaults.xsrfCookieName = 'csrftoken'
      // axios header设置X-CSRFTOKEN
      axios.defaults.xsrfHeaderName = 'X-CSRFTOKEN'
      // 设置Content-Type
      axios.defaults.headers = {
      'Content-Type': 'application/json;charset=UTF-8'
      }
      // 设置超时时间
      axios.defaults.timeout = 50000
      
      // request拦截器
      axios.interceptors.request.use(
      config => {
       // sessionStorage 如果存在jwt token
       if (sessionStorage.token) {
         // 则JWT token认证格式,写入header
         config.headers.Authorization = 'JWt ' + sessionStorage.token
         // console.log(sessionStorage.token)
       }
       return config
      },
      error => {
       // console.log(error)
       return Promise.reject(error)
      }
      )
      // reponse自定义拦截器 start
      function customAxiosResponseInterceptor () {
      const interceptor = axios.interceptors.response.use(
       response => response,
       error => {
         // console.log(error)
         var config = error.config
         if (error.response.status === 401) {
           axios.interceptors.response.eject(interceptor)
           let data = { 'refresh': sessionStorage.getItem('refresh') }
           return axios.post('/api-token-refresh/', data).then(response => {
             sessionStorage.setItem('token', response.data.access)
             console.log('refresh access token success')
             // 重新发送请求
             return axios(config)
           }).catch(error => {
             Message({
               showClose: true,
               message: '登录超时,请重新登录!!!',
               type: 'error'
             })
             redirectLoginWithQuery()
             console.log(error)
             return Promise.reject(error)
           }).finally(customAxiosResponseInterceptor)
         } else {
           Message({
             showClose: true,
             message: error.response.data,
             type: 'error'
           })
           return Promise.reject(error)
         }
       }
      )
      }
      customAxiosResponseInterceptor()
      // reponse自定义拦截器 end
      // login with redirect
      function redirectLoginWithQuery () {
      router.push({
       path: '/login',
       query: {
         redirect: router.currentRoute.fullPath
       }
      })
      }
      export default axios
      
      ```[simplejwt token前后端验证详解](https://wordpress-1251154727.cos.ap-beijing.myqcloud.com/wp-content/uploads/2021/07/simplejwt-token前后端验证详解.html "simplejwt token前后端验证详解")
    • login.vue

      
      
      
      
      
      
  3. token相关接口使用说明

    方法 功能说明 特点
    TokenObtainPairView 用户第一次登录,返回access和refresh的两个token access的token应该设置较短有效时间,refresh的token应该设置较长的合理有效时间
    TokenRefreshView 传递有效时间内的refresh-token,接口返回有效时间的新access-token refresh-token过期返回的状态码与access-token过期返回的状态码都是401(vue拦截器对此处理需要一点小技巧,请参考axios-filter.js)
    TokenVerifyView 验证提交的token,返回当前token状态信息
  4. settings.py中simplejwt相关设置参数

    SIMPLE_JWT = {
       'ACCESS_TOKEN_LIFETIME': timedelta(minutes=1),
       'REFRESH_TOKEN_LIFETIME': timedelta(minutes=2),
       'ROTATE_REFRESH_TOKENS': False,
       'BLACKLIST_AFTER_ROTATION': True,
       'UPDATE_LAST_LOGIN': False,
    
       'ALGORITHM': 'HS256',
       'SIGNING_KEY': SECRET_KEY,
       'VERIFYING_KEY': None,
       'AUDIENCE': None,
       'ISSUER': None,
    
       'AUTH_HEADER_TYPES': ('JWt',),
       'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
       'USER_ID_FIELD': 'id',
       'USER_ID_CLAIM': 'user_id',
       'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
    
       'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
       'TOKEN_TYPE_CLAIM': 'token_type',
    
       'JTI_CLAIM': 'jti',
    
       'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
       'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
       'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
    }

    完整参数请参考:simplejwt完整参数项

  5. 实际应用中:
    verify_jwt_token调用的频率可能比较少,
    如果对安全性没太大要求,可以只使用access的token,不启用refresh的token,access的token超时后,跳转登录页即可。
    当用户成功验证而应用程序不更新cookie时,这个时候就存在会话固定漏洞,攻击者可利用此漏洞发起会话劫持
    所以建议设置——ACCESS_TOKEN_LIFETIME为较短时间,设置REFRESH_TOKEN_LIFETIME为合适的较长时间,
    即——access为较短的有效期,refresh设置为合理的较长有效期,
    前端不断携带refresh token刷新接口,获取新的access token来进行业务交互。

  6. 简述一下djangorestframework-jwt和djangorestframework-simplejwt的区别

    1. 关于jwt的模块,在使用obtain_jwt_token方法时,只会返回一个token,且该token只能在固定有效期时间内调用refresh_jwt_token,获取新的token(还是固定有效期)

    2. 而如果使用djangorestframework-simplejwt模块,在使用TokenObtainPairView方法时,会直接返回两个(access和refresh)token,且设置不同有效期,携带refresh token访问TokenRefreshView,才能返回新的固定有效期的access token

  7. 参考链接:

    https://jpadilla.github.io/django-rest-framework-jwt
    https://django-rest-framework-simplejwt.readthedocs.io/en/latest/
    https://simpleisbetterthancomplex.com/tutorial/2018/12/19/how-to-use-jwt-authentication-with-django-rest-framework.html
    https://www.remoteinning.com/blog/how-to-use-jwt-authentication-with-django-rest-framework
    https://stackoverflow.com/questions/51646853/automating-access-token-refreshing-via-interceptors-in-axios

  8. 可能格式展示有问题,请下载附件查看详情
    simplejwt token前后端验证详解

You may also like...