先後端登陸註冊之node剖析與token的使用狀態

 

登陸模塊功能詳解

  一、用戶名密碼的格式驗證  

      由前端完成,根據需求自行決定,不加敘述css

  

   二、點擊提交按鈕思路詳解

       前端將用戶名 以及加密後的密碼還有驗證碼輸入的內容統一發給後端  由後端和數據庫的數據進行比對 html

       將比對的結果返回給前端前端

   三、密碼加密及解密技術  使用插件包------jsencrypt 和 node-jsencrypt

        這裏我麼利用了阿里的一個加密軟件,此軟件做用是生成兩個不同的key而後至關於一個鍵值對能夠經過這兩個key分別進行加密解密,vue

       其中一個在前端使用加密另外一個在後端解密,這樣的話就會讓安全性提升  具體代碼以下node

       

 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

   四、圖形驗證碼之過時實現  ----- Redis 軟件

       關於Redis這個軟件很少作敘述,他的做用至關於一個小型的數據庫,能夠存入一個鍵值對並對其能夠進行設置過時時間mysql

       詳情請看 https://www.runoob.com/redis/redis-tutorial.htmlwebpack

       使用步驟  先讓其和咱們的node鏈接起來  代碼以下web

 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-一、圖形驗證碼的實現與存入Redis中去並設置過時時間60s----插件包  svg-captcha

           生成一個驗證碼圖片而且將其存到Redis代碼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

   五、登陸接口的處理狀況

       登陸接口的處理,首先先判斷驗證碼是否是成功,成功返回---    失敗返回結果sql

       而後根據用戶名去數據庫查找該用戶是否存在   存在----    不存在返回結果

       將數據庫中進行加密的密碼取出來解密,並將前端發來的密碼進行解密而後比對  密碼相等-----   密碼不等 ----   代碼以下

 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

  六、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

  七、記住密碼功能

      根據登陸成功後返回的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

 

 

註冊模塊詳解

   一、註冊前端思路

        同理前端基本事項檢驗 而後將加密後的密碼和用戶名發給後端而後端進行存儲  郵箱的做用是用來找回密碼使用的

    二、後端思路

        存儲以前先判斷用戶是否被註冊過  若是沒有   將其註冊成功後而且一塊兒生成一條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

找回密碼功能 

   一、此處介紹個新的加密解密方式 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;
 

      二、介紹個發送郵件的插件包  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

 

 

 

   未完待續~~~

相關文章
相關標籤/搜索