django+vue实现钉钉群机器人验证码
又是忙碌的一天,今天给大家带来的教程是使用钉钉群机器人发送随机验证码,
在vue中输入然后在后端进行判断并返回相应的返回值,首先我们去看官方文档
然后开始操作
我们需要django+vue跨域 点击查看
一、创建钉钉群机器人
首先明确一点,钉钉自定义机器人早就不支持在手机端创建了,所以打开你的pc端或者mac端的钉钉客户端,在需要机器人的聊天群界面,点击智能群助手
需要注意的是,在安全设置一栏里,我们选择加签的方式来验证,在此说明一下,钉钉机器人的安全策略有三种,第一种是使用关键字,就是说你推送的消息里必须包含你创建机器人时定义的关键字,如果不包含就推送不了消息,第二种就是使用加密签名,第三种是定义几个ip源,非这些源的请求会被拒绝,综合来看还是第二种又安全又灵活。
创建成功后,系统会分配给你一个webhook地址,这个地址需要保存一下,地址中有唯一的accesstoken
ok,那么怎么利用这个地址让你的机器人推送消息呢?查看官方文档
二、测试发送信息
发现文档居然还是python2.0的版本,好吧,我们自己来翻译成3.0
在项目下新建包utils/dindin.py
import time
import hmac
import hashlib
import base64
import urllib.parse
import requests, json # 导入依赖库
# https://oapi.dingtalk.com/robot/send?access_token=e2e7db9c547beff2f8f93ee06fec87c1a354594045d49fbf30669d7e05b23c38
timestamp = str(round(time.time() * 1000))
#这里的secret是我们cp备份的
secret = 'SECc96a44d409036efafe8645c659a551e3ad3ad15bdfd8a408b6254927a2d0d1ca'
secret_enc = secret.encode('utf-8')
string_to_sign = '{}\n{}'.format(timestamp, secret)
string_to_sign_enc = string_to_sign.encode('utf-8')
hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
sign = urllib.parse.quote(base64.b64encode(hmac_code))
print(sign) # eu4Q16kFubncXuZprXxwRuvDx3Yh/roFWEPn0T5MRPo%3D
# print(timestamp)
# print(sign)
headers = {'Content-Type': 'application/json'} # 定义数据类型
#这里webhook是我们创建成功后 webhook地址
webhook = 'https://oapi.dingtalk.com/robot/send?access_token=e2e7db9c547beff2f8f93ee06fec87c1a354594045d49fbf30669d7e05b23c38×tamp=' + timestamp + "&sign=" + sign
# 定义要发送的数据
# "at": {"atMobiles": "['"+ mobile + "']"
def dindin_post(text):
data = {
"msgtype": "text",
"text": {"content": text},
"isAtAll": True}
res = requests.post(webhook, data=json.dumps(data), headers=headers) # 发送post请求
print(res.text)
dindin_post(134235)
- 测试发送成功
三、创建user(app)
user/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
from utils.basemodel import Base
# Create your models here.
# 用户表
class User(AbstractUser):
username = models.CharField(max_length=32, unique=True)
password = models.CharField(max_length=132)
email = models.CharField(max_length=32, null=True, blank=True)
class Meta:
db_table = "用户"
def __str__(self):
return self.username
我们这里重写了django自带的用户表,所以要在settings中重新定义
AUTH_USER_MODEL = 'user.User'
user/views.py
# 解密
from django.contrib.auth.hashers import check_password, make_password
# 返回HttpResponse
from django.http import HttpResponse
# 解码
from django.utils.baseconv import base64
# 返回Response
from rest_framework.response import Response
#
from rest_framework.views import APIView
from .models import *
import time
import hmac
import base64
from hashlib import sha256
import urllib
import json
import requests
from utils.dindin import dindin_post
import random
from rest_framework.permissions import AllowAny
import jwt
from mydjango import settings
# 注册
class RegisterAPIView(APIView):
permission_classes = (AllowAny,)
def post(self, request):
# 获取信息
username = request.data.get("username")
password = request.data.get("password")
try:
# 存储数据
User.objects.create(username=username, password=make_password(password))
return Response({"msg": "ok", "code": 200})
except Exception as e:
print("user.view--16", e)
return Response({"msg": "no", "code": 400})
# 登录
class LoginAPIView(APIView):
permission_classes = (AllowAny,)
def post(self, request):
# 获取信息
username = request.data.get("username")
password = request.data.get("password")
user_obj = User.objects.filter(username=username).first()
# 判断密码
if user_obj:
if check_password(password, user_obj.password):
# jwt加密
encode_jwt = jwt.encode({"uid": user_obj.id}, settings.SECRET_KEY, algorithm='HS256')
print(encode_jwt)
return Response({"msg": "ok", "code": 200, "token": encode_jwt})
else:
return Response({"msg": "no", "code": 400})
else:
return Response({"msg": "no", "code": 400})
# 发送验证码
class VerificationAPIView(APIView):
permission_classes = (AllowAny,)
def get(self, request):
# 生成随机验证码6位
code = '%06d' % random.randint(0, 999999)
# 存储到session
request.session["code"] = code # 存储的也是 key-value 键值对
request.session.set_expiry(120) # 设置 session 存活期 单位是秒
# 调用钉钉接口 发送验证码
dindin_post(code)
return Response({"msg": "ok", "code": 200})
# 校验验证码
class IsVerificationAPIView(APIView):
permission_classes = (AllowAny,)
def get(self, request):
verification = request.GET.get("verification")
code = request.session.get("code")
print(code)
if verification == code:
return Response({"msg": "ok", "code": 200})
else:
return Response({"msg": "no", "code": 400})
配置子路由user/urls.py
from django.urls import path
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from .views import *
from rest_framework.routers import SimpleRouter, DefaultRouter
from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token
# ... the rest of your URLconf goes here ...
urlpatterns = [
# 定义超链接路由
# re_path('^static/upload/(?P<path>.*)$',serve,{'document_root':'/static/upload/'}),
path('register/', RegisterAPIView.as_view()), # 注册
path('mylogin/', LoginAPIView.as_view()), # 登录自定义token
path('dindin_url/', DingDingAPIView.as_view()), # 钉钉三方登录路由
path('dindin_back/', DingBackApiView.as_view()), # 三方登录回调地址
path('verification/', VerificationAPIView.as_view()), # 发送验证码接口
path('isverification/', IsVerificationAPIView.as_view()), # 判断验证码接口
path('login/', obtain_jwt_token), # 全局token,登录视图 使用djangorestframework-jwt==1.11.0
]
配置主路由django/urls.py
from django.urls import path, re_path, include
urlpatterns = [
path('user/', include('user.urls'))
]
三、前端vue构建登录注册页面
src/http/apis.js
/* eslint-disable */
// 用户登录
import {get, post, put, del} from './index'
export const postRegister = (params, headers) => post("/user/register/", params, headers)
export const getVerification = (params, headers) => get("/user/verification/", params, headers)
export const getisVerification = (params, headers) => get("/user/isverification/", params, headers)
export const postLogin = (params, headers) => post("/user/login/", params, headers)
export const postDinDin = (params, headers) => post("/user/dindin_url/", params, headers)
注册页面src/components/register.vue
<template>
<div>
<center><h1>用户注册</h1></center>
<a-form-item label="用户名" v-bind="formlayout">
<a-input ref="userNameInput" v-model="username" placeholder="Basic usage">
<a-icon slot="prefix" type="user"/>
<a-tooltip slot="suffix" title="Extra information">
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)"/>
</a-tooltip>
</a-input>
</a-form-item>
<a-form-item label="密码" v-bind="formlayout">
<a-input prefix="*" suffix="" v-model="password"/>
</a-form-item>
<a-form-item v-bind="buttonlayout">
<a-button type="primary" @click="submit">注册</a-button>
</a-form-item>
</div>
</template>
<script type="text/javascript">
import {postRegister} from "../http/apis";
export default {
data() {
return {
username: "",
password: "",
//表单样式
formlayout: {
//标签
labelCol: {
xs: {span: 10},
sm: {span: 8}
},
//文本框
wrapperCol: {
xs: {span: 10},
sm: {span: 3}
}
},
//按钮样式
buttonlayout: {
//按钮
wrapperCol: {
xs: {
span: 24,
offset: 0
},
sm: {span: 16, offset: 8}
}
}
}
},
//自定义方法
methods: {
submit: function () {
let params = {
username: this.username,
password: this.password,
}
postRegister(params).then(res => {
console.log(res)
if (res.code == 200) {
this.$router.push('/login')
} else {
alert("失败")
}
}).catch(err => {
console.log(err)
})
}
}
};
</script>
<style type="text/css">
</style>
登录页面src/components/login.vue
<template>
<div>
<center><h1>用户登录</h1></center>
<a-form-item label="用户名" v-bind="formlayout">
<a-input ref="userNameInput" v-model="username" placeholder="Basic usage">
<a-icon slot="prefix" type="user"/>
<a-tooltip slot="suffix" title="Extra information">
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)"/>
</a-tooltip>
</a-input>
</a-form-item>
<a-form-item label="密码" v-bind="formlayout">
<a-input prefix="*" suffix="" v-model="password"/>
</a-form-item>
<a-form-item label="验证码" v-bind="formlayout">
<a-input v-model="verification" @blur="isVerification"/>
<span v-if="isverification">验证码错误</span>
<a-button type="primary" @click="clickVerification">点击发送验证码</a-button>
</a-form-item>
<a-form-item v-bind="buttonlayout">
<a-button type="primary" @click="submit">登录</a-button>
<img style="margin-left:20px;cursor:pointer;" @click="dingding" src="http://localhost:8000/static/dingding.png"/>
</a-form-item>
</div>
</template>
<script type="text/javascript">
import {getisVerification, getVerification, postDinDin, postLogin} from "../http/apis";
export default {
data() {
return {
selected: "",
startdate: "",
dinDinUrl: "",
username: "",
password: "",
verification: "",
isverification: false,
//表单样式
formlayout: {
//标签
labelCol: {
xs: {span: 24},
sm: {span: 8}
},
//文本框
wrapperCol: {
xs: {span: 24},
sm: {span: 3}
}
},
//按钮样式
buttonlayout: {
//按钮
wrapperCol: {
xs: {
span: 24,
offset: 0
},
sm: {span: 16, offset: 8}
}
}
}
},
//自定义方法
methods: {
//判断验证码
isVerification() {
getisVerification({verification: this.verification}).then(res => {
console.log(res)
if (res.code == 200) {
this.isverification = false
} else {
this.isverification = true
}
}).catch(err => {
console.log(err)
})
},
//发送钉钉验证码
clickVerification() {
getVerification().then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
},
//钉钉登录
dingding: function () {
postDinDin().then(res => {
console.log(res)
this.dinDinUrl = res.dindin_url
//打开新窗口跳转到此地址
window.open(res.dindin_url)
})
},
submit: function () {
// let just = true;
// just = this.isVerification() & just;
if (this.isverification === false) {
let params = {
username: this.username,
password: this.password,
}
//登录
postLogin(params).then(res => {
console.log(res)
if (res.token) {
localStorage.setItem("token", res.token)
localStorage.setItem("username", res.username)
localStorage.setItem("id", res.id)
} else {
alert("登录失败")
}
}).catch(err => {
console.log(err)
})
}
};
</script>
<style type="text/css">
</style>
src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import register from '@/components/register'
import login from '@/components/login'
Vue.use(Router)
var routes = [
{
path:'/register',
name:'register',
component:register
},
{
path:'/login',
name:'login',
component:login
},
]
export default new Router({
routes:routes,
mode:'history' /*hash*/
})