django+vue实现钉钉群机器人验证码

又是忙碌的一天,今天给大家带来的教程是使用钉钉群机器人发送随机验证码

在vue中输入然后在后端进行判断并返回相应的返回值,首先我们去看官方文档

然后开始操作
我们需要django+vue跨域 点击查看

一、创建钉钉群机器人

首先明确一点,钉钉自定义机器人早就不支持在手机端创建了,所以打开你的pc端或者mac端的钉钉客户端,在需要机器人的聊天群界面,点击智能群助手

钉钉java发送验证码 钉钉 验证码_jwt


钉钉java发送验证码 钉钉 验证码_jwt_02

钉钉java发送验证码 钉钉 验证码_django_03


钉钉java发送验证码 钉钉 验证码_钉钉java发送验证码_04


钉钉java发送验证码 钉钉 验证码_jwt_05

需要注意的是,在安全设置一栏里,我们选择加签的方式来验证,在此说明一下,钉钉机器人的安全策略有三种,第一种是使用关键字,就是说你推送的消息里必须包含你创建机器人时定义的关键字,如果不包含就推送不了消息,第二种就是使用加密签名,第三种是定义几个ip源,非这些源的请求会被拒绝,综合来看还是第二种又安全又灵活。

钉钉java发送验证码 钉钉 验证码_钉钉java发送验证码_06

创建成功后,系统会分配给你一个webhook地址,这个地址需要保存一下,地址中有唯一的accesstoken

钉钉java发送验证码 钉钉 验证码_钉钉java发送验证码_07


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构建登录注册页面

钉钉java发送验证码 钉钉 验证码_django_08


钉钉java发送验证码 钉钉 验证码_axios_09

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