当前位置:
首页
文章
后端
详情

云函数手撸用户体系

使用云函数实现用户系统 数据库为腾讯云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不携带

云函数手撸用户体系

免责申明:本站发布的内容(图片、视频和文字)以转载和分享为主,文章观点不代表本站立场,如涉及侵权请联系站长邮箱:xbc-online@qq.com进行反馈,一经查实,将立刻删除涉嫌侵权内容。

同类热门文章

深入了解C++中的new操作符:使用具体实例学习

C++中的new操作符是动态分配内存的主要手段之一。在程序运行时,我们可能需要动态地创建和销毁对象,而new就是为此提供了便利。但是,使用new也常常会引发一些问题,如内存泄漏、空指针等等。因此,本文将通过具体的示例,深入介绍C++中的new操作符,帮助读者更好地掌握其使用。


深入了解C++中的new操作符:使用具体实例学习

怎么用Java反射获取包下所有类? 详细代码实例操作

Java的反射机制就是在运行状态下,对于任何一个类,它能知道这个类的所有属性和方法;对于任何一个对象,都能调用这个对象的任意一个方法。本篇文章将通过具体的代码示例,展示如何通过Java反射来获取包下的所有类。


怎么用Java反射获取包下所有类? 详细代码实例操作

员工线上学习考试系统

有点播,直播,在线支付,三级分销等功能,可以对学员学习情况的监督监控,有源码,可二次开发。支持外网和局域网私有化部署,经过测试源码完整可用!1、视频点播:视频播放,图文资料,课件下载,章节试学,限时免

员工线上学习考试系统

了解Java中的volati关键字的作用 以及具体使用方法

本篇文章将和大家分享一下Java当中的volatile关键字,下面将为各位小伙伴讲述volatile关键字的作用以及它的具体使用方法。


了解Java中的volati关键字的作用 以及具体使用方法

Java Map 所有的值转为String类型

可以使用 Java 8 中的 Map.replaceAll() 方法将所有的值转为 String 类型: 上面的代码会将 map 中所有的值都转为 String 类型。 HashMap 是 Java

Java Map 所有的值转为String类型