基于阿里egg框架搭建博客3注册与登录

4次阅读

共计 8144 个字符,预计需要花费 21 分钟才能阅读完成。

相关文章

基于阿里 egg 框架搭建博客(1)——开发准备
基于阿里 egg 框架搭建博客(2)——Hello World
基于阿里 egg 框架搭建博客(3)——注册与登录
基于阿里 egg 框架搭建博客(4)——权限控制
基于阿里 egg 框架搭建博客(5)——置顶导航条
基于阿里 egg 框架搭建博客(6)——浏览、发表文章
基于阿里 egg 框架搭建博客(7)——编辑文章

git

https://github.com/ZzzSimon/e…
喜欢就点个赞吧!

正文

俗话说万事开头难,此章节涉及大量的知识点,在每个代码后面都有解释,需要大家查看官方文档。

user 表设计

简单来说,注册与登录就是对 user 表的读写。所以我们首先对 user 表进行设计:

字段说明

字段 解释
id uuid
username 用户名
password 密码
phone 手机号
create_time 创建时间
update_time 更新时间
avatar_url 头像 url

sql 脚本

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (`id` varchar(20) NOT NULL,
  `username` varchar(18) NOT NULL,
  `password` varchar(20) NOT NULL,
  `phone` varchar(11) DEFAULT NULL,
  `create_time` datetime NOT NULL,
  `update_time` datetime NOT NULL,
  `avatar_url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

注册

页面设计


页面设计如上图所示,需要用户输入用户名,密码,手机号,还可以上传自己的头像。

前端代码

官方文档推荐我们使用 nunjucks 作为模板。

使用方法:https://eggjs.org/zh-cn/intro…

我们在 app\view\home 目录下创建 register.tpl 文件:

<html>
<head>
    <title> 注册 </title>
    <link rel="stylesheet" href="/public/bootstrap/css/bootstrap.css">
    <script type="text/javascript" src="/public/js/jquery.min.js"></script>
    <script type="text/javascript" src="/public/bootstrap/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
    <form class="center-block" style="width: 50%;margin-top: 10%"
          method="POST" action="/user/register?_csrf={{ctx.csrf | safe}}" enctype="multipart/form-data">
        <div class="form-group">
            <img src="/public/avatar/default.jpg" id="avatarPic" class="img-circle center-block" style="width: 64px;">
            <input type="file" id="avatarBtn" name="file" style="visibility: hidden">
            <p class="text-center help-block"> 点击头像更改,只支持 jpg,png 格式,大小≤ 200 kb</p>
        </div>
        <div class="form-group">
            <label for="username"> 账号 </label>
            <input type="text" class="form-control" id="username" placeholder="用户名" name="username">
        </div>
        <div class="form-group">
            <label for="password"> 密码 </label>
            <input type="password" class="form-control" id="password" placeholder="密码" name="password">
        </div>
        <div class="form-group">
            <label for="phone"> 手机号 </label>
            <input type="text" class="form-control" id="phone" placeholder="手机号" name="phone">
        </div>
        <div class="form-group">
            <button type="submit" class="btn btn-info pull-right"> 注册 </button>
        </div>
    </form>
</div>
<script>
    $('#avatarPic').bind('click', function () {$('#avatarBtn').click();});
    $('#avatarBtn').bind('change',function (e) {if (window.FileReader) {var reader = new FileReader();
            reader.readAsDataURL(e.target.files[0]);
            // 监听文件读取结束后事件
            reader.onloadend = function (e) {$('#avatarPic').attr("src",e.target.result);    //e.target.result 就是最后的路径地址
            };
        }
    });
</script>
</body>
</html>

有 3 点需要注意:

  1. 静态文件的引用路径/public

官方文档:https://eggjs.org/zh-cn/intro…

bootstrap 自行搜索并下载,也可以用 cdn。

  1. form 表单请求 url 中的 ?_csrf={{ctx.csrf | safe}} 部分

官方文档:https://eggjs.org/zh-cn/core/…

  1. 最后一段 js 代码的作用:

由于 bootstrap 没有 input type=file 标签样式,原版实在丑的不行,于是我把原版的隐藏了,在头像图片的点击事件监听里面,手动触发 input type=file 文件上传按钮的 click 事件。
然后当用户选完头像后,通过 window.FileReader 实时把图片显示出来。

后端代码

HomeController

我们在 app\controller\ 目录下创建 home.js 文件:
该 controller 主要返回主页、登录页、注册页等基础页面。

const Controller = require('egg').Controller;

class HomeController extends Controller {async register(){await this.ctx.render('home/register.tpl')
    }
}
module.exports = HomeController;

UserController

我们在 app\controller\ 目录下创建 user.js 文件:

//app/controller/user.js
const Controller = require('egg').Controller;
const fs = require('mz/fs');

class UserController extends Controller{async register(){
        const ctx = this.ctx;
        const {username, password, phone} = ctx.request.body;
        const avatar = ctx.request.files[0];
        // 默认头像
        let filepathNew = this.config.baseDir+'\\app\\public\\avatar\\default.jpg';
        // 如果用户上传了头像
        if (avatar) {console.log('file:%j', avatar);
            let filenameNew = ctx.helper.uuid() +'.'+  avatar.filename.split('.').pop();
            filepathNew = this.config.baseDir+'\\app\\public\\avatar\\'+filenameNew;
            // 把临时文件剪切到新目录去
            await fs.rename(avatar.filepath, filepathNew);
        }

        const nowTime = new Date();
        const userNew = {id : ctx.helper.uuid(),
            username : username,
            password : password,
            phone : phone,
            avatar_url : filepathNew.split("\\app")[1],
            create_time : nowTime,
            update_time : nowTime
        };
        const flag = await ctx.service.user.save(userNew);
        if (flag){
            // 设置 Session
            ctx.session.user = {username:username};
            ctx.cookies.set('avatarUrl',userNew.avatar_url,{httpOnly:false});
            ctx.body = {
                successFlag:'Y',
                errorMsg:'登录成功!'
            };
            ctx.redirect('/')
        }else {
            ctx.body = {
                successFlag:'N',
                errorMsg:'用户名已存在!'
            }
        }
    }
}

module.exports = UserController;

有 4 点需要注意:

  1. controller 获取上传的文件

官方文档:https://eggjs.org/zh-cn/basic…

  1. cookie 与 session

官方文档:https://eggjs.org/zh-cn/core/…

  1. 重定向

官方文档:https://eggjs.org/zh-cn/basic…

  1. uuid 工具类,helper 的使用

官方文档:https://eggjs.org/zh-cn/basic…

附上我自己实现的 uuid 生成方法:

moment 组件请自行 npm 安装,官方:http://momentjs.cn/

const moment = require('moment');

//uuid 格式:年月日时分秒 3 位毫秒 + 3 位随机数,共 20 位  ===>   20190312162455043167
exports.uuid = function uuid() {let uuid = moment().format("YYYYMMDDHHmmssSSS");
    uuid += (Array(3).join(0) + Math.random()*100).slice(-3);
    return uuid;
};

UserService

我们在 app\service\ 目录下创建 user.js 文件:

const Service = require('egg').Service;

class UserService extends Service {async save(user){const userQ =await this.app.mysql.get('user', {username : user.username});
        if (userQ){return false}else {const result =await this.app.mysql.insert('user', user);
            // 判断插入成功
            const insertSuccess = result.affectedRows === 1;
            if (insertSuccess) {return true}
        }
        return false
    }
}
module.exports = UserService;

有 1 点需要注意:

  1. mysql 的使用

官方文档:https://eggjs.org/zh-cn/tutor…

router.js

我们在 app\ 目录下的 router.js 文件中添加以下内容:

module.exports = app => {const { router, controller} = app;

    // 页面
    router.get('/register.htm', controller.home.register);

    // 接口
    router.post('/user/register',controller.user.register);
};

登录

页面设计

前端代码

我们在 app\view\home 目录下创建 login.tpl 文件:

<html>
<head>
    <title> 登录 </title>
    <link rel="stylesheet" href="/public/bootstrap/css/bootstrap.css">
    <script type="text/javascript" src="/public/js/jquery.min.js"></script>
    <script type="text/javascript" src="/public/bootstrap/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
    <form class="center-block" style="width: 50%;margin-top: 20%"
          method="POST" action="/user/login?_csrf={{ctx.csrf | safe}}" enctype="application/x-www-form-urlencoded">
        <div class="form-group">
            <label for="username"> 账号 </label>
            <input type="text" class="form-control" id="username" placeholder="用户名" name="username">
        </div>
        <div class="form-group">
            <label for="password"> 密码 </label>
            <input type="password" class="form-control" id="password" placeholder="密码" name="password">
        </div>
        <div class="checkbox">
            <label>
                <input type="checkbox" name="rememberMe"> 记住登录状态
            </label>
        </div>
        <div class="form-group">
            <button type="submit" class="btn btn-info pull-right"> 登录 </button>
            <div>
                <a href="/register.htm" class="btn btn-link pull-right"> 注册 </a>
            </div>
        </div>
    </form>
</div>
</body>
</html>

后端代码

HomeController

我们在 app\controller\home.js 文件中添加以下内容:

    async login(){await this.ctx.render('home/login.tpl');
    }

UserController

我们在 app\controller\user.js 文件中添加以下内容:

    async login(){
        const ctx = this.ctx;
        ctx.logger.info('req body:: %j',ctx.request.body);
        const {username, password, rememberMe} = ctx.request.body;
        const user = await ctx.service.user.loginAndGetUser(username, password);
        if (!user){
            ctx.body = {
                successFlag:'N',
                errorMsg:'用户名或密码错误!'
            }
        }else {
            // 设置 Session
            ctx.session.user = {username:user.username};
            ctx.cookies.set('avatarUrl',user.avatar_url,{httpOnly:false});
            // 如果用户勾选了 ` 记住我 `,设置 的过期时间
            if (rememberMe) ctx.session.maxAge = this.config.rememberMe;
            ctx.body = {
                successFlag:'Y',
                errorMsg:'登录成功!'
            };
            ctx.redirect('/')
        }
    }

有 1 点需要注意:

  1. 用户勾选 ’ 记住我 ’ 后的 session 过期时间读取了配置文件中的值。配置:

官方文档:https://eggjs.org/zh-cn/basic…

UserService

我们在 app\service\user.js 文件中添加以下内容:

    async loginAndGetUser(username, password) {const user =await this.app.mysql.get('user', {username : username});
        if (!user || user.password !== password){return false} else {return user;}
    }

router.js

我们在 app\router.js 文件中添加以下内容:

    router.get('/login.htm',controller.home.login);
    router.post('/user/login',controller.user.login);

配置文件参考

config.default.js

参考官方文档后,你的配置文件应该如下(object 对象写法):

const path = require('path');
module.exports = appInfo => {
    return {
        keys: "123456",
        rememberMe : 24 * 60 * 60 * 1000, // 选择记住我之后,session 有效时长
        security : {domainWhiteList:['.127.0.0.1'],  // 安全白名单,以 . 开头
        },
        multipart : {
            mode: 'file',
            tmpdir: path.join(appInfo.baseDir, 'app/public/temp'),
        },
        session : {
            key: 'EGG_SESS',
            maxAge: 10 * 1000, // 单位毫秒
            httpOnly: true,
            encrypt: true,
        },
        view : {
            defaultViewEngine: 'nunjucks',
            mapping: {'.tpl': 'nunjucks',},
        },
        mysql : {
            // 单数据库信息配置
            client: {
                // host
                host: 'localhost',
                // 端口号
                port: '3306',
                // 用户名
                user: '数据库账号',
                // 密码
                password: '数据库密码',
                // 数据库名
                database: '你的数据库名',
            },
            // 是否加载到 app 上,默认开启
            app: true,
            // 是否加载到 agent 上,默认关闭
            agent: false,
        },
    };
};

plugin.js

exports.nunjucks = {
    enable: true,
    package: 'egg-view-nunjucks'
};

exports.mysql = {
    enable: true,
    package: 'egg-mysql',
};

结尾

如果看完觉得有用,请给作者一个喜欢吧!谢谢啦!

正文完
 0