当前位置: 首页 > 后端技术 > Node.js

如何使用Eggjs从头开发一个项目(二)

时间:2023-04-03 23:17:12 Node.js

上一篇我们已经使用Sequelize连接数据库,可以进行简单的数据库操作。在此基础上,我们尝试开发一个完整的s项目。本篇我们从用户注册和登录入手,尝试开发用户模块的相关代码。用户注册1.注册逻辑用户注册的逻辑很简单:客户端:用户输入帐号、密码等信息进行用户注册;服务器:收到客户端提交的注册信息后,检查字段(是否必填,字段长度等),字段符合要求后,根据用户注册的账号查询数据库,判断是否为根据返回的结果,用户是一个新用户。如果是新用户,将用户信息写入数据库,完成注册过程。2、用户密码处理客户端用户提交数据后,服务器端校验通过,写入数据库。但是,用户密码是敏感信息。为了服务安全,明文密码不能直接写入数据库,防止数据库被攻击。密码泄露。因此,在存储用户密码时,首先对用户密码进行加盐和哈希处理,这样即使存储在数据库中的密码泄露,其他人也无法通过处理后的密码登录。这里使用散列算法来处理密码,因为散列的特性是不可逆的。详见MD5为什么不可逆?.这里我们处理密码的方法叫做hash,即hash。它不是加密算法,而是摘要算法。网上很多文章混淆了这两个概念。事实上,加密和哈希是两个完全不同的概念。详见:散列(Hash)与加密(Encrypt)的基本原理、区别及工程应用。hash可以暴力破解,加盐可以大大增加破解难度。所以密码处理方式一般是每个用户密码对应一个随机盐,和密码一起存储。这样即使是出了数据库,别人也很难破解,因为每一个密码都要破解。所以最终,我们生成hash的公式为:R=H(password+randomSalt)如果还是觉得不安全,甚至可以在代码中写一个固定的salt值,使用两种salt进行hash,或者多次hash等。等等……废话不多说,下面开始写代码。首先,让我们安装bcryptjs,我们用它来加盐和比较密码:npminstallbcryptjs--save然后我们将这两个方法写入app/extend/helper.js:constbcrypt=require('bcryptjs');module.exports={加密(密码){constsalt=bcrypt.genSaltSync(10);//添加盐consthash=bcrypt.hashSync(password,salt);//hash(同步调用)returnhash;},compare(password,hash){returnbcrypt.compareSync(password,hash);//比较}};说到这里你可能会有一些疑惑:为什么bcrypt.compareSync方法没有salt入参呢?这里说明一下,bcrypt.hashSync在生成hash的时候已经把salt值存储在了result中,所以它计算的结果就是hash字符串和salt的组合,这样我们就不需要存储salt值了在数据库里,这样更方便。这样我们就可以通过this.ctx.helop.encrypt和this.ctx.helop.compare在项目中使用这些公共方法了。然后,在UserController中添加一个register方法:asyncregister(){constparams=this.ctx.request.body;//参数验证if(!params.name||!params.password||!params.phone||!params.email){this.ctx.body={code:'500',msg:'参数无效'};}//查询用户是否已经注册constuser=awaitthis.ctx.model.User.findOne({其中:{名称:params.name}});if(user){this.ctx.body={code:'500',msg:'这个用户已经存在'};}//插入数据库constresult=awaitthis.ctx.model.User.create({...params,password:this.ctx.helper.encrypt(params.password)});if(result){this.ctx.body={code:'200',msg:'注册成功'};}}最后,添加路由://app/router.jsrouter.post('/register',controller.user.register);3、功能测试测试,用postman创建一个post请求到localhost:7001/register,传入注册用户信息:{"name":"xiaoming","password":"test1234","phone":"13412341234","绘马il":"test@gmai.com"}服务器返回提示“注册成功”,这时候我们去数据库就可以看到这个数据,密码是一堆看不懂的密文。这个时候我们用同样的数据再次发起请求,服务器返回“用户已注册”,说明我们的注册功能已经完成!用户登录用户登录的逻辑很简单,就是一个用户名和密码客户端传入和服务端存储的比较,如果匹配则登录成功,否则用户名和密码错误。是否将这些信息随请求一起带到服务端?常用的用户认证方式有两种,一种是通过Cookie实现的,一种是通过Token实现的,用户授权认证可以看这篇文章:授权认证登录关于Cookie、Session、Token、JWT的详细解释,Eggjs官网也有专门的章节Cookie和Session相关知识。Cookie和Session,学习服务器,掌握这些知识还是很有必要的。这里,我们选择JWT作为我们的解决方案。关于JWT,这里有两篇文章可供参考,JSONWebToken入门教程,JSONWebTokens简介。OK,看完原理我们来写代码。首先,我们安装egg-jwt插件:npminstallegg-jwt--save导入插件://app/config/plugin.jsjwt:{enable:true,package:"egg-jwt"}配置jwtsecret://app/config/config.default.jsconfig.jwt={secret:'12312456};OK,我们写下面的代码://app/controller/user.jsasynclogin(){constparams=this.ctx.request.body;if(!params.name||!params.password){this.ctx.body={code:'500',msg:'参数无效'}}constuser=awaitthis.ctx.model.User.findOne({其中:{名称:params.name}});if(!user){this.ctx.body={code:'500',msg:'用户名密码错误'}}//校验密码constcheckPassword=this.ctx.helper.compare(params.password,user.password);if(checkPassword){//根据用户名创建token,过期时间为2小时consttoken=this.app.jwt.sign({name:user.name},this.app.config.jwt.秘密,{expiresIn:'2h'});this.ctx.body={代码:'200',data:token}}else{this.ctx.body={code:'500',msg:'Incorrectusernameandpassword'}}}Configurerouting://app/router.jsrouter.post('/login',controller.user.login);thecodeiscomplete,let'stestit,createapostrequesttolocalhost:7001/login,entertheusernameandpassword:{"name":"xiaoming","password":"test1234"}结果如下:{"code":"200","data":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoieGlhb21pbmciLCJpYXQiOjE2MDg1NTkzMTYsImV4cCI6MTYwODU2NjUxNn0.SyXAhVvrwAql-4FzaZrlEs6dsEJ4wXbdjQsHv43CSOI"}成功了,这一大坨就是我们创建的token嗯……我们是拿到了token,但是怎么useit?Rememberthetwointerfaceswefirstwrote?Wenowconfigurejwtverificationforhim,andthentrytoaccessitwithoutloggingin.//app/router.jsrouter.post('/createUser',app.jwt,controller.user.createUser);router.get('/getUsers',app.jwt,controller.user.getUsers);thenweretestCheckthesetwointerfaces,andtheserverreturns401Unauthorized,whichmeansthattheverificationhastakeneffect.然后我们在请求的时候,在Authorization的BearerToken字段中设置刚才从登录界面获取的token,再试一次,成功了!如果你跟我走到这一步,说明你离成为一名合格的服务端开发者又近了一步,不过这里我们似乎并没有用到token中携带的信息。在下一篇文章中,我们将分析token所携带的信息。信息,每次都知道是哪个用户在访问我们的服务,我们会优化我们的代码,让它看起来更简洁合理。