首页 > 编程语言 >微信小程序-手机验证码短信登录接口(防薅方法)

微信小程序-手机验证码短信登录接口(防薅方法)

时间:2024-08-07 15:26:57浏览次数:11  
标签:openid code const cryptoKey 微信 验证码 接口 return CryptoJS

前言

  • 开发的小程序手机号短信验证码登录这一个功能,入参只有手机号。

结局

  • 盗刷、 恶意刷。

解决方案

1. nginx

只允许referer是小程序来源的请求

# 你的接口
location ^~ /api/
{
    if ($http_referer !~* "^https://servicewechat.com/【你的小程序appid】/\d+/page-frame.html$") {
        return 444;
}

2. 利用小程序code来进行验证

  1. 利用小程序code,在后端静默获取openid,根据openid结合当前时间戳,生成加密串返回给后端。
  2. 前端请求短信接口时需要将openid和生成的加密串传给后端。
  3. 后端解析加密串后,判断解密后的openid是否一致、时间戳是否有效。

加密和解密过程都在后端进行,避免微信小程序被抓包反编译后能够找到加密方法。

伪代码实现逻辑

  • 前端
export default {
	mounted() {
		wx.login({
			success: function(res) {
				const {
					code
				} = res
				if (code) {
					// 后端code获取openid接口
					codeToOpenid({
						code
					}).then(data => {
						if (data) {
							const { openid, cryptoKey } = data
							that.openid = data.openid
							that.cryptoKey = data.cryptoKey
						}
					})
				} else {
					console.log('登录失败!' + res.errMsg)
				}
			}
		});
	},
	methods: {
		// 获取验证码
		getCode() {
				const checkPhone = /^1[3-9]\d{9}$/.test(this.phone)
				if (!checkPhone) {
					uni.showToast({
						icon: 'none',
						title: '请输入正确的手机号码'
					})
					return;
				}
				wx.showLoading({
					title: '正在获取验证码'
				})
				// 获取手机号验证码接口
				getPhoneCode({
					phone: this.phone,
					openid: this.openid,
					cryptoKey: this.cryptoKey,
				}).then((code) => {
						if (code) {
							uni.showToast({
							title: '验证码获取成功'
						})
					}
				})

			}
	}

}
  • 后端

const rp = require('request-promise');
const CryptoJS = require("crypto-js");

const key = 'xxxxxxxxx'
const iv = '1234567887654321'

// 加密
export function Encrypt(text) {
    return CryptoJS.AES.encrypt(text, CryptoJS.enc.Utf8.parse(key), {
        iv: CryptoJS.enc.Utf8.parse(iv),
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    }).toString()
}

// 解密
export function Decrypt(text) {
    let decrypted = CryptoJS.AES.decrypt(text, CryptoJS.enc.Utf8.parse(key), {
        iv: CryptoJS.enc.Utf8.parse(iv),
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    })
    return decrypted.toString(CryptoJS.enc.Utf8)
}

module.exports = {
	async getOpenIdByCode(code) {
        const secret = "xxxxxxx"
        const apppid = "xxxxxxx"
        const options = {
            method: 'GET',
            url: 'https://api.weixin.qq.com/sns/jscode2session',
            qs: {
                grant_type: 'authorization_code',
                js_code: code,
                secret,
                appid,
            }
        };
        return new Promise(async (resolve, reject) => {
            try {
                // 获取openid
                const res = await rp(options);
                const sessionData = JSON.parse(res);
                if (sessionData.openid) {
                    resolve(sessionData)
                } else {
                    throw sessionData;
                }
            } catch (e) {
                reject(e)
            }
        })

    }
	
	// 后端code获取openid接口逻辑
	async codeToOpenid() {
	        const code = this.post('code');
	        let openid;
	        try {
	        	// 通过微信接口使用code获取openid
	            const { openid } = await this.getOpenIdByCode(code);
	            // 返回成功结果
	            return this.success({
	                openid,
	                cryptoKey: Encrypt(`${Date.now()}:${openid}`) // 加密内容为  当前时间戳:openid
	            });
	
	        } catch (e) {
	        	// 返回失败结果
	            return this.fail(500, 'openid获取失败', e);
	        }
	    }
	
	// 获取手机号验证码
	async getPhoneCode() {
        const openid = this.post('openid')
        const cryptoKey = this.post('cryptoKey')
        const phone = this.post('phone')
        // 校验解密的cryptoKey是否和openid一致
        const decryptStr = Decrypt(cryptoKey)
        const [timestamp, decryptOpenid] = decryptStr.split(':')
        if(decryptOpenid !== openid) {
            return this.fail('无效凭证!')
        }
        const now = Date.now()
        // 5min有效期
        if( timestamp > now ||  (now - timestamp > 5 * 60 * 1000)) {
            return this.fail('凭证过期')
        }

        const oldCode = await this.cache(phone); // 判断是否redis中已存在该手机号的验证码
        if (oldCode) {
            return this.fail('60秒内不可重复获取')
        }
        
        let code = randomCode() // 随机生成验证码
        
         const smsSerivce = this.service('sms'); // 短信服务
         const msg = { code }
         try {
             const res = await smsSerivce.send(phone, msg) // 发送短信验证码
         } catch (e) {
             return this.fail('短信发送失败')
         }

        // 存储redis中1分钟,避免重复请求
        await this.cache(phone, code, { timeout: 60 * 1000 })
        return this.success(code) // 返回验证码给前端

    }

}

标签:openid,code,const,cryptoKey,微信,验证码,接口,return,CryptoJS
From: https://blog.csdn.net/weixin_41895625/article/details/140991520

相关文章

  • 2024美团官方霸王餐API接口
    在数字化日益深入的今天,餐饮行业正经历着一场前所未有的变革。作为行业内的领军企业,美团不断推出创新服务以优化用户体验,提升商家运营效率。其中,2024年美团官方推出的霸王餐API接口便是这一趋势下的重要产物。本文将从接口的背景、功能、优势、应用场景以及未来展望等方面,深入剖析......
  • 接口
    接口的概念:接口是一个全部由抽象方法组成的集合,接口需要用interface定义,里面只能有抽象的方法和常量。接口体现的是事物扩展的功能,在Java中,类定义了一个实体,包括实体的属性,实体的行为。而接口定义了一个实体可能发生的动作,只有一个声明,没有具体的行为。当一个方法在很多......
  • Java SE核心技术——10接口
    接口就是一种规范或标准,只要符合这个标准都可以通用,接口的表现在于对行为的抽象。就好比插座,每个厂商生产的插座我们都可以用,这是因为他们有同一个标准,这里插座就是接口。//接口的定义格式与定义类的格式基本相同,将class关键字换成interface关键字,就定义了一个接口public......
  • 微信电脑版v3.9.11.17 防撤回版 多开版
    版本特色:1、看到对方撤回的消息2、多账号可正常登录修改原理,如下图:使用说明:解压后,双击start_Wechat.exe来运行软件下载地址:Wechat防撤回版v3.9解压密码:helloh下载时可能会有广告,忽略,等下载结束即可部分杀软会因该版本软件未购买签名证书(如下图)而阻止运行,可通过暂时......
  • Java计算机毕业设计个人博客微信小程序演示录像220239(开题报告+源码+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着互联网技术的飞速发展和智能手机的普及,个人博客作为一种重要的网络表达和信息分享方式,逐渐从传统的PC端向移动端延伸。微信小程序作为一种轻量级......
  • 毕业设计:基于微信小程序的校篮球联赛系统【代码+论文+PPT】
    全文内容包括:1、采用技术;2、系统功能;3、系统截图;4、配套内容。索取方式见文末微信号,欢迎关注收藏!一、采用技术语言:Java1.8框架:SpringBoot数据库:MySQL5.7、8.0开发工具:IntelliJIDEA旗舰版、微信开发工具其他:Maven3.8以上二、系统功能球队管理:负责添加、编辑、删除和查询......
  • uniapp 在微信H5、安卓、IOS预览PDF
    前言uniapp在微信内通过链接预览PDF,而现有uniapp插件都是通过在线网站解析例如:mumu-previewOffce但解析网站目前已经不能访问准备工作下载PDF.JShttps://github.com/mozilla/pdf.js/releases/download/v2.4.456/pdfjs-2.4.456-dist.zip放置在uniapp根目录static下并自......
  • Spring Boot 统一接口响应格式的正确姿势
    01、背景介绍熟悉web系统开发的同学可能比较熟悉,目前绝大多数的互联网软件平台基本都是前后端分离的开发模式,为了加快前后端接口对接速度,一套完善且规范的接口标准格式是非常有必要的,不仅能够提升开发效率,也会让代码看起来更加简洁、好维护。今天这篇文章,我们一起来学习一......
  • SAP PO接口报文保存到内表中
    有一个需求,接收PI传数据过来后,除整理数据保存到用户数据表外,在后期还需要把报文加工一下(要求报文内容与原接收报文一致,只是补加少量值),在发送到其它的系统。由于数据接收后已对数据加工到了用户表,已没有原报文内容(比如金额已累计),如从用户数据表中再取出数据整理加入发送出去,一是......
  • 从零开始开发微信小程序-资源准备
            工欲善其事,必先利其器。在完成小程序的申请之后,接下来就要着手准备开发环境和服务器的相关资源了。本文主要聚焦于域名备案以及SSL证书的购置流程;如果您对这些方面相当了解,可以直接跳过这部分内容。        1、服务器资源        1.1服务器......