由前端完成,根據需求自行決定,不加敘述css
前端將用戶名 以及加密後的密碼還有驗證碼輸入的內容統一發給後端 由後端和數據庫的數據進行比對 html
將比對的結果返回給前端前端
這裏我麼利用了阿里的一個加密軟件,此軟件做用是生成兩個不同的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 }
關於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;
生成一個驗證碼圖片而且將其存到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 }
登陸接口的處理,首先先判斷驗證碼是否是成功,成功返回--- 失敗返回結果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 }
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
根據登陸成功後返回的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 }
同理前端基本事項檢驗 而後將加密後的密碼和用戶名發給後端而後端進行存儲 郵箱的做用是用來找回密碼使用的
存儲以前先判斷用戶是否被註冊過 若是沒有 將其註冊成功後而且一塊兒生成一條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 }
使用方式 加密
//token加密的key
let secret = 'xxx';
jwt.encode(token, secret)
第一個參數能夠是加密的對象 能夠是單個字符變量 也能夠是對象 可是注意不能傳JSON字符串形式的對象 否則解密後結構不出來
解密過程
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 }
具體使用代碼以下
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 }
結合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 })
未完待續~~~