云函数手撸用户体系

代码哈士奇
• 阅读 1424

使用云函数实现用户系统 数据库为腾讯云TDSQL 其它服务商云函数 通用 只需修改index.js返回参数即可 主要有用户注册 用户登陆 邮箱发送验证码 邮箱验证码校检 邮箱绑定 邮箱解绑 邮箱验证码登陆 生成token 校验token 其它功能可以在此基础上拓展 纯手撸代码 云函数环境为nodejs12.13 由于我比较穷 就不带大家使用短信服务了 短信发送验证码和邮箱验证码逻辑差不多


主要为 安装并且依赖包 配置邮箱服务 配置数据库连接 封装用户模块 调用封装的用户模块 用户模块为主要

以下操作 在本地执行

下载依赖包

npm install dmhsq-mysql-pool 操作数据库 npm install nodemailer 邮件发送服务 npm install js-md5 md5加密

操作数据库以及邮件发送详情可以看 华为函数工作流云函数操作云MySQL数据库实现邮箱验证码发送以及校验 使用华为云函数实现邮件发送

目前目录结构为 云函数手撸用户体系 其中index.js是云函数入口文件

配置邮箱服务(封装邮箱模块)

需要拿到SMTP的授权码 具体为找到邮箱设置 云函数手撸用户体系 之前的文章已经配置过 我们直接上代码 由于目前邮箱只负责发验证码 我就把验证码发送直接写成固定的了 其中 code为验证码 time为有效时间

新建email.js

const nodemailer = require('nodemailer')

const transporter = nodemailer.createTransport({
    service: 'xx', // qq,126等等.
    auth: {
        user: 'xxxxx@xxx.com',
        pass: 'xxxx'
    }
});

const sendCode = (code,time) => {
    let message = {
        from: "验证码<xxxx@xx.com>",
        to: email,
        subject: "验证码服务",
        html: `<html>
                    <head>
                        <meta charset="utf-8">
                        <title></title>
                    </head>
                    <body>
                        <div>
                            <p style="font-size: 20px;">欢迎您使用,您的验证码为 
                            <span style="color: blue;font-size: 30px;font-weight: 800;">${code}</span> ,有效时间为${time/60}分钟, 请勿泄露并及时验证</p>

                            <div style="margin-top: 50px;"></div>
                            <p style="color: red;font-size: 25px;">请勿回复</p>
                        </div>
                    </body>
                </html>`
    };
   await transporter.sendMail(message)
    return {
            code:0,
            msg:"邮件已发送,如果没有收到,请检查邮箱"
    }
}

module.exports = {sendCode};

配置数据连接

新建db.js

引入dmhsq-mysql-pool并配置

const database = require("dmhsq-mysql-pool");
const configs = {
    host: 'xxxxx',
    port: 'xxxxx',
    user: 'xxxx',
    password: 'xxxxx',
    database: "xxxx"
}
let user = new database(configs).table("user")
let codes = new database(configs).table("email")
module.exports = {
    user,
    codes
};

数据库为腾讯云TDSQL 这里使用简单的数据表 用户表如下 云函数手撸用户体系

验证码表如下 云函数手撸用户体系

编写用户管理模块

新建user.js 引入验证码发送以及数据库操作模块

const {user,codes} = require("./db.js");
const sendCode = require("./email.js");
const md5 = require("js-md5")

注册模块

逻辑如下 需要用户名和密码 注册时 密码会加密一次 存入数据库 注册成功会自动登录并返回 token token过期时间

const sign = async (username, password) => {
    const dfp = password
    password = md5(password);
    let isH = await user.where({username}).get();
    if(isH.data.length>0){
        return {
            code: 5742,
            msg: "用户名已被占用",
        }
    }
    const _id = md5(Math.random().toString(36)).substr(0, 10);
    let res = await user.add({
        username,
        password,
        _id
    }).get();
    let rsp = {
        code: 5741,
        msg: "注册失败",
    }
    if (res.code == 0) {
        let userRes = await login(username, dfp);
        rsp = {
            code: 0,
            msg: "注册成功"
        }
        if (userRes.code == 0) {
            rsp.data = userRes.userInfo
        }
    }
    return rsp;
}

登录模块

逻辑如下 如果用户密码输入正确 会生成一个token 以及token过期时间 以及最后一次登录时间 也就是本次登录的时间 入库 登录成功返回 token token过期时间

const login = async (username, password) => {
    password = md5(password)

    let res = await user.where({
        username,
        password
    }).get()
    if (!res.data.length) {
        return {
            code: 9001,
            msg: "用户名或者密码错误"
        }
    } else {
        let token = md5(md5(Math.random().toString(36)) + md5(Math.random().toString(36)));
        const tokenExpired = parseInt(Date.parse(new Date()).toString().substr(0, 10)) + 72000;
        const last_login_time = parseInt(Date.parse(new Date()).toString().substr(0, 10));
        let qres = await user.updata({
            token_expired: tokenExpired,
            token,
            last_login_time
        }).where({username}).get();
        if (qres.code == 0) {
            return {
                code: 0,
                userInfo: {
                    token,
                    tokenExpired,
                    username
                }
            }
        } else {
            return {
                code: 9002,
                msg: "登陆失败",
                data: qres
            }
        }

    }
}

token校检

逻辑如下 通过token可以获取对应用户的用户信息

//token校检
const checkToken = async (token) =>{
    const reqs = await user.where({token}).get();
    let res = {}
    if(reqs.data.length>0){
        const userInfos = reqs.data[0]
        const check_time = userInfos.token_expired;
        const now_time = parseInt(Date.parse(new Date()).toString().substr(0, 10));
        if(check_time>now_time){
            res = {
                code:0,
                userInfo:{
                    username:userInfos.username
                }
            }
        }else{
            res = {
                code:7412,
                msg:"token过期"
            }
        }
    }else{
        res = {code:7417,msg:"token非法"}
    }
    return res;
}

邮箱发送验证码

逻辑如下 如果邮箱发送成功 则会生成验证码入库 可以发送登录或者绑定或者解除绑定验证码 也可以自定义类型

const sendEmailCode = async (email,type) => {
    const randomStr = '00000' + Math.floor(Math.random() * 1000000)
    const code = randomStr.substring(randomStr.length - 6);
    let time = 3600
    const check_time = parseInt(Date.parse(new Date()).toString().substr(0, 10)) + time;
    let res = {}
    res = await sendCode(email, code, time)
    if (res.code == 0) {
        await codes.add({
            email,
            code,
            check_time,
            state: 0,
            type
        }).get();
    } else {
        res = {
            code: 4046,
            msg: "发送失败"
        }
    }
    return res
}

邮箱验证码校验

逻辑如下 邮箱验证码 类型均正确为通过

const checkCode = async (email,code,type) =>{
    let result = await codes.where({
        email,
        code,
        type
    }).sort({
        check_time: "DESC"
    }).get();
    let data = result.data;
    let res = {}
    if (data.length == 0) {
        res = {
            code: 4048,
            msg: "验证码错误"
        }
    } else {
        data = data[0]
        const check_times = parseInt(Date.parse(new Date()).toString().substr(0, 10));
        if (data.state == 0 & data.check_time > check_times) {
            await codes.updata({
                state: 1
            }).where({
                email
            }).get()
            res = {
                code: 0,
                msg: "验证通过"
            }
        } else if (data.check_time < check_times) {
            res = {
                code: 4044,
                msg: "验证码失效"
            }
        } else if (data.state == 1) {
            res = {
                code: 4045,
                msg: "验证码已经验证"
            }
        } else {
            res = {
                code: 4048,
                msg: "验证码错误"
            }
        }
    }
    return res;
}

邮箱绑定

进行此步骤之前需要校检token获取username

需要用户名 邮箱 以及验证码 如果用户已经绑定 就告知已经绑定 否则 绑定

const bind = async (username,email,code) => {
    const check_code = await checkCode(email,code,"bind");
    const check_user = await user.where({username}).get();
    let res = {}
    if(check_user.data.length>0){
        res = {
            code:74174,
            msg:"用户已经绑定邮箱"
        }
    }else{
        if(check_code.code==0){
            const datas = await user.updata({email}).where({username}).get();
            if(datas.code==0){
                res = {
                    code:0,
                    msg:"绑定成功"
                }
            }
        }else{
            res = check_code
        }
    }
    return res;
}

邮箱解除绑定

进行此步骤之前需要校检token获取username

需要用户名 邮箱 以及验证码 如果用户还未绑定 就告知还未绑定 否则 解除绑定

const unbind = async (username,email,code) => {
    const check_code = await checkCode(email,code,"bind");
    const check_user = await user.where({username,email}).get();
    let res = {}
    if(check_user.data.length==0){
        res = {
            code:74175,
            msg:"用户还未绑定邮箱"
        }
    }else{
        if(check_code.code==0){
            const datas = await user.updata({email:""}).where({username}).get();
            if(datas.code==0){
                res = {
                    code:0,
                    msg:"解除绑定成功"
                }
            }
        }else{
            res = check_code
        }
    }
    return res;
}

邮箱验证码校检登录

逻辑如下 根据验证码 邮箱 以及验证码类型查询数据库 如果数据库 存在符合数据 且状态为0则验证通过 验证通过则生成token token过期时间 最后一次登录时间入库 返回 token token过期时间 email

const checkCode = async (email, code) => {
    const ress = await checkCode(email,code,"login")
    const isH = await user.where({email}).get();
    if(isH.data.length==0){
        return {
            code:9003,
            msg:"非法邮箱(邮箱未绑定用户)"
        }
    }
    if(ress.code==0){
        let token = md5(md5(Math.random().toString(36)) + md5(Math.random().toString(36)));
        const tokenExpired = parseInt(Date.parse(new Date()).toString().substr(0, 10)) + 72000;
        const last_login_time = parseInt(Date.parse(new Date()).toString().substr(0, 10));
        let qres = await user.updata({
            token_expired: tokenExpired,
            token,
            last_login_time
        }).where({email}).get();
        if (qres.code == 0) {
            res = {
                code: 0,
                userInfo: {
                    token,
                    tokenExpired,
                    email
                }
            }
        } else {
            res = {
                code: 9002,
                msg: "登陆失败",
                data: qres
            }
        }
    }
    return res;
}

完成后 导出模块

module.exports = {
    sign,
    login,
    sendEmailCode,
    checkCode,
    bind,
    unbind,
    checkCodeLogin,
    checkTokenk
}

使用

在index.js引入

const userCenter = require("./user.js")

exports.handler = async (event, context ,callback) => {

    let noCheckAction = ['sign', 'checkToken', 'login', 'checkCode', 'loginByEmail', 'emailCode']
    let params = event.queryStringParameters;
    let res = {}
    const {
        action
    } = params
    if (noCheckAction.indexOf(action) === -1) {
        if (!params.token) {
            res = {
                code: 401,
                msg: '缺少token'
            }
            const output = {
                'statusCode': 200,
                'headers': {
                    'Content-Type': 'application/json'
                },
                'isBase64Encoded': false,
                'body': JSON.stringify(res),
            }
            callback(null, output);
        }else{
            let datas = await userCenter.checkToken(params.token)
            if (datas.code != 0) {
                res = datas
                const output = {
                    'statusCode': 200,
                    'headers': {
                        'Content-Type': 'application/json'
                    },
                    'isBase64Encoded': false,
                    'body': JSON.stringify(res),
                }
                callback(null, output);
            }else{
                params.username = datas.userInfo.username;
            }
        }

    }
    switch (action) {
        case "sign": {
            const {
                username,
                password
            } = params;
            res = await userCenter.sign(username, password);
            break;
        }
        case "login": {
            const {
                username,
                password
            } = params;
            res = await userCenter.login(username, password)
            break;
        }
        case "emailCode": {
            const {
                email,
                type
            } = params;
            res = await userCenter.sendEmailCode(email, type)
            break;
        }
        case "checkCode": {
            const {
                email,
                code,
                type
            } = params;
            res = await userCenter.checkCode(email, code, type)
            break;
        }
        case "bind": {
            const {
                username,
                email,
                code
            } = params;
            res = await userCenter.bind(username, email, code)
            break;
        }
        case "unbind": {
            const {
                username,
                email,
                code
            } = params;
            res = await userCenter.unbind(username, email, code)
            break;
        }
        case "loginByEmail": {
            const {
                email,
                code
            } = params;
            res = await userCenter.checkCodeLogin(email, code)
            break;
        }
        case "checkToken": {
            const {
                token
            } = params;
            res = await userCenter.checkToken(token)
            break;
        }
        default: {
            res = {
                code: 403,
                msg: "非法访问"
            };
            break;
        }
    }


    const output = {
        'statusCode': 200,
        'headers': {
            'Content-Type': 'application/json'
        },
        'isBase64Encoded': false,
        'body': JSON.stringify(res),
    }
    callback(null, output);
}

上传代码

将整个目录文件打成zip压缩包 如下

云函数手撸用户体系

云函数手撸用户体系

创建云函数的时候选择上传代码 或者创建完选择也可以

创建触发器

云函数手撸用户体系

测试

注册

注册成功自动登录返回用户 token token过期时间

云函数手撸用户体系

注册时 用户名已被占用 云函数手撸用户体系

登录

登录成功返回用户 token token过期时间 云函数手撸用户体系

用户名或者密码错误 云函数手撸用户体系

绑定邮箱

获取邮箱验证码 云函数手撸用户体系

云函数手撸用户体系

绑定前 云函数手撸用户体系

绑定后 云函数手撸用户体系

云函数手撸用户体系

绑定失败 云函数手撸用户体系

解除绑定

云函数手撸用户体系

云函数手撸用户体系

解除绑定失败

云函数手撸用户体系

邮箱验证码验证失败

云函数手撸用户体系

云函数手撸用户体系

云函数手撸用户体系

邮箱验证码登录

通过邮箱登录 不会返回用户名 会返回邮箱

云函数手撸用户体系

云函数手撸用户体系

获取用户信息

通过checkToken

云函数手撸用户体系

如果请求action不在switch case中

云函数手撸用户体系

如果token不正确

云函数手撸用户体系

如果token不携带

云函数手撸用户体系

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
4cast
4castpackageloadcsv.KumarAwanish发布:2020122117:43:04.501348作者:KumarAwanish作者邮箱:awanish00@gmail.com首页:
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
HTML5新增input标签属性
一.inputtype属性1<formaction""2邮箱<inputtype"email"name""id""<inputtype"submit"value"提交"<br/<br/3手机号码<inputtype"tel"name
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这