zoukankan      html  css  js  c++  java
  • 二.3.token认证,jwt认证,前端框架

    一.token:

    铺垫:  

    之前用的是通过最基本的用户名密码登录我的运维平台http://127.0.0.1:8000/---这种用的是form表单,但是这种对于前后端分离的不适合。前后端分离,应该通过http的url地址去登录,登录完之后获取一个token,我下次请求只需要带着这个token去获取数据即可。

    查看django-drf认证官方配置:

    如下图中可以看到它中有几个认证方式,它默认的认证是BasicAuthentication和SessionAuthentication认证。

    1.默认的认证就走的session认证,只要前后端没有分离的情况下,所有认证全部走的session,也就是你登录完后,后端会向你的浏览器cookie中写入一数据,此数据代表了你的身份。但这个数据它实际上跟session有一定的关联,如下进它的数据库查看session表:

    当用户登录之后发现有如下一条记录,而session_key是放在浏览器上的,session_data就是它的value,这种方式就是session认证。

     

     如下图:session_key就是在浏览器的application下的cookie中---sessionid:

     这是最原始的方式,通过session登录。!

    2.django-drf默认用session和basic,这两个默认我们要写上,如果不想用默认的session,那把session关掉即可那就用用户名登录不了了,那怎样配置,怎样知道它默认用session登录?如下图,它的官方配置默认就支持session和basic,所以如果我想用别的方式就把这个代码配置拷贝到我项目settings.py中的drf中,并覆盖掉它默认值即可--建议线上把session关掉,basic留着:

    token:

      经查看它的源码authentication.py中可知它有提供TokenAuthentication方法(上图1中有),所以我把此方法复制并配置到我项目settings.py中即可如下:

    1.配置token:

    REST_FRAMEWORK = {
     'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
        'PAGE_SIZE':10,
        'DEFAULT_PAGINATION_CLASS':'users.pagination.Pagination',
        'DEFAULT_FILTER_BACKENDS': (
            'django_filters.rest_framework.DjangoFilterBackend',
        ),
        'DEFAULT_PERMISSION_CLASSES': [
            'rest_framework.permissions.AllowAny',
            # 'devops.permissions.Permissions',
        ],
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework.authentication.TokenAuthentication',     <<<这里我配置同时支持session和token
            'rest_framework.authentication.SessionAuthentication',
            'rest_framework.authentication.BasicAuthentication'
        ],
    }

    2.配置installed_apps:

      就像上述所说的,如果用session的话,数据库中有session表,那使用token它也有一张表,我要想同步表结构,必须要把token放在installed_app中,

    .....
    'rest_framework.authtoken',
    'rest_framework',
    .....

    3.同步数据库:

    (python36env) [vagrant@CentOS devops]$ python manage.py makemigrations

    (python36env) [vagrant@CentOS devops]$ python manage.py migrate

    (python36env) [vagrant@CentOS devops]$ python manage.py dbshell

    MariaDB [devops]> show tables;如下已经有token表了

     authtoken_token 

    4.配置url地址:在项目urls.py中加入如下代码:

    from rest_framework.authtoken import views
    urlpatterns += [
        url(r'^api-token-auth/', views.obtain_auth_token)
    ]

    (python36env) [vagrant@CentOS devops]$ python manage.py runserver 0.0.00:8000启动服务

    5.测试:用curl命令用户登录,要先登录所以走的是post,-d参数走json数据,-H告诉它用json去解析

    (python36env) [vagrant@CentOS devops]$ curl -X POST -H "Content-Type: application/json" -d '{"username":"admin","password":"123456"}' http://127.0.0.1:8000/api-token-auth/

    结果如下拿到token了
    {"token":"780ba1a240a974b958731bb446c5a7574976df4e"}

     去数据库看如下,也有了,告诉我们创建时间created,但是发现没有过期时间,也就是说申请了这个token,我再次去请求,拿到的还是这个token,永远不过期。那怎样让它过期,手动删除此token即可。

    MariaDB [devops]> select * from authtoken_tokenG;
    key: 780ba1a240a974b958731bb446c5a7574976df4e
    created: 2020-06-26 15:00:56.118433
    user_id: 2

    6.获取数据:

    (1)给我的运维平台所有api加上访问权限--批量配置(只要登录就可访问):

    settings.py:

    REST_FRAMEWORK = {
     'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
        'PAGE_SIZE':10,
        'DEFAULT_PAGINATION_CLASS':'users.pagination.Pagination',
        'DEFAULT_FILTER_BACKENDS': (
            'django_filters.rest_framework.DjangoFilterBackend',
        ),
        'DEFAULT_PERMISSION_CLASSES': [
            # 'rest_framework.permissions.AllowAny',
            'devops.permissions.Permissions',    <<<<打开模型权限,此时所有的api就需要登录权限
        ],
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework.authentication.TokenAuthentication',
            'rest_framework.authentication.SessionAuthentication',
            'rest_framework.authentication.BasicAuthentication'
        ],
    }

    (2)怎样使用我上述拿到的token去访问数据:

    (python36env) [vagrant@CentOS devops]$ curl -X GET http://127.0.0.1:8000/users/
    {"detail":"Authentication credentials were not provided."}    <<<<它告诉我需要权限,如下我加上我的token后就拿到数据了
    (python36env) [vagrant@CentOS devops]$ curl -X GET http://127.0.0.1:8000/users/ -H 'Authorization: Token 780ba1a240a974b958731bb446c5a7574976df4e' {"count":98,"next":"http://127.0.0.1:8000/users/?page=2","previous":null,"results":[{"id":1,"username":"devops","email":"123@qq.com"}...
    格式化一下如下:就显示标准json结果了
    (python36env) [vagrant@CentOS devops]$ curl -X GET http://127.0.0.1:8000/users/ -H 'Authorization: Token 780ba1a240a974b958731bb446c5a7574976df4e'|python -m json.tool
    { "count": 98, "next": "http://127.0.0.1:8000/users/?page=2", "previous": null, "results": [ { "id": 1, "username": "devops", "email": "123@qq.com" }, { "id": 2, "username": "admin", "email": "admin@51reboot.com" },
    .........

      拿到的token永远不过期,要使过期就手动删除它。因为表中token直接跟user_id关联上,跟某user_id关联上就代表此用户已经登录了,如果这个用户是超级管理员,你只要拿到这个token,那所有的权限信息你也会拥有,那这样就很危险了,因为它永远不过期,不变。除非你每天晚上刷一次,把token表清空。

    二.jwt

    前后端分离之JWT用户认证https://www.jianshu.com/p/180a870a308a

    参考https://github.com/jpadilla/django-rest-framework-jwt

    (python36env) [vagrant@CentOS devops]$ pip install djangorestframework-jwt  安装

    1.配置权限验证--我这里是基于模型的

    REST_FRAMEWORK = {
    .....
        'DEFAULT_PERMISSION_CLASSES': [
            # 'rest_framework.permissions.AllowAny',
            'devops.permissions.Permissions',
        ],
    ......

    2.配置认证后端类

    REST_FRAMEWORK = {
    .....
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
            'rest_framework.authentication.SessionAuthentication',
            'rest_framework.authentication.BasicAuthentication'
        ],
    ...

    3.配置url---devops/urls.py:

    from rest_framework_jwt.views import obtain_jwt_token
    
    urlpatterns = [
        url(r'^', include(route.urls)),
        url(r'^aip-auth', include("rest_framework.urls",namespace="reset_framework")),
        url(r'^docs/', include_docs_urls("lizhihua运维平台接口文档")),
        url(r'^api-token-auth/', obtain_jwt_token)
    ]

    (python36env) [vagrant@CentOS devops]$ python manage.py runserver 0.0.00:8000启动服务

    4.测试:

    (python36env) [vagrant@CentOS devops]$ curl -X POST -d "username=admin&password=123456" http://localhost:8000/api-token-auth/   拿到token
    {"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTkzMjI0NDI2LCJlbWFpbCI6ImFkbWluQDUxcmVib290LmNvbSJ9.SZhNkN6WgkKyMEZKora7rZRzHgbMQyTRrFP7OFxg0Ts"}

    5.获取数据

    (python36env) [vagrant@CentOS devops]$ curl -H "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTkzMjI0MzU1LCJlbWFpbCI6ImFkbWluQDUxcmVib290LmNvbSJ9.3cn__Tc3ddUhl4_PhST83vRZBcNASs0UBKdLvhgupt4" http://localhost:8000/users/|python -m json.tool

    {
    "count": 98,
    "next": "http://localhost:8000/users/?page=2",
    "previous": null,
    "results": [
    {
    "id": 1,
    "username": "devops",
    "email": "123@qq.com"
    },
    {
    "id": 2,
    "username": "admin",
    "email": "admin@51reboot.com"
    },
    .....

       jwt默认它的过期时间是300s,如果想改的话在项目settings.py中如下位置drf下改即可:

    import datetime
    JWT_AUTH = {
        'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=3000),   <<<<秒
    }

    三.前端框架

     参考https://juejin.im/post/59097cd7a22b9d0065fb61d2

     1.需要在windows装node环境----https://nodejs.org/en/下载想要的版本即可

    C:UsersAdministrator>node -v
    v12.18.1

    2.git clone 想要的vue模版并npm run dev

    3.修改项目主题---vueAdmin-template/index.html中修改title

    4.配置用户登录

    (1)登录页面配置src/views/login/index.vue

    <template>
      <div class="login-container">
        <el-form autoComplete="on" :model="loginForm" :rules="loginRules" ref="loginForm" label-position="left" label-width="0px"
                 class="card-box login-form">
          <h3 class="title">lizhihua运维平台</h3>
          <el-form-item prop="username">
            <span class="svg-container svg-container_login">
              <svg-icon icon-class="user" />
            </span>
            <el-input type="text" v-model="loginForm.username" autoComplete="off" placeholder="请输入用户名" />
          </el-form-item>
          <el-form-item prop="password">
            <span class="svg-container">
              <svg-icon icon-class="password"></svg-icon>
            </span>
            <el-input :type="pwdType" @keyup.enter.native="handleLogin" v-model="loginForm.password" autoComplete="off"
                      placeholder="请输入密码"></el-input>
            <span class="show-pwd" @click="showPwd"><svg-icon icon-class="eye" /></span>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" style="100%;" :loading="loading" @click.native.prevent="handleLogin">
              登录
            </el-button>
          </el-form-item>
        </el-form>
      </div>
    </template>
    
    <script>
      // import { isvalidUsername } from '@/utils/validate'
      export default {
        name: 'login',
        data() {
          return {
            loginForm: {
              username: '',
              password: ''
            },
            loginRules: {
              username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
              password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
            },
            loading: false,
            pwdType: 'password'
          }
        },
        methods: {
          showPwd() {
            if (this.pwdType === 'password') {
              this.pwdType = ''
            } else {
              this.pwdType = 'password'
            }
          },
          handleLogin() {
            this.$refs.loginForm.validate(valid => {
              if (valid) {
                this.loading = true
                this.$store.dispatch('Login', this.loginForm).then(() => {
                  this.loading = false
                  this.$router.push({ path: '/' })
                }).catch(() => {
                  this.loading = false
                })
              } else {
                console.log('error submit!!')
                return false
              }
            })
          }
        }
      }
    </script>
    
    <style rel="stylesheet/scss" lang="scss">
      $bg:#2d3a4b;
      $dark_gray:#889aa4;
      $light_gray:#eee;
    
      .login-container {
        position: fixed;
        height: 100%;
        100%;
        background-color: $bg;
        input:-webkit-autofill {
          -webkit-box-shadow: 0 0 0px 1000px #293444 inset !important;
          -webkit-text-fill-color: #fff !important;
        }
        input {
          background: transparent;
          border: 0px;
          -webkit-appearance: none;
          border-radius: 0px;
          padding: 12px 5px 12px 15px;
          color: $light_gray;
          height: 47px;
        }
        .el-input {
          display: inline-block;
          height: 47px;
           85%;
        }
        .tips {
          font-size: 14px;
          color: #fff;
          margin-bottom: 10px;
        }
        .svg-container {
          padding: 6px 5px 6px 15px;
          color: $dark_gray;
          vertical-align: middle;
           30px;
          display: inline-block;
          &_login {
            font-size: 20px;
          }
        }
        .title {
          font-size: 26px;
          font-weight: 400;
          color: $light_gray;
          margin: 0px auto 40px auto;
          text-align: center;
          font-weight: bold;
        }
        .login-form {
          position: absolute;
          left: 0;
          right: 0;
           400px;
          padding: 35px 35px 15px 35px;
          margin: 120px auto;
        }
        .el-form-item {
          border: 1px solid rgba(255, 255, 255, 0.1);
          background: rgba(0, 0, 0, 0.1);
          border-radius: 5px;
          color: #454545;
        }
        .show-pwd {
          position: absolute;
          right: 10px;
          top: 7px;
          font-size: 16px;
          color: $dark_gray;
          cursor: pointer;
          user-select:none;
        }
        .thirdparty-button{
          position: absolute;
          right: 35px;
          bottom: 28px;
        }
      }
    </style>

    (3)config/dev.env.js

    module.exports = merge(prodEnv, {
      NODE_ENV: '"development"',
      BASE_API: '"http://127.0.0.1:8000"',
    })

    (4)src/api/login.js

    export function login(username, password) {
      return request({
        url: '/api-token-auth/',
        method: 'post',
        data: {
          username,
          password
        }
      })
    }

    F:devopsdatawebvueAdmin-template>npm run dev    重启服务

    (5)src/utils/request.js:修改配置request拦截器

     配置成每次请求把token带上,这里用jwt

    import axios from 'axios'
    import { Message } from 'element-ui'
    import store from '../store'
    import { getToken } from '@/utils/auth'
    
    // 创建axios实例
    const service = axios.create({
      baseURL: process.env.BASE_API, // api的base_url
      timeout: 5000 // 请求超时时间
    })
    
    // request拦截器
    service.interceptors.request.use(config => {
      if (store.getters.token) {
        config.headers['Authorization'] = 'JWT ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
      }
      return config
    }, error => {
      // Do something with request error
      console.log(error) // for debug
      Promise.reject(error)
    })
    
    // respone拦截器
    service.interceptors.response.use(
      response => {
        /**
         * code为非20000是抛错 可结合自己业务进行修改
         */
        console.log(response)
        return response.data
      },
      error => {
        console.log('err' + error)
        Message({
          message: error.message,
          type: 'error',
          duration: 5 * 1000
        })
        return Promise.reject(error)
      }
    )
    
    export default service

    (6)src/store/modules/user.js:  配置把token写到浏览器cookie中

    .....  
    actions: {
        // 登录
        Login({ commit }, userInfo) {
          const username = userInfo.username.trim()
          return new Promise((resolve, reject) => {
            login(username, userInfo.password).then(response => {
              setToken(response.token)
              commit('SET_TOKEN', response.token)
              resolve()
            }).catch(error => {
              reject(error)
            })
          })
        },
    ........

    (7)src/utils/auth.js:

    ....
    const TokenKey = 'Token'
    ....

    (8)src/permission.js: 配置登录成功则跳转到首页

    import router from './router'
    import store from './store'
    import NProgress from 'nprogress' // Progress 进度条
    import 'nprogress/nprogress.css'// Progress 进度条样式
    import { Message } from 'element-ui'
    import { getToken } from '@/utils/auth' // 验权
    
    const whiteList = ['/login'] // 不重定向白名单
    router.beforeEach((to, from, next) => {
      NProgress.start()
      if (getToken()) {
        if (to.path === '/login') {
          next({ path: '/' })
          NProgress.done()
        } else {
          if (store.getters.name === '') {
            store.dispatch('GetInfo').then(res => { // 拉取用户信息
              next({ ...to, replace: true })
            }).catch((err) => {
              store.dispatch('FedLogOut').then(() => {
                Message.error(err || 'Verification failed, please login again')
                next({ path: '/' })
              })
            })
          } else {
            next()
          }
        }
      } else {
        if (whiteList.indexOf(to.path) !== -1) {
          next()
        } else {
          next('/login')
          NProgress.done()
        }
      }
    })
    
    router.afterEach(() => {
      NProgress.done() // 结束Progress
    })

    (9)src/views/dashboad/index.vue :

    <template>
      <div class="dashboard-container">
        dashboard
      </div>
    </template>
    
    <script>
    export default {
      name: 'dashboard'
    }
    </script>

    这样就能登录后跳转到首页了!

    若登录后跳转并如下报错:原因是没获取到userinfo对象

    GET http://127.0.0.1:8000/UserInfo/ 404 (Not Found)
    [Vue warn]: data functions should return an object:

     解决:导入userinfo即可

    from django.conf.urls import url, include
    from django.contrib import admin
    from rest_framework.routers import  DefaultRouter
    from rest_framework.documentation import include_docs_urls
    from rest_framework_jwt.views import obtain_jwt_token
    from idcs.views import IdcViewset
    from users.views import UserViewset, DashboardStatusViewset, UserInfoViewset
    from cabinet.views import CabinetViewset
    from manufacturer.views import ManufacturerViewset, ProductModelViewset
    from servers.views import ServerAutoReportViewset, NetworkDeviceViewset, IPViewset, ServerViewset
    
    route = DefaultRouter()
    route.register("idcs", IdcViewset, basename="idcs")
    route.register("users", UserViewset, basename="users")
    route.register("UserInfo", UserInfoViewset, basename="UserInfo")
    route.register("cabinet", CabinetViewset, basename="cabinet")
    route.register("Manufacturer", ManufacturerViewset, basename="Manufacturer")
    route.register("ProductModel", ProductModelViewset, basename="ProductModel")
    route.register("ServerAutoReport", ServerAutoReportViewset, basename="ServerAutoReport")
    route.register("Servers", ServerViewset, basename="Servers")
    route.register("NetworkDevice", NetworkDeviceViewset, basename="NetworkDevice")
    route.register("IP", IPViewset, basename="IP")
    route.register("dashboardStatus", DashboardStatusViewset, basename="dashboardStatus")
    
    urlpatterns = [
        url(r'^', include(route.urls)),
        url(r'^api-auth', include("rest_framework.urls", namespace="rest_framework")),
        url(r'^docs/', include_docs_urls("51reboot运维平台接口文档")),
        url(r'^api-token-auth/', obtain_jwt_token),
    ]

    11

    22

  • 相关阅读:
    JAVA下使用 连接sqlserver 驱动包
    Windows 7 、Windows Server 2008 和 Windows Server 2008 R2 的支持结束
    VBoxManage命令详解
    端口扫描之王——nmap入门精讲
    rehat-server7常见服务安装与配置总结
    mysql的安装和密码管理、mysql初始密码查找、密码修改、mysql登录
    vim常用命令总结 (转)
    关于《Python绝技:运用Python成为顶级黑客》的学习笔记
    常用MySQL图形化管理工具
    Chrome谷歌浏览器离线安装包下载
  • 原文地址:https://www.cnblogs.com/dbslinux/p/13196264.html
Copyright © 2011-2022 走看看