Django rest+jwt+vue-element-admin 实现用户登录

一、写在前面

这两天重写了之前项目前台与后台对接的代码。引入 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、前台完整的用户管理

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容