前后端登录注册之node剖析与token的使用状态

登录模块功能详解

  1、用户名密码的格式验证  

      由前端完成,根据需求自行决定,不加叙述

  

   2、点击提交按钮思路详解

       前端将用户名 以及加密后的密码还有验证码输入的内容统一发给后端  由后端和数据库的数据进行比对 

       将比对的结果返回给前端

   3、密码加密及解密技术  使用插件包------jsencrypt 和 node-jsencrypt

        这里我么利用了阿里的一个加密软件,此软件作用是生成两个不一样的key然后相当于一个键值对可以通过这两个key分别进行加密解密,

       其中一个在前端使用加密另一个在后端解密,这样的话就会让安全性提高  具体代码如下

       

 1 //此模块是根据jsencrypt封装一个加密过程
 2 
 3 import JSEncrypt from 'jsencrypt'
 4 
 5 export function jsEncrypt(str) {
 6   //实例这个方法
 7   let encrypt = new JSEncrypt();
 8   //根据一款软件 随机生成一段key值  这个key值是两个相互的 前后端各有一个 然后我们根据这个key值可以进行加密解密处理
 9   let key = `MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlec7cf57opzCzTxrsSWmnycz0QVg9jA9syIDXyamt0cAypThrLpzFgCA6bTGoRTp5fjUzdQ1Z/PyN6L7BJ01EYQBdp6LERkqCNTySP1furoB1tlsxmi6lvYAFfJXgABJiAv7+5pZGilDtHvJDAduAZD2SKjg4etQom7bkAjV0GCwJnW6VkAUilgV+xwXhMDpjkzgNA6gdKVJjuF4n09fwRO4Y3bnypbOYLb0ks03QH1YkhJglEv6NrFpnUy1qFIkzKwgs0ieZ3qXW5yYmS/I3ZLcmsQ7RutCmJoqwTgfXodUGTxKCjIme+TeqcJmdHc84ElhIuk30nCFqYclehae8wIDAQAB`
10   //将这个key值传给这个方法  然后后端调用这个方法传入另一个相对的key就会进行解密
11   encrypt.setPublicKey(key);
12   //进行加密
13   let data = encrypt.encrypt(str);
14   //因为+号可能有其他不好的影响 我们用%2b替换一下这个+15   let code = encodeURI(data).replace(/+/g, '%2B')
16   //将加密后的字符串返回
17   return code
18 }
19 
20 //解密函数  
21 export function decode(pwd) {
22   var decrypt = new JSEncrypt();
23   //存放key
24   decrypt.setPrivateKey('MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCV5ztx/nuinMLNPGuxJaafJzPRBWD2MD2zIgNfJqa3RwDKlOGsunMWAIDptMahFOnl+NTN1DVn8/I3ovsEnTURhAF2nosRGSoI1PJI/V+6ugHW2WzGaLqW9gAV8leAAEmIC/v7mlkaKUO0e8kMB24BkPZIqODh61CibtuQCNXQYLAmdbpWQBSKWBX7HBeEwOmOTOA0DqB0pUmO4XifT1/BE7hjdufKls5gtvSSzTdAfViSEmCUS/o2sWmdTLWoUiTMrCCzSJ5nepdbnJiZL8jdktyaxDtG60KYmirBOB9eh1QZPEoKMiZ75N6pwmZ0dzzgSWEi6TfScIWphyV6Fp7zAgMBAAECggEAGk+WyIBhVP5s1rcnM9Wm9EJePu7RwQRgoAN1Ugsnsf2dbvFI1xd2wcLe3aZkQru3/ix5tZLsuM1Bk3Bg3MN3IBbqZtaXFC41iY1O5W7Lkau6TOqmxAB3161gAHojz4y9W0q3NMc3onbhslkTxa+8KDw4bjJuHlk+MvSARzy1wrghDB6QbdJIPzV19dGggez/ICf6UrCgdmQuJAbFdHnoEK83qFSbBz5aljwWnMWTWvMJ0vN4SJXjiLwNMrOyKPkaeMulYFL2avFNeHhj/vzJL5R1bYWyFJ3mFQpSBVGUDfZoeCp5hDbwU6ERe2pjagPgsD2S72IhAEwKrlXUlemDQQKBgQDHA3gKdE47IlNrsqEmPiJHHoXY3Ix+4GPWT513DqwAAgxc0Sd/iFqHrxwAP7ZgohU4Eln/fCd9CsrOLqT9e3EETOjfSxFQjxC/RmcKkg4fXZZCXryx81OpjdTRT4v2mbpA0Hm/Kb4s0tT03vHrPeMOffiKefK0NQnbZfFvpQzk0wKBgQDA08WGesbUJKidTV0AjYNZQjJgXpYoRwneERxQiu7Ofxsc0oKbsYv5rZtKp7LjVkTyXUj8yhzJObVKm3qtoj8qvRhVMUhyPAiJJjFh+gcybV39jyvuq0zCv9n0ytuCfTfvPZEe/bsGCzhv71wuHNhxrkQ3St37APgf6wrzo+WJYQKBgEFoF3TAItH2hxo3PBVYiGV9V5odaiNs1gMiaWsurELYaX2709JrWu2LFJXUWrlJq9Wg2mlIQaYr/NlkpR8WCd/S8xooDsm+K0/h8I2d0PxoArFPd464nP91uMMN9L8YaQlSOyEjs/gBVrIf77xTu6MQrbW9PJITeGjeCUqbITC3AoGAXPX7dTC9qEqgC23fl0Oh/iceuD0BcRuGU0u2ddH0/RJkFMob80luLQmYIy6j3Fub06hLZqtdo1kx4G0CgLEGeOk+0Nt4jLIKf2wtRInQbGwzculSCbcFw6HQRuaBWvBZRfpNez5hqrFAHR6tNwHrCyszceCjEb5O4LxkxD7QiyECgYEAhujdPXQQcn7CqpY61ivxy03LCz4lW3R8xSJMtm1BY+iBp6P2GbgdKpbSXiTSr/Y3//8DwccLf47xc7Otcw6Yz+tcen88UxqHrI1myHltFNYkrbQ692PtszO8n3fta5bCPr8RY2ON3+uGYSHIDGyixAdxRQxkQPwMD0CFntHA2EA=');
25   pwd = pwd.replace(/%2B/g, "+")
26   pwd = decrypt.decrypt(pwd);
27   return pwd;
28 }
View Code

   4、图形验证码之过期实现  ----- Redis 软件

       关于Redis这个软件不多做叙述,他的作用相当于一个小型的数据库,可以存入一个键值对并对其可以进行设置过期时间

       详情请看 https://www.runoob.com/redis/redis-tutorial.html

       使用步骤  先让其和我们的node连接起来  代码如下

 1 //连接redis数据库
 2 
 3 const redis = require('redis')
 4 const client = redis.createClient();
 5 //如果没有启动redis,会报错,启动redis方法,在cd到redis的安装目录,执行redis-server.exe redis.windows.conf
 6 client.on("error", function (err) {
 7   console.log("Error " + err);
 8 });
 9 
10 
11 module.exports=client;
View Code

         4-1、图形验证码的实现与存入Redis中去并设置过期时间60s----插件包  svg-captcha

           生成一个验证码图片并且将其存到Redis代码

 1 module.exports.Verify = (req, res) => {
 2     var captcha = svgCaptcha.create({});
 3     //此时text生成的就是随机的四位数验证码 真正的验证码
 4     let text = captcha.text;
 5     //console.log(captcha);
 6     //在生成一个随机的key用来做这个验证码的标识
 7     let keyId = createToken();
 8     //console.log(keyId);
 9     //把这个生成的key和captcha.data这个生成的图片返回给前端
10     let temp = {
11         keyId: keyId,
12         captcha: captcha.data,
13     }
14     //将其存到Redis这个数据库中 并且设置60s后从这个数据库删除
15 
16     client.set(keyId, text, 'EX', 60) //60秒后验证码过期知道
17 
18     //检查一下如果是不是存进去了
19     client.get(keyId, function (err, v) {
20         //console.log("图形验证码的值存入redis,值为:", v);
21         if (err) {
22             res.statusCode = 500;
23             res.send({
24                 code: 0,
25                 msg: "请稍后重试"
26             })
27         } else {
28             res.send({
29                 code: 1,
30                 data: temp,
31                 message: '验证码'
32             });
33         }
34     })
35 }
View Code

   5、登录接口的处理情况

       登录接口的处理,首先先判断验证码是不是成功,成功返回---    失败返回结果

       然后根据用户名去数据库查找该用户是否存在   存在----    不存在返回结果

       将数据库中进行加密的密码取出来解密,并将前端发来的密码进行解密然后比对  密码相等-----   密码不等 ----   代码如下

 1 module.exports.Login = (req, res) => {
 2     //console.log(req.body)
 3     //console.log(req.body.keyId)
 4     let {
 5         user,
 6         pwd,
 7         keyId,
 8         img
 9     } = req.body;
10     //console.log(img,"什么")
11 
12     //接到登录请求后先检验这个图片验证码是不是存在或者不正确 
13     client.get(keyId, (err, succ) => {
14         if (err) {
15             console.log(err);
16             res.statusCode = 500;
17             res.send({
18                 code: 0,
19                 msg: "验证码已过期"
20             })
21             return false;
22         } else {
23             //console.log(img.toUpperCase() === succ.toUpperCase())
24             console.log(succ)
25             if (succ) {
26                 if (img.toUpperCase() === succ.toUpperCase()) {
27                     //根据用户名去数据库查询这个人是不是存在数据库
28                     //如果存在的话 在对密码进行解密处理
29                     const $sql = `select * from user where user="${user}"`;
30                     connection.query($sql, (err, resaults) => {
31                         if (err) {
32                             res.statusCode = 500;
33                             res.send({
34                                 code: 0,
35                                 msg: "登录失败,请稍后再试"
36                             })
37                         } else {
38                             if (resaults.length) {
39                                 //console.log(resaults[0])
40                                 //取出这个密码来进行解密 然后与传过来的密码进行比较 看是否密码一样
41                                 var decrypt = new JSEncrypt();
42                                 //存放key
43                                 decrypt.setPrivateKey('MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCV5ztx/nuinMLNPGuxJaafJzPRBWD2MD2zIgNfJqa3RwDKlOGsunMWAIDptMahFOnl+NTN1DVn8/I3ovsEnTURhAF2nosRGSoI1PJI/V+6ugHW2WzGaLqW9gAV8leAAEmIC/v7mlkaKUO0e8kMB24BkPZIqODh61CibtuQCNXQYLAmdbpWQBSKWBX7HBeEwOmOTOA0DqB0pUmO4XifT1/BE7hjdufKls5gtvSSzTdAfViSEmCUS/o2sWmdTLWoUiTMrCCzSJ5nepdbnJiZL8jdktyaxDtG60KYmirBOB9eh1QZPEoKMiZ75N6pwmZ0dzzgSWEi6TfScIWphyV6Fp7zAgMBAAECggEAGk+WyIBhVP5s1rcnM9Wm9EJePu7RwQRgoAN1Ugsnsf2dbvFI1xd2wcLe3aZkQru3/ix5tZLsuM1Bk3Bg3MN3IBbqZtaXFC41iY1O5W7Lkau6TOqmxAB3161gAHojz4y9W0q3NMc3onbhslkTxa+8KDw4bjJuHlk+MvSARzy1wrghDB6QbdJIPzV19dGggez/ICf6UrCgdmQuJAbFdHnoEK83qFSbBz5aljwWnMWTWvMJ0vN4SJXjiLwNMrOyKPkaeMulYFL2avFNeHhj/vzJL5R1bYWyFJ3mFQpSBVGUDfZoeCp5hDbwU6ERe2pjagPgsD2S72IhAEwKrlXUlemDQQKBgQDHA3gKdE47IlNrsqEmPiJHHoXY3Ix+4GPWT513DqwAAgxc0Sd/iFqHrxwAP7ZgohU4Eln/fCd9CsrOLqT9e3EETOjfSxFQjxC/RmcKkg4fXZZCXryx81OpjdTRT4v2mbpA0Hm/Kb4s0tT03vHrPeMOffiKefK0NQnbZfFvpQzk0wKBgQDA08WGesbUJKidTV0AjYNZQjJgXpYoRwneERxQiu7Ofxsc0oKbsYv5rZtKp7LjVkTyXUj8yhzJObVKm3qtoj8qvRhVMUhyPAiJJjFh+gcybV39jyvuq0zCv9n0ytuCfTfvPZEe/bsGCzhv71wuHNhxrkQ3St37APgf6wrzo+WJYQKBgEFoF3TAItH2hxo3PBVYiGV9V5odaiNs1gMiaWsurELYaX2709JrWu2LFJXUWrlJq9Wg2mlIQaYr/NlkpR8WCd/S8xooDsm+K0/h8I2d0PxoArFPd464nP91uMMN9L8YaQlSOyEjs/gBVrIf77xTu6MQrbW9PJITeGjeCUqbITC3AoGAXPX7dTC9qEqgC23fl0Oh/iceuD0BcRuGU0u2ddH0/RJkFMob80luLQmYIy6j3Fub06hLZqtdo1kx4G0CgLEGeOk+0Nt4jLIKf2wtRInQbGwzculSCbcFw6HQRuaBWvBZRfpNez5hqrFAHR6tNwHrCyszceCjEb5O4LxkxD7QiyECgYEAhujdPXQQcn7CqpY61ivxy03LCz4lW3R8xSJMtm1BY+iBp6P2GbgdKpbSXiTSr/Y3//8DwccLf47xc7Otcw6Yz+tcen88UxqHrI1myHltFNYkrbQ692PtszO8n3fta5bCPr8RY2ON3+uGYSHIDGyixAdxRQxkQPwMD0CFntHA2EA=');
44                                 pwd = pwd.replace(/%2B/g, "+")
45                                 //此时就能把传过来的密码解密了
46                                 pwd = decrypt.decrypt(pwd);
47                                 //在把数据库的数据解密出来进行比对
48                                 resaults[0].password = resaults[0].password.replace(/%2B/g, "+")
49                                 let mysqlPwd = decrypt.decrypt(resaults[0].password)
50                                 //console.log(mysqlPwd,"密码是什么")
51                                 //比较下两个密码 看是否一样
52                                 if (pwd === mysqlPwd) {
53                                     res.send({
54                                         code: 1,
55                                         token: resaults[0].token,
56                                         msg: "登陆成功"
57                                     })
58                                 } else {
59                                     res.send({
60                                         code: 0,
61                                         msg: "密码输入错误"
62                                     })
63                                 }
64                             } else {
65                                 //该用户没有在数据库中
66                                 res.send({
67                                     code: 0,
68                                     msg: "该用户还会注册,请去注册页面进行注册"
69                                 })
70                             }
71                         }
72                     })
73                 } else {
74                     res.statusCode = 500;
75                     res.send({
76                         code: 0,
77                         msg: "验证码输入错误"
78                     })
79                 }
80             } else {
81                 res.send({
82                     code: 0,
83                     msg: "验证码已过期"
84                 })
85             }
86 
87             console.log(succ, "===");
88         }
89     })
90 }
View Code

  6、MySQL数据库的连接测试  ------   插件包mysql

 1 //数据库连接
 2 const mysql=require("mysql");
 3 
 4 var connection=mysql.createConnection({
 5     host:"localhost",
 6     user:"root",
 7     password:"root",
 8     database:"project"//数据库名称
 9 })
10 
11 connection.connect((error) => {
12     if (error) {
13       console.log('数据库连接失败,详情:',error)
14     } else {
15       console.log('数据库连接成功')
16     }
17   })
18   
19   module.exports = connection
View Code

  7、记住密码功能

      根据登录成功后返回的token然后携带者其向后端发送请求 ,让后端把用户名和加密后的密码返回回来

      由前端进行解密后然后展示到页面 

      缺点  本来用于前后端加密解密的key这样都暴露给前端了  另外这种实现也不是好的办法  不如浏览器自带的记住密码功能好

      

 1 module.exports.Remember=(req,res)=>{
 2     let {token}=req.query;
 3     //根据这个token去数据库找到改用户的密码然后返回给前端让其默认加载页面上
 4     const $sql=`select * from user where token="${token}"`;
 5     connection.query($sql,(err,resaults)=>{
 6         if(err){
 7             return false;
 8         }
 9         let {user,password}=resaults[0];
10         res.send({
11             code:1,
12             info:{
13                 user,
14                 password
15             }
16         })
17     })
18 }
View Code

 

注册模块详解

   1、注册前端思路

        同理前端基本事项检验 然后将加密后的密码和用户名发给后端然后端进行存储  邮箱的作用是用来找回密码使用的

    2、后端思路

        存储之前先判断用户是否被注册过  如果没有   将其注册成功后并且一起生成一条token  然后此token在用户登录成功时返给前端并将其存在本地

 1 module.exports.Registry = (req, res) => {
 2     //console.log(req.body)
 3     let {
 4         password,
 5         username,
 6         email
 7     } = req.body;
 8 
 9     //此时密码是加密后的密码 我们需要先根据这个用户名去数据库查询该用户
10     //如果存在该用户 提示已经注册过了  不存在改用户 则让其注册
11 
12     const $sql = `select * from user where user="${username}"`
13     connection.query($sql, (err, results) => {
14         if (err) {
15             //这样代表后台服务器错误 返回一个500的状态码吧
16             res.statusCode = 500;
17             res.send({
18                 code: 0,
19                 msg: "注册失败,请稍后重试"
20             })
21         } else {
22             console.log(results);
23             if (results.length) {
24                 //证明该用户已经被注册过了 提示注册失败
25                 res.send({
26                     code: 0,
27                     msg: "注册失败,该用户已被注册"
28                 })
29             } else {
30                 //存入数据库
31                 //在存入数据库的同时为该用户注册一个token 用来做身份认证
32                 let token = createToken(username);
33                 const $save = `insert into user(user,password,token,email) values("${username}","${password}","${token}","${email}")`;
34                 connection.query($save, (err, result) => {
35                     if (err) {
36                         //这样代表后台服务器错误 返回一个500的状态码吧
37                         res.statusCode = 500;
38                         res.send({
39                             code: 0,
40                             msg: "注册失败,请稍后重试"
41                         })
42                     } else {
43                         //注册成功,向用户返回token以及注册成功的信息
44                         res.send({
45                             code: 1,
46                             msg: "注册成功",
47                             token
48                         })
49                     }
50                 })
51             }
52         }
53     })
54 }
View Code

找回密码功能 

   1、此处介绍个新的加密解密方式 jwt-simple

        使用方式 加密

        //token加密的key

                             let secret = 'xxx';

                            jwt.encode(token, secret)

         第一个参数可以是加密的对象  可以是单个字符变量 也可以是对象  但是注意不能传JSON字符串形式的对象 不然解密后结构不出来

        解密过程

         let secret="xxx";//解密加密所对应的key
         let decode=jwt.decode(token, secret);
                        let {user}=decode;
 

      2、介绍个发送邮件的插件包  nodemailer  代码如下

 

 1 // async..await is not allowed in global scope, must use a wrapper
 2 
 3 const nodemailer=require("nodemailer")
 4 
 5 module.exports.sendEmail=async (username, email, url)=>{
 6 
 7     // Generate test SMTP service account from ethereal.email
 8     // Only needed if you don't have a real mail account for testing
 9     let testAccount = await nodemailer.createTestAccount();
10 
11     // create reusable transporter object using the default SMTP transport
12     let transporter = nodemailer.createTransport({
13         host: 'smtp.sina.com',
14         // service: 'qq',
15         // port: 465,
16         //secure: false, // true for 465, false for other ports
17         secureConnection: true, // 使用了 SSL
18         auth: {
19             user: 'qiangchen1996@sina.com', // generated ethereal user
20             pass: '填写自己的邮箱密码' // generated ethereal password
21         }
22     })
23 
24     // send mail with defined transport object
25     let info = await transporter.sendMail({
26         from: '<qiangchen1996@sina.com>', // sender address
27         to: email, // list of receivers
28         subject: "重新设置密码", // Subject line
29         html: `<b>${username}您好!您可以点击下面的链接设置新的密码,<span style="color:red">幽默的小强为您奉上</span></b>
30     <a href=${url}>${url}</a>,<h2>测试功能,打扰之处抱歉</h2>` // html body
31     });
32 
33     console.log("Message sent: %s", info.messageId);
34     // Message sent: <b658f8ca-6296-ccf4-8306-87d57a0b4321@example.com>
35 
36     // Preview only available when sending through an Ethereal account
37     console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info));
38     // Preview URL: https://ethereal.email/message/WaQKMgKddxQDoou...
39 }
View Code

     具体使用代码如下

 1 module.exports.Retrieve = (req, res) => {
 2     //console.log(req.body);
 3     let {
 4         email
 5     } = req.body;
 6     console.log(email)
 7     //根据这个email去数据库里查到对应的用户去
 8     const $sql = `select * from user where email="${email}"`;
 9     console.log($sql);
10     connection.query($sql, async (err, results) => {
11         if (err) {
12             res.statusCode = 500;
13             res.send({
14                 code: 0,
15                 msg: "服务器繁忙,请稍后重试"
16             })
17             return
18         }
19         if (results.length) {
20             //console.log(results[0])
21             let {
22                 user
23             } = results[0];
24             let token=createToken(user);
25             //console.log(token,"====")
26             try {
27                 await sendEmail(user, email, `http://localhost:8080/#/reset/${token}`)
28                 //成功以后就告诉前端
29                 res.send({
30                     msg: "请注意查收邮件",
31                     code: 1
32                 })
33             } catch (error) {
34                 res.send({
35                     msg: "邮件发送失败,请重新提交",
36                     code: 0
37                 })
38             }
39         } else {
40             res.send({
41                 code: 0,
42                 msg: "该邮箱未被注册"
43             })
44         }
45     })
46 }
View Code

国际化多语言功能

       结合vue使用的插件包  vue-i18n

  此处只展示main.js的布置  具体用法请参考 https://www.npmjs.com/package/vue-i18n

 1 // The Vue build version to load with the `import` command
 2 // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
 3 import Vue from 'vue'
 4 import App from './App'
 5 import router from './router'
 6 
 7 import ElementUI from 'element-ui';
 8 import 'element-ui/lib/theme-chalk/index.css';
 9 
10 
11 import store from "./store/index"
12 
13 //引入语言切换
14 import VueI18n from 'vue-i18n'
15 
16 //简体中午语言
17 import zhCN from '@/i18n/zh-CN.js'
18 //英语
19 import enUS from '@/i18n/en-US.js'
20 //繁体字
21 import zhTW from '@/i18n/zh-TW.js'
22 
23 //将vue-i18n挂载到全局
24 Vue.use(VueI18n)
25 
26 Vue.use(ElementUI);
27 
28 //配置语言项
29 const messages = {
30   'en-US': {...enUS},
31   'zh-CN': {...zhCN},
32   'zh-TW': {...zhTW}
33 }
34 
35 //设置语言项  如果本地有从本地提取  本地没有显示默认简体中午
36 //存到本地的原因是防止页面刷新导致语言不能显示
37 let currentLocale = localStorage.getItem('language_type') || 'zh-CN';
38 
39 const i18n = new VueI18n({
40   locale: currentLocale, // 设置地区
41   messages, // 设置地区信息
42 })
43 
44 
45 Vue.config.productionTip = false
46 
47 /* eslint-disable no-new */
48 new Vue({
49   el: '#app',
50   router,
51   store,
52   i18n,
53   components: { App },
54   template: '<App/>'
55 })
View Code

 

   未完待续~~~

原文地址:https://www.cnblogs.com/cq1715584439/p/11238339.html