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*/ })