阅读 1039

NodeJS:图片验证码登录

效果展示

样式可能有点丑,请不要在意

用到的技术

express、vue、redis、axios

功能实现

图片验证码生成

svg-captcha是一个可以生成图形验证码的模块,详细介绍移步svg-captcha文档

下载svg-captcha模块

npm i svg-captcha -S
复制代码

在nodejs中引入

const code = require("svg-captcha");
复制代码

之后对模块进行配置并导出

const code = require("svg-captcha");
function createCode() {
    return code.create({
        size: 4,
        ignoreChars: "0o1iIl",
        noise: 3,
        color: true,
        background: "#fff",
        fontSize: 60
    });
}
module.exports = createCode;
复制代码

模块导出后在后端路由模块中引入生成图片验证码的模块

const captcha = require("./Code")
复制代码

svg-captcha的实例,给我们提供了两个属性

  • data:验证码svg
  • text:验证码文本

我们要让前端显示的肯定是svg格式的图片,不可能是text文本,因为如果要爬虫模拟登录,岂不是太简单了?

之后访问验证码接口地址,就可以看到图片了。

前端页面代码

所有框架和工具,我引的都是CND

html部分

<div id="app">
    <p><label>用户名:</label><input type="text" ref="username" value="Wick"></p>
    <p><label>密码:</label><input type="text" ref="password" value="123456"></p>
    <p id="code">
        <label>验证码:</label>
        <input ref="codeValue" type="text">
        <img @click="getCode" :src="codeImg" alt="">
    </p>
    <button id="login" @click="login">登录</button>
</div>
复制代码

js部分

axios.defaults.baseURL = "http://localhost:10086";
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.withCredentials = true;
axios.interceptors.request.use(config => {
    return config;
})
const app = new Vue({
    el: "#app",
    data: {
        codeImg: `http://localhost:10086/code?t=${new Date().getTime()}`
    },
    methods: {
        getCode() {
            // 添加时间戳进行验证码切换
            this.codeImg = `http://localhost:10086/code?t=${new Date().getTime()}`;
            this.$refs.codeValue.value = "";
        },
        login() {
            let username = this.$refs.username.value;
            let password = this.$refs.password.value;
            let code = this.$refs.codeValue.value;
            let data = {
                username,
                password: CryptoJS.AES.encrypt(password, username).toString(),   //密码加密
                code
            }
            axios.post("/login", Qs.stringify(data)).then(res => {
                alert(res.data.msg);
                if(res.data.success === "ok") {
                    window.location = "/Home.html";
                }
            })
        }
    }
});
复制代码

引入express-session

原生nodejs未提供session功能,所以我们只能引入第三方模块express-session

下载express-session

npm i -S express-session
复制代码

nodejs引入express-session

const session = require("express-session");
复制代码

中间件设置,详细配置移步express-session文档

app.use(session({
    secret: "WickYo",	// 对cookie进行签名
    name: "session",	// cookie名称,默认为connect.sid
    resave: false,	// 强制将会话保存回会话容器
    rolling: true,	// 强制在每个response上设置会话标识符cookie
    cookie: {
        // 5分钟
     	maxAge: 300000
    }
}))
复制代码

配置之后,页面的response都会带有这个session

查看一下Application里的cookies

这里的session的存活时间,我是用来做验证码是否失效的,当session过期了,那么验证码也就跟着失效,那么就需要切换验证进行登录

引入redis并设置验证码

下载redis模块

npm i redis -S
复制代码

nodejs中引入

const redis = require("redis");
复制代码

对redis进行配置,这里只是简单配置,详细移步redis文档

const client = redis.createClient({
    host: "192.168.56.101",	//	redis地址
    port: 6379	// 端口号
})
// 监听连接事件
client.on("connect", error => {
    if(!error) {
        console.log("connect to redis")
    }
})
// 监听错误事件
client.on("error", error => {
    throw new Error(error)
})
复制代码

封装set方法

function setString(key, value, expire) {
    return new Promise((resolve, reject) => {
        client.set(key, value, (error, replay) => {
            if(error) {
                reject("设置失败")
            }
            if(expire) {
                client.expire(key, expire);
            }
            resolve("设置成功")
        });
    })
}
复制代码

封装get方法

function getString(key) {
    return new Promise((resolve, reject) => {
        if(key) {
            client.get(key, (error, replay) => {
                if(error) {
                    reject(`获取${key}失败`)
                }
                resolve(replay);
            })
        }
    })
}
复制代码

最后导出方法。

module.exports = {
    setString,
    getString
}
复制代码

上面封装了set和get方法,那么我们就可以在redis中设置值了。

经过前面的express-session配置之后,我们在进入页面时,就会加载验证码图片并设置cookie(存着sessionID的值),然后在登录的时候,会带上这个cookie。那这样,我们就可以在获取图片的时候,以sessionID值为key,svg-captcha的text为value进行设置

注:sessionID是使用了express-session后注入的

登录功能实现

为了方便,就不连接数据库了,直接创建一个本地的文件当做用户的账号密码吧

直接上代码

Router.post("/login", (req, res) => {
    let username = req.body.username;
    let password = CryptoJS.AES.decrypt(req.body.password, username).toString(CryptoJS.enc.Utf8);   // 解密
    let code = req.body.code.toLowerCase();
    if(!req.signedCookies.session) {
        res.send({success: "no", msg: "验证码过期"})
        return;
    }
    // redis封装的方法
    getString(req.signedCookies.session).then(data => {
        // signedCookies即被签名过的cookie
        console.log(data)
        if(code === data) {
            console.log("验证码正确")
            // 读文件
            fs.readFile(__dirname + "/../user.conf", "utf8", (err, data) => {
                let dataArr = data.toString().split("=");
                if(username === dataArr[0]) {
                    if(password === dataArr[1]) {
                        // 根据登录用户名设置cookie,并规定只能cookie存活30分钟
                        res.cookie("uid", username, {maxAge: 300000})
                        res.send({success: "ok", msg: "登录成功"})
                        return;
                    }
                }
                res.send({success: "no", msg: "用户名或密码错误"})
            })
        } else {
            res.send({success: "no", msg: "验证码错误"})
        }
    }).catch(err => {
        console.log(err)
    })
})
复制代码

引入cookie-parser

我们可以引入cookie-parse对cookie进行解密

中间件设置

app.use(cookieParser(secret))
复制代码

配置好之后,就能解析出被指定secret加密过的cookie了

获取post请求参数

我们没办法直接获取到post的参数,需要安装body-parser模块,才能进行获取

npm i -S body-parser
复制代码

配置body-parser

app.use(bodyParser.urlencoded({ extended: false }))	//解析application/x-www-form-urlencoded
复制代码

配置之后,我们就能获取到请求体了

什么是Signed cookie?

cookie有一个不好地方的,就是在浏览器中可见,并且是可以修改的,如果我们直接把敏感信息存储在cookie中,那么任何人都可见,任何人都可对其进行修改,这是很不安全的。

正是因为不安全,所以才会需要签名Cookie(Signed Cookie)对其进行加密,以防止被某些信息被泄露。

我们手动对加密后的cookie进行解密

解密出来的值,其实就是在获取验证码时的sessionID。

所以,我们能通过req.signedCookies获取对应的cookie,都是因为cookie-parser帮我们做了解密。

本文已收录至github:github.com/OnlyWick/Fu…

如有错误,请及时指出!

关注下面的标签,发现更多相似文章
评论