在上一篇文章,咱们曾经应用 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
携带的信息,晓得每次都是哪个用户在拜访咱们的服务,而且咱们将优化咱们的代码,让它看起来更简洁、正当。