django

Django rest+jwt+vue-element-admi

2019-08-06  本文已影响0人  天狼星1942

一、写在前面

这两天重写了之前项目前台与后台对接的代码。引入 Rest 框架和 samplejwt。在重写过程中参考网上文章发现很多文章多是 django 1.11和 jwt的文章。所以纪录分享

django 2.2 + samplejwt 与 vue-element-admin对接的方法如下。

调试通过后的代码分享在github,大家有兴趣的可自行下载。
注:当前仅调试登录验证框架,logout功能后续再补充。

后台代码地址:https://github.com/Sirius1942/banana/releases/tag/V0.0.1

前台代码地址:https://github.com/Sirius1942/rock/releases/tag/V0.0.1

代码主要参考官方说明:上面demo中很多参考官方示例代码,有疑惑地方大家可自行查找

  1. django rest framework 官方文档:https://www.django-rest-framework.org/

  2. django 官方文档:扩展和自定义后端https://docs.djangoproject.com/en/2.2/topics/auth/customizing/

  3. django-rest-framework-simplejwt 官方代码说明:https://github.com/davesque/django-rest-framework-simplejwt (建议看代码和官方说明,网上资料比较少)

  4. django-cros 官方代码及说明:https://github.com/ottoyiu/django-cors-headers

  5. vue-element-admin 官方指南:https://panjiachen.github.io/vue-element-admin-site/zh/guide/

以下主要参考的下面网上文章,感谢相关作者的支持

  1. https://www.jianshu.com/p/3aa4a9625717

  2. https://www.jianshu.com/p/51dafff7d1a9

  3. https://www.jianshu.com/p/740a0320f960

二、主要修改部分及代码实现过程

1、创建django-admin项目,新建user App 这里不详细说明,请参考相关文档。

2、扩展user 模型,满足vue 需要:

from django.db import models
from django.contrib.auth.models import AbstractUser

 # Create your models here.

 #userProfile继承AbstractUser分类,进行拓展
class UserProfile(AbstractUser):
    """
    用户类拓展
    """
    # name = models.CharField(max_length=30, null=True, blank=True, verbose_name="姓名" )
    avatar = models.CharField(max_length=100, null=True, blank=True, verbose_name="avatar")
    role = models.CharField(max_length=10, default="editor", verbose_name="role")
    introduction = models.TextField(max_length=500,null=True,blank=True, verbose_name="introduction")

    class Meta:
        verbose_name = "user"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.username

在设置文档(settings.py)中 添加默认类

# 添加AUTH_USRE_MODEL 替换默认的user
AUTH_USER_MODEL = 'user.UserProfile'

别忘检查下是否添加了userapp

INSTALLED_APPS = [
    'user.apps.UserConfig',
]

下面补充 login方法,正常情况下你应该已经完成用户模型的字段的补充,并且集成好了rest framework 和jwt 这时登录

image.png

上面的图片显示已经使用了 restframework 且jwt生效所以 直接访问users 想获取用户列表 由于没有权限不能查看到用户信息。

下面自定义登录的 serializer 以便可以补充vue所需要的code,message 字段

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        print('in MyTokenObtainPairSerializer')
        token = super().get_token(user)
        # Add custom claims
        #token['username'] = user.username
        #token['code'] = 20000
       # print(token)
        # ... 官方示例中上面的部分没有生效
        # print(token)
        return token
    def validate(self,attrs):
        data = super().validate(attrs)
        re_data={}
        re_data['data']=data
        re_data['code']=20000
        re_data['message']='success'

        return re_data

注意,安官方说明是不需要 validate 方法的。不过查看了 samplejwt的源代码,发现丢与token的封装都是在这个方法中进行的。所以按下面的方式封装后成功。

以下是官方代码截图 get_token 方法仅返回加密的token而已

class TokenObtainSlidingSerializer(TokenObtainSerializer):
    @classmethod
    def get_token(cls, user):
        return SlidingToken.for_user(user)

    def validate(self, attrs):
        data = super().validate(attrs)

        token = self.get_token(self.user)

        data['token'] = str(token)

        return data

补充封装后的View方法

#别忘了from引用刚刚新增的 serializer
class MyTokenObtainPairView(TokenObtainPairView):
    serializer_class = MyTokenObtainPairSerializer

修改url 调用

urlpatterns = [
    ...
    path('user/login', MyTokenObtainPairView.as_view(), name='token_obtain_pair'),
   ....
]

完成以上应该可以通过正确的 用户名和密码获取到 token

使用postman 测试如下:


image.png

下面新增userinfo 接口

目前简单实现,直接加了一个接口。在 user views 中补充 get_user_info 方法

def get_user_info(request):

    User = get_user_model()
    if request.method=='GET':
        print("in get")
        # print(dir(request))
        #获取请求参数token的值
        token=request.headers.get('AUTHORIZATION')
        # test=request.META.get('CONTENT-TYPE')
        # print(test)
        # print(token)

        token_msg=authentication.JWTAuthentication().get_validated_token(token)
        print(token_msg)
        user_object=authentication.JWTAuthentication().get_user(token_msg)
        
        data = {"username":user_object.username,
                     "first_name":user_object.first_name,
                     "last_name": user_object.last_name,
                     "avatar":user_object.avatar,
                    #  "groups":user_object.groups,
                     "roles":user_object.role,
                     "introduction":user_object.introduction         
        }
        re_data={"data": data,
                 "code": 20000,
                 "message": "success"
        }
        return JsonResponse(re_data)

注:这里遇到个问题是 request 方法中没有 django 传统的 META 方法,所以直接使用 JWTAuthentication.authenticate 方法会报错。这里手工调用 get_user方法,通过 token获取对应的user对象。
另外: data 后续可以通过序列化优化,这里先留一个坑后面填

继续添加 user info url

path('user/info',views.get_user_info),

以上登录后通过token自动获取用户信息后台封装完毕
下面来继续修改前台

前台vue 代码中主要需要修改 utils/request.js
补充消息头:config.headers.Authorization = getToken()
config.headers['Content-Type'] = 'application/json'

// request interceptor
service.interceptors.request.use(
  config => {
    // 后台使用jwt时候发送post请求必须使用 formdata模式
    if (config.method === 'post') {
      // JSON 转换为 FormData
      const formData = new FormData()
      Object.keys(config.data).forEach(key => formData.append(key, config.data[key]))
      config.data = formData
    }
    if (store.getters.token) {
      // let each request carry token
      // ['X-Token'] is a custom headers key
      // please modify it according to the actual situation
      // config.headers['X-Token'] = getToken()
      config.headers.Authorization = getToken()
    }
    config.headers['Content-Type'] = 'application/json'
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

另外需要在获取token 时修改 从 .access对象获取 修改store/modules/user.js 中 set_token 中 data.access

const actions = {
  // user login
  login({ commit }, userInfo) {
    const { username, password } = userInfo
    return new Promise((resolve, reject) => {
      login({ username: username.trim(), password: password }).then(response => {
        const { data } = response
        console.log('in store')
        commit('SET_TOKEN', data.access) // 对接jwt token改成access 获取token
        setToken(data.access)
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  },

其余由于后台的role目前采用1个字段所以前台获取user地方临时处理成,如果传入的roles不是list 将其转化成list 。注意下面代码当获取不到role时还是会报错,只要role不为空不会有问题,留个坑后面填

// roles must be a non-empty array
        if (!roles || roles.length <= 0) {
          // reject('getInfo: roles must be a non-null array!')
          var roles_list = []
          roles_list[0] = roles
          commit('SET_ROLES', roles_list)
        } else {
          commit('SET_ROLES', roles)
        }

以上基本上是全部修改,可能有记不清的地方,大家如果执行代码时候有问题欢迎留言。

后续继续填的坑,再慢慢更新:
1、logout —— auth与jwt、rest框架整合。
2、user与groups 关联
3、多roles 权限更改与判断
4、前台完整的用户管理

上一篇下一篇

猜你喜欢

热点阅读