登录与注册的流程

512 阅读4分钟

注册流程

注册的前端:

  • 1.前端的表达验证可以减轻服务器的负载,根据验证是否通过用DOM操作写页面特效
    • 1.1邮箱或者手机号验证注册账号,绑定change事件,判读value值是否符合邮箱或者手机号的正则
    • 1.2密码验证,绑定change事件,判读value值是否符合格式,比如密码必须包含英文大小写和数字,以及验证两次密码需要一致
    • 1.3昵称验证,绑定change事件,判断value值是否符合格式,比如长度和不包含特殊字符等
    • 1.4图片格式的验证以及图片文件的大小验证,然后做图片预览把用户选择的图片显示在页面上
  • 2.给页面的提交按钮绑定点击事件,当用户点击提交按钮时获取用户交互信息
  • 3.把数据用POST请求发送给后端服务器
  • 4.等后端返回的数据,根据返回的数据包的业务码来操作不同的页面,当返回的数据包的业务码是正确的就跳转到登录页面或者直接帮用户登录
<body>
    <input type="text" placeholder="请输入注册的邮箱" id="acount"> <br>
    <input type="password" placeholder="请输入密码" id="pwd"> <br>
    <input type="password" placeholder="请再次输入密码" class="pwd2">
    <span id="pwdtext">两次输入的密码不一致</span>
    <br>
    <input type="text" placeholder="请输入昵称" id="screenname">
    <div class="imgbox">
        <p>请上传头像</p>
        <input type="file" id="touxiang">
    </div>
    <p id="imgtext">图片格式不正确,只支持jpg、jpeg、png格式</p>
    <p id="imgsize">图片大小必须大于10kb以及小于1M</p>
    <button onclick="submit()">注册</button>
    <script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.js"
        type="application/javascript"></script>
    <script>
        //1.前端表单验证
        //数组用于验证每个表单需要都正确才会提交表单数据
        let flag = new Array(4).fill(true);//创建一个长度为5的空数组并替换为5个true
        console.log(flag);
        let acount = document.getElementById("acount");
        acount.addEventListener("change", () => {
            let reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
            if (reg.test(acount.value)) {
                //格式符合,边框变绿
                acount.style.border = "2px green solid";
                flag[0] = true;
            } else {
                acount.style.border = "2px red solid";
                flag[0] = false;
            }
        });
        let pwd = document.getElementById("pwd");
        pwd.addEventListener("change", () => {
            //至少8-16个字符,至少1个大写字母,1个小写字母和1个数字,其他可以是任意字符
            let reg = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,16}$/;
            if (reg.test(pwd.value)) {
                pwd.style.border = "2px green solid";
                flag[1] = true;
            } else {
                pwd.style.border = "2px red solid";
                flag[1] = false;
            }
        });
        let pwd2 = document.querySelector(".pwd2");
        pwd2.addEventListener("change", () => {
            document.querySelector("#pwdtext").style.visibility = "hidden";
            //至少8-16个字符,至少1个大写字母,1个小写字母和1个数字,其他可以是任意字符
            if (pwd.value == pwd2.value) {
                pwd2.style.border = "2px green solid";
                flag[2] = true;
            } else {
                document.querySelector("#pwdtext").style.visibility = "visible";
                pwd2.style.border = "2px red solid";
                flag[2] = false;
            }
        });
        let screenname=document.getElementById("screenname");
        screenname.addEventListener("change",()=>{
            //4-16位字母,数字,汉字,下划线 其中两个汉字是可以注册成功的,表示4个字符
            let reg=/^([\u4e00-\u9fa5]{2,4})|([A-Za-z0-9_]{4,16})|([a-zA-Z0-9_\u4e00-\u9fa5]{3,16})$/;
            if (reg.test(screenname.value)) {
                screenname.style.border = "2px green solid";
                flag[3] = true;
            } else {
                screenname.style.border = "2px red solid";
                flag[3] = false;
            }
        });
        let touxiang=document.getElementById("touxiang");
        touxiang.addEventListener("change",()=>{
            document.querySelector("#imgtext").style.visibility = "hidden";
            document.querySelector("#imgsize").style.visibility = "hidden";
            //当用户选择了一张图片,change事件就会触发
            console.log(touxiang.files);
            let urlobj=window.URL.createObjectURL(touxiang.files[0]);
            document.querySelector(".imgbox").style.backgroundImage=`url("${urlobj}")`;
            //验证图片格式
            let arr=["image/png","image/jpg","image/jpeg"];//用数组保存只允许的图片的格式
            // 数组是否包含指定值,包含返回true,不包含返回false
            if(arr.includes(touxiang.files[0].type)){
                flag[4] = true;
            }else{
                document.querySelector("#imgtext").style.visibility = "visible";
                flag[4] = false;
            }
            //验证图片大小,获取的是字节byte,1024byte=1kb,1M=1024kb
            if(10*1024<touxiang.files[0].size&&touxiang.files[0].size<1*1024*1024){
                flag[4] = true;
            }else{
                document.querySelector("#imgsize").style.visibility = "visible";
                flag[4] = false;
            }
        });
        
        //2.给页面的提交按钮绑定点击事件,当用户点击提交按钮时获取用户交互信息
        async function submit(){
            if(flag.includes(false)){

            }else{
                let fdata=new FormData();
                fdata.append("acount",acount.value);
                fdata.append("password",pwd2.value);
                fdata.append("screenname",screenname.value);
                fdata.append("touxiang",touxiang.files[0]);
                //3.把数据用POST请求发送给后端服务器
                let resquest=await axios.post("/register",fdata);
                // console.log(resquest);
                console.log(resquest.data.code==4001)
                if(resquest.data.code==2001){
                    //注册成功跳转到登陆页面
                    window.location.href="/public/login/login.html";
                }else if(resquest.data.code==4001){
                    alert(resquest.data.info);
                    window.location.href="/public/login/login.html"
                }
            }
        }
        //4.等后端返回的数据,根据返回的数据包的业务码来操作不同的页面

    </script>
</body>

注册的后端:

  • 1.处理接收到的数据,把前端用户上传的文件处理成网络路径的字符串
  • 2.将处理后的数据写入数据库
    • 2.1下载egg-mysql插件并配置
    • 2.2写代码操作MySQL数据库
      • 2.2.1查询邮箱是否注册过
      • 2.2.2插入数据
 async register() {
        //接收POST字符串参数
        let ziduan=this.ctx.request.body;
        let f1=this.ctx.request.files;
        console.log(ziduan,f1);
        //1.处理数据
        for(let i=0;i<f1.length;i++){
            let oldpath=f1[i].filepath;
            let newpath=__dirname+"/../public/userdata/"+path.basename(oldpath);
            if(f1[i]){
                fs.copyFileSync(oldpath,newpath);
                fs.unlink(oldpath,()=>{});
            };
            ziduan[f1[i].field]="/public/userdata/"+path.basename(oldpath);
            console.log(ziduan);
        }
        //2.将数据写入数据库
        //查询账号是否存在,查询返回的是一个数组,
        //如果没有查找到就返回空数组,数据库中找到了几条符合的数据数组中就有几个对象
        let sqlselect=`SELECT * FROM user WHERE acount="${ziduan.acount}"`;
        let sqlselectres=await this.app.mysql.query(sqlselect);
        if(sqlselectres[0]){
            this.ctx.body={info:"账号已注册",code:4001};
        }else{
            //将处理的数据插入数据库
            let create_time=new Date().toLocaleDateString();
            let sqlinsert=`INSERT INTO user (acount,password,screenname,touxiang,create_time) VALUES ("${ziduan.acount}","${ziduan.password}","${ziduan.screenname}","${ziduan.touxiang}","${create_time}")`;
            let sqlinsertres=await this.app.mysql.query(sqlinsert);
            console.log(222222,sqlinsertres,111111);
            this.ctx.body={info:"注册成功",code:2001};
        }
    }

登录的流程

登录的前端:

  • 1.前端表单验证目的是减轻服务器的负载,根据验证是否通过用DOM操作写页面特效
    • 1.1账号验证,账号是邮箱或者手机号,绑定change事件,判读value值是否符合邮箱或者手机号的正则
    • 1.2密码验证,绑定change事件,判读value值是否符合指定的格式
  • 2.用POST请求把账号和密码发送给后端—JWT
  • 3.等后端返回数据,根据返回的数据包的业务码来操作不同的页面。
  • 4.验证码
    • 4.1当前端开始页面渲染时就请求验证码
    • 4.2前端获取用户输入的验证码发送给后端
    • 4.3前端根据收到的业务码来操作页面的业务
<body>
    <style>
        body {
            background-image: url(../image/img-41.jpg);
            background-size: 100%;
            background-repeat: no-repeat;
        }

        .mobanbox {
            width: 100%;
            height: 100%;
            background-color: rgba(202, 239, 247, 0.8);
            position: fixed;
            left: 0px;
            top: 0px;
            display: none;
        }

        .loginbox {
            width: 500px;
            height: 400px;
            margin: 0 auto;
            background-color: darkorchid;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }

        .inputbox {
            width: 200px;
            height: 300px;
            margin: 0 auto;
            background-color: darkorchid;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }
    </style>
    <button class="btn">点击进入登录窗口</button>
    <div class="mobanbox">
        <div class="loginbox">
            <div class="inputbox">
                <input type="text" placeholder="请输入注册的邮箱" id="acount"> <br>
                <input type="password" placeholder="请输入密码" id="pwd"> <br>
                <input type="text" class="verif">
                <div class="yanzhengma"></div>
                <button class="loginbtn">登录</button>
            </div>
        </div>
    </div>
    <script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.js"
    type="application/javascript">
    </script>
    <script>
        //页面以加载就请求验证码
        let ver=document.querySelector(".yanzhengma");
        function verifdata(){
            axios.get("/verif")
        .then((res=>{
            console.log(res);
            //将请求的验证码显示在页面上
            ver.innerHTML=res.data;
        }));
        };
        //页面一加载时自动调用一次,生成一个验证码,之后用户点击可以刷新
        verifdata();
        //没点击一次刷新一次验证码
        ver.onclick=verifdata;
    </script>
    <script>
        let btn = document.querySelector(".btn");
        let mobanbox = document.querySelector(".mobanbox")
        btn.onclick = function () {
            mobanbox.style.display = "block";
        };
        let loginbtn = document.querySelector(".loginbtn");
        loginbtn.onclick = async function () {
            //前端表单验证
            let flag = new Array(2).fill(true);//创建一个长度为2的空数组并替换为2个true
            let acount = document.getElementById("acount");
            acount.addEventListener("change", () => {
                let reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
                if (reg.test(acount.value)) {
                    //格式符合,边框变绿
                    acount.style.border = "2px green solid";
                    flag[0] = true;
                } else {
                    acount.style.border = "2px red solid";
                    flag[0] = false;
                }
            });
            let pwd = document.getElementById("pwd");
            pwd.addEventListener("change", () => {
                //至少8-16个字符,至少1个大写字母,1个小写字母和1个数字,其他可以是任意字符
                let reg = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,16}$/;
                if (reg.test(pwd.value)) {
                    pwd.style.border = "2px green solid";
                    flag[1] = true;
                } else {
                    pwd.style.border = "2px red solid";
                    flag[1] = false;
                }
            });
            let verif=document.querySelector(".verif");
            if(flag.includes(false)){
                alert("账号和密码格式不正确,请重新输入");
                acount.value="";
                pwd.value="";
            }else{
                let resquest=await axios.post("/login",{"acount":acount.value,"password":pwd.value,"verif":verif.value});
                console.log(resquest);
                if(resquest.data.code==2002){
                    window.location.href="/public/home.html";
                }else if(resquest.data.code==4002){
                    alert("账号不存在,请先注册");
                    window.location.href="/public/register/register.html"
                }else if(resquest.data.code==4003){
                    alert("密码错误,请重新输入");
                    pwd.value="";
                    pwd.focus();
                }else if(resquest.data.code==4007){
                    alert(resquest.data.info);
                    verif.focus()
                    verif.value="";
                    verifdata();
                }
            }
        };
    </script>
</body>

登录的后端:

  • 1.生成验证码并储存,然后把验证码发送给前端。
    1. 取出储存的验证码和接收前端发送的验证码以及将两者做比较,根据正确与否决定不同的业务,不正确就发送业务码给前端提示用户验证码输入有误,验证正确就验证前端发送的账号密码和数据库中的数据是否匹配,根据账号密码的匹配再发送不同的业务码给前端
const Controller = require('egg').Controller;
//引入生成验证码的模块
const svgCaptcha = require('svg-captcha');

class LoginController extends Controller {
    async login() {
        //获取前端发送的字段
        let ziduan = this.ctx.request.body;
        console.log(ziduan);
        //1.先验证验证码是否正确,不正确就不执行后面的操作
        //1.1取出储存的验证码信息
        let ver = this.ctx.cookies.get("verif");
        //不相等直接退出该函数,不执行后面的操作
        if (ziduan.verif != ver) {
            this.ctx.body = { info: "验证码输入有误", code: 4007 };
            return;
        }
        //2.查询数据库
        //查询账号
        let sqlselect = `SELECT * FROM user WHERE acount="${ziduan.acount}"`;
        let sqlselres = await this.app.mysql.query(sqlselect);
        //返回的是数组,数组取值取到了,表示数据库中有该账号,没有取到值表示未注册
        if (sqlselres[0]) {
            //账号存在,查询密码并判断前端用户输入的是否一致匹配
            let sqlselpwd = `SELECT * FROM user WHERE acount="${ziduan.acount}" AND password="${ziduan.password}"`;
            let sqlselpwdres = await this.app.mysql.query(sqlselpwd);
            if (sqlselpwdres[0]) {
                //设置登录成功的储存信息cookies
                //在设置cookie时有个对象类型的可选参数,可以对cookie进行相关设置:maxAge:1000*60*60*24*30表示有效时间是30天
                // this.ctx.cookies.set("acount",ziduan.acount,{maxAge:1000*60*60*24*30});
                //session同理,给acount字段储存ziduan.acount的值
                this.ctx.session.acount = ziduan.acount;
                //给uid字段储存从数据库中返回的数组中查询到对象的uid的值
                this.ctx.session.uid = sqlselpwdres[0].uid;
                this.ctx.body = { info: "登陆成功", code: 2002 };
            } else {
                this.ctx.body = { info: "密码错误", code: 4003 };
            }
        } else {
            this.ctx.body = { info: "账号不存在", code: 4002 };
        }
    }
    async getUserInfo() {
        // 1.取出登录成功的储存信息cookies
        // let act=this.ctx.cookies.get("acount");
        //session同理,取出acount字段的值
        //在封装的工具函数中取出登录成功的储存有关用户的信息,返回true表示登录过
        let act = await this.ctx.service.database.islogin();
        //有储存信息才开始获取信息
        if (act) {
            // 2.获取当前请求的用户信息,根据登录成功的储存信息cookies从数据库中获取当前请求的用户信息
            let acount = this.ctx.session.acount;
            let sql1 = `SELECT touxiang,screenname FROM user WHERE acount="${acount}"`;
            let sql1res = await this.app.mysql.query(sql1);
            this.ctx.body = { info: "请求成功", code: 2004, ...sql1res[0] }
        } else {
            //没有储存信息表示未登录过
            this.ctx.body = { info: "未登录", code: 4004 };
        }
    }
    async verif() {
        //1.生成验证码
        let captcha = svgCaptcha.create({
            size: 4,
            noise: 1,
            color: true,
            background: "Gold"
        });
        //2.设置储存验证码的信息
        this.ctx.cookies.set("verif", captcha.text);
        //3.发送给前端
        this.ctx.body = captcha.data;
    }
    async loginout() {
        this.ctx.session.acount=null;
        this.ctx.session.uid=null; 
        this.ctx.body={info:"退出登录成功"}
    }
}

module.exports = LoginController;