一.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_token\G; 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:\Users\Administrator>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="width: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%;
width: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;
width: 85%;
}
.tips {
font-size: 14px;
color: #fff;
margin-bottom: 10px;
}
.svg-container {
padding: 6px 5px 6px 15px;
color: $dark_gray;
vertical-align: middle;
width: 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;
width: 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:\devops\data\web\vueAdmin-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