![](https://i-blog.csdnimg.cn/blog_migrate/763e641397e4d017635968532a667359.png)
登录模块功能详解
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 }
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;
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 }
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 }
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
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 }
注册模块详解
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 }
找回密码功能
1、此处介绍个新的加密解密方式 jwt-simple
使用方式 加密
//token加密的key
let secret = 'xxx';
jwt.encode(token, secret)
第一个参数可以是加密的对象 可以是单个字符变量 也可以是对象 但是注意不能传JSON字符串形式的对象 不然解密后结构不出来
解密过程
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 }
具体使用代码如下
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 }
![](https://i-blog.csdnimg.cn/blog_migrate/f9df78d87122eafa7a7ab1175caf82b9.png)
国际化多语言功能
结合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 })
未完待续~~~