如何用Eggjs从零开始开发一个项目(2)

在上一篇文章,我们已经使用Sequelize连接上了数据库,并能进行简单的数据库操作,在此基础上,我们试着来开发一个完整的项目。这篇文章我们从用户的注册、登录着手,试着开发用户模块的相关的代码。

用户注册

1. 注册逻辑

用户注册的逻辑很简单:

  • 客户端:用户输入输入账号,密码等信息进行用户注册;
  • 服务端:接收到客户端提交的注册信息后,进行字段的检验(是否必填、字段长度等),字段符合要求后,根据用户注册的账号查询数据库,根据返回结果判断该用户是否是新用户,如果是新用户,将用户信息写入到数据库,完成注册流程。

2. 用户密码处理

客户端用户提交数据后,服务端验证通过进行数据库写入,但是其中用户密码是敏感信息,为了服务安全考虑,不能直接将明文密码写入到数据库,防止数据库被攻击,用户密码泄露。所以一般在存储用户密码时,会先对用户密码进行加盐加密处理,这样哪怕数据库存储的密码泄露,其他人也无法通过处理后的密码进行登录。这里使用哈希算法对密码进行加密,因为哈希的特性是不可逆,具体的细节可以参考 为什么说 MD5 是不可逆的?
。说明:哈希可以被暴力破解,加盐可以很大程度上增加破解难度。

废话不多说,我们下面来写代码。

首先,我们安装一下 bcryptjs
,我们使用它对密码进行加盐加密和比对:

npm install bcryptjs --save

然后我们把这两个方法写到 app/extend/helper.js

const bcrypt = require('bcryptjs');
module.exports = {
encrypt(password) {
const salt = bcrypt.genSaltSync(5);    //加盐
const hash = bcrypt.hashSync(password, salt);    //哈希(同步调用)
return hash;
},
compare(password, hash) {
return bcrypt.compareSync(password, hash);    //比对
}
};

这样我们在项目里就可以通过 this.ctx.helop.encrypt
this.ctx.helop.compare
的方式去使用这些公用的方法了。

然后,在 UserController
中添加一个 register
方法:

async register() {
const params = this.ctx.request.body;
// 参数校验
if (!params.name || !params.password || !params.phone || !params.email) {
this.ctx.body = {
code: '500',
msg: '参数不合法'
};
}
// 查询该用户是否已经注册
const user = await this.ctx.model.User.findOne({ where: {
name: params.name
} });
if (user) {
this.ctx.body = {
code: '500',
msg: '该用户已存在'
};
}
// 插入数据库
const result = await this.ctx.model.User.create({
...params,
password: this.ctx.helper.encrypt(params.password)
});
if (result) {
this.ctx.body = {
code: '200',
msg: '注册成功'
};
}
}

最后,添加路由:

// app/router.js
router.post('/register', controller.user.register);

3.功能测试

测试一下,用postman创建一个post请求到 localhost:7001/register
,传入注册用户信息:

{
"name":"xiaoming",
"password":"test1234",
"phone":"13412341234",
"email":"test@gmai.com"
}

服务端返回“注册成功”的提示语,这时候我们去数据库就能看到这条数据了,而且密码是一坨看不懂的密文,这个时候我们用同样的数据再次发起请求,服务端返回“该用户已注册”,说明我们的注册功能已经完成了!

用户登录

用户登录的逻辑很简单,就是一个客户端传入的用户名密码与服务器存储的相比较,匹配就登录成功,不然就是用户名密码错误。但是,我们还需要额外考虑一个问题,http请求是无状态的,那我们怎么去记住用户的登录状态和登录信息呢,并且每次向服务端发起请求都带上这些信息呢?

常用的用户认证方式有两种,一种是通过Cookie实现,一种是Token的实现方式。关于用户授权认证可以看看这篇文章: 授权认证登录之 Cookie、Session、Token、JWT 详解
,Eggjs官网也有一个章节讲了Cookie和Session相关的知识 Cookie 与 Session
,学习服务端,掌握这些知识还是很必要的。

在这里,我们选择JWT作为我们的解决方案。关于JWT,这里提供两篇文章作为参考, JSON Web Token 入门教程
Introduction to JSON Web Tokens
。OK,原理看完以后我们来写代码。

首先我们安装 egg-jwt
插件:

npm install egg-jwt --save

引入插件:

// app/config/plugin.js
jwt: {
enable: true,
package: "egg-jwt"
}

配置jwt secret:

// app/config/config.default.js
config.jwt = {
secret: '12312456
};

OK,我们下面编写代码的代码:

// app/controller/user.js
async login() {
const params = this.ctx.request.body;
if (!params.name || !params.password) {
this.ctx.body = {
code: '500',
msg: '参数不合法'
}
}
const user = await this.ctx.model.User.findOne({ where: {
name: params.name
} });
if (!user) {
this.ctx.body= {
code: '500',
msg: '用户名密码错误'
}
}
//校验密码
const checkPassword = this.ctx.helper.compare(
params.password,
user.password
);
if (checkPassword) {
// 根据用户名称创建token,过期时间为2小时
const token = this.app.jwt.sign({
name: user.name
}, this.app.config.jwt.secret, { expiresIn: '2h' });
this.ctx.body = {
code: '200',
data: token
}
} else {
this.ctx.body = {
code: '500',
msg: '用户名密码错误'
}
}
}

配置路由:

// app/router.js
router.post('/login', controller.user.login);

代码完毕,让我们测试一下,创建一个post请求到 localhost:7001/login
,输入用户名密码:

{
"name":"xiaoming",
"password":"test1234"
}

结果如下:

{
"code": "200",
"data": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoieGlhb21pbmciLCJpYXQiOjE2MDg1NTkzMTYsImV4cCI6MTYwODU2NjUxNn0.SyXAhVvrwAql-4FzaZrlEs6dsEJ4wXbdjQsHv43CSOI"
}

成功了,这一大坨就是我们创建的token。

嗯……我们是拿到了token,但是怎么用呢?还记得我们最开始写的两个接口吗?我们现在给他配置jwt验证,然后试试不登录能不能访问。

// app/router.js
router.post('/createUser', app.jwt, controller.user.createUser);
router.get('/getUsers', app.jwt, controller.user.getUsers);

然后我们重新测试一下这两个接口,服务端返回 401 Unauthorized
,这说明验证生效了。那我们在请求的时候把刚才登录接口获取到的token设置在 Authorization
Bearer Token
字段中,再试一次,成功了!

如果你跟我走到了这一步,那说明你已经距离一个合格的服务端开发者更近了一步,但是这里我们好像并没有用到 token
里面携带的信息,下一篇我们将会解析 token
携带的信息,知道每次都是哪个用户在访问我们的服务,而且我们将优化我们的代码,让它看起来更简洁、合理。

SegmentFault博客
我还没有学会写个人说明!
上一篇

辛选回应燕窝事件:接受行政处罚决定,积极履行责任并整改

你也可能喜欢

评论已经被关闭。

插入图片