1 用户模块梳理
1.1 注册
我们在编写注册模块的时候,尤其是高负载的情况下,尽可能地不读库,会极大地提高我们的性能。可以采用唯一索引。
1.2 登录
在登录的时候,我们要有一个思想,单因子登录,双因子登录和三因子登录
- 单因子登录:传统的验证用户名,密码这种机制不安全(密码为唯一登录,不安全)图片验证码
- 双因子登录:需要东西来验证,识别(短信,邮件)
- 三因子登录:滑块,面部识别,指纹验证
1.3 webhook
- 动向扩展
整体思路就是,通过webhook把验证码发给机器人,机器人在钉钉端告诉用户这个码是多少,用户看到码,把码通过表单形式,然后django端作对比,判断是否登录成功。
2 所遇到的问题
2.1 设置钉钉机器人问题
- 重要的地方(一)---> 加签
- 重要的地方(二)---> 获取webhook
2.2 session问题
-
非常可气,postman调试十分正常,但是浏览器没有效果。
-
根据自己的理解,写了一个比较low的解决方案。
-
由于采取的是session存储,那就意味着,没有办法在获取完验证码,
request.session['key']=value
的情况下,在下一个视图继续获取了。
3 代码实例
3.1 Django端
- settings里无需配置
3.1.1 views.py
from django.contrib.auth.hashers import make_password, check_password
from django.contrib.sessions.models import Session
from rest_framework.response import Response
from rest_framework.views import APIView
from userapp.models import UserModel
from userapp.utils import create_token, message_code, dingding_robot
class RegisterView(APIView):
def post(self, request):
username = request.data.get('username')
password = request.data.get('password')
tel = request.data.get('tel')
try:
user = UserModel.objects.create(username=username, password=make_password(password),tel=tel)
token = create_token(user)
return Response(
{'msg': '注册成功', 'code':200, 'token': token}
)
except Exception as e:
print('异常', e)
return Response(
{'msg': '用户名或手机号重复', 'code':400}
)
class LoginView(APIView):
def post(self, request):
num = request.data.get('num')
username = request.data.get('username')
password = request.data.get('password')
get_key = request.data.get('session_key')
user = UserModel.objects.filter(username=username).first()
if check_password(password, user.password):
session_data = Session.objects.get(pk=get_key).get_decoded()
num_get = session_data.get('num')
if num == str(num_get):
token = create_token(user)
return Response(
{'msg': '登录成功', 'code': 200, 'token': token, 'num':num}
)
return Response(
{'msg': '登录失败', 'code': 400}
)
class DingDingView(APIView):
def get(self, request):
num = message_code()
dingding_robot(num)
request.session['num'] = num
request.session.set_expiry(300)
if not request.session.session_key:
request.session.create()
return Response(
{'msg': '发送成功', 'code':200, 'num':num, 'session_key': request.session.session_key}
)
3.1.2 utils.py
# -*- coding: utf-8 -*-
from rest_framework_jwt.settings import api_settings
def create_token(user):
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return token
def message_code():
import random
num = random.randint(100000, 999999)
return num
def dingding_robot(num):
import time
import hmac
import hashlib
import base64
import urllib.parse
timestamp = str(round(time.time() * 1000))
secret = 'SECc0cc53c0179ded46f9050ff40c8c767f2bd0f50a2e7255c1bc9afa014d246db2'
secret_enc = secret.encode('utf-8')
string_to_sign = '{}
{}'.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_plus(base64.b64encode(hmac_code))
import requests, json # 导入依赖库
headers = {'Content-Type': 'application/json'} # 定义数据类型
webhook = 'https://oapi.dingtalk.com/robot/send?access_token=2c3730563856be4f9dfc1ed8cfa1e1789c6ec88ba4964782529d4b866f503d1a×tamp={}&sign={}'.format(timestamp,sign)
data = {
"msgtype": "text",
"text": {"content": num},
"isAtAll": True}
res = requests.post(webhook, data=json.dumps(data), headers=headers) # 发送post请求
return res
3.2 Vue端
3.2.1 Register.vue
<template>
<div style="margin-top:200px">
<center><h1 style="margin-top:50px">注 册</h1></center>
<div style="margin:0 auto;500px;height:200px">
用户名:
<a-input placeholder="input your username" style="300px" v-model="username"/>
<br>
<br>
密  码:
<a-input-password placeholder="input your password" style="300px" v-model="password"/>
<br>
<br>
手机号:
<a-input placeholder="input your telphone" style="300px" v-model="tel"/>
<br>
<br>
<a-button type="primary" @click="register">注册</a-button>
</div>
</div>
</template>
<script>
import { user_register } from '@/http/apis'
export default {
data() {
return {
username:'',
password:'',
tel:''
}
},
methods: {
register(){
let register_data = {
'username':this.username,
'password': this.password,
'tel':this.tel
}
user_register(register_data).then(res=>{
// 已生成token,可以免登陆直接跳主页的,后续写
res.code == 200?alert('注册成功,即将跳转登录界面')&this.$router.push('/login'):alert('请保证用户名和手机号唯一哦')
})
}
},
created() {
}
}
</script>
<style scoped>
</style>
3.2.2 Login.vue
<template>
<div style="margin-top:200px">
<center><h1 style="margin-top:50px">登 录</h1></center>
<div style="margin:0 auto;500px;height:200px">
用户名:
<a-input placeholder="input your username" style="300px" v-model="username"/>
<br>
<br>
密  码:
<a-input-password placeholder="input your password" style="300px" v-model="password"/>
<br>
<br>
<p>
<a-input type="text" v-model="message_code" style="200px"/> <a-button type="primary" @click="getMessageCode">钉钉获取验证码</a-button>
</p>
<a-button type="primary" @click="login">登录</a-button>
</div>
</div>
</template>
<script>
import { get_message_code, user_login } from '@/http/apis'
export default {
data() {
return {
username:'',
password:'',
message_code:'',
}
},
methods: {
// 获取验证码
getMessageCode(){
get_message_code().then(res=>{
console.log(res)
res.code == 200?alert('获取验证码成功')&sessionStorage.setItem('session_key', res.session_key):alert('失败失败')
})
},
// 登录
login(){
let user_info = {
'username':this.username,
'password':this.password,
'num':this.message_code,
'session_key': sessionStorage.getItem('session_key')
}
user_login(user_info).then(
res=>{
console.log(res)
res.code == 200?alert('登录成功'):alert('登录失败')
}
)
}
},
created() {
}
}
</script>
<style scoped>
</style>