node脚手架搭建服务器实现token验证的方法
内容
- 用脚手架快速搭建 node 项目
- 用 mysql 连接池实现与数据库的交互
- 用 jsonwebtoken 实现 token 身份验证
- 综合案例:用简介登录页面实现上述内容
1. 快速搭建 node 项目
我们都知道 express 框架可高效的开发 node 服务器,但对于底层的搭建还要靠自己手写。然而 express-generator 的出现就很好地解决了此问题,它可一键为我们生成项目基本骨架,可谓node 脚手架
1.1 生成项目
①:首先全局安装 express : npm install express -g
②:接着全局安装 express-generator:npm install express-generator -g
③:然后使用命令创建项目: express token_learn(项目名称)
1.2 修改入口文件
对于好多习惯了手撸服务器的人,app.js 总是让人难忘,奈何此骨架中入口文件为 www.js。此时我们可以手动修改app.js 代码,让其成为我们的入口文件
示例:
var express = require('express'); var path = require('path'); var cookieParser = require('cookie-parser'); const index = require('./routes/index'); const users = require('./routes/users'); const app = express(); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', index); app.use('/users', users); app.listen(3000, () => console.log('server is run 3000'))
2. 连接mysql 数据库
此处采用连接池的方式进行连接(高效安全)
2.1 创建连接
①:安装 mysql 模块:npm i mysql
②:在项目根目录下配置连接池
首先在项目根目录下创建 util 文件夹,文件夹下创建 bdconfig.js 文件
bdconfig.js
const mysql = require('mysql'); module.exports = { mysqlParams: { host: 'localhost', //域名 port: '3306', //端口 user: 'root', //用户名 password: 'myroot', //密码 database: 'nodeapi' //数据库 }, sySqlConnect(sql, sqlArr) { return new Promise((resolve, reject) => { //用Promise对象将其改造,方便接收数据 const pool = mysql.createPool(this.mysqlParams); pool.getConnection((err, conn) => { if (err) { reject(err) } else { conn.query(sql, sqlArr, (err, data) => { //操作数据库 if (err) { reject(err) } else { resolve(data) } }) conn.release() //释放连接 } }) }) } }
2.2 使用连接
使用时只需要传入 sql(sql语句)、sqlArr(参数),通过Promise 改造后可直接用返回值拿到结果
3. token 身份验证
随着web 的发展,session、cookie 的验证方式弊端也愈发突出,此时token孕育而生,token的强大不仅限于其是无状态的,还在于它可以跨域
3.1 实现步骤
①:首先安装 jsonwebtoken 模块:npm i jsonwebtoken
②:接着在项目中使用该模块
const dbConfig = require('../util/dbconfig'); const jwt = require('jsonwebtoken'); const secret = 'login-rule'; //秘钥规则(自定义) token = async(req, res, next) => { //定义token验证中间件函数(应用于除登录外的每个请求) if (req.headers.authorization) { const token = req.headers.authorization; const { id, username } = jwt.verify(token, secret); // 对token进行解密查找 let sql = 'select * from test where id=?'; let sqlArr = [id]; let result = await dbConfig.sySqlConnect(sql, sqlArr); if (result.length === 0) { res.status(200).send({ msg: '用户错误' }) return } if (username !== result[0].username) { res.status(200).send({ msg: '用户错误' }) } else { next() } } else { res.status(200).send({ msg: '无效请求头' }) } } login = async(req, res) => { //定义登录接口(因为这个请求头不携带token,所以引用在token验证中间件之前) let { username, password } = req.body; let sql = 'select * from test where username=?'; let sqlArr = [username]; let result = await dbConfig.sySqlConnect(sql, sqlArr); if (result.length) { if (password === result[0].password) { const { id, username } = result[0]; //对token进行加密响应个客户端(参数1:传值规则;参数2:加密规则; 参数3:定义时间) const token = jwt.sign({ id, username }, secret, { expiresIn: 60 * 60 }); res.status(200).send({ msg: '登陆成功', token: token, status: 200 }); } else { res.status(200).send({ msg: '登陆失败', status: 422 }); } } else { res.status(200).send({ msg: '用户名不存在', status: 401 }) } } // 验证身份中间件 module.exports = { token, login }
③:在 app.js 中进行配置
// 写在 app.use() 之后,路由之前 app.use('/users/loginjwt', token.login); //登录接口(无需验证token,所以写在token中间件之前) app.use(token.token);
4. 案例实现 token
4.1 原理阐述
为确保身份唯一且有效:用户每次发送登录请求且登录成功,服务器端都会响应给用户一个含用户信息(唯一)的加密 token(字符串),此时用户接收到 token,并把 token 存储在 sessionStorage 或 localStorage中(此处为)。同时用户每次发送其他请求时,在请求头中携带本地的 token,服务器端 token验证中间件拦截请求,对 token进行解密,得到用户信息并与数据库比对,信息存在则放行(身份验证成功)。
4.2 效果预览
4.3 开始实现
写简洁静态页面,且实现ajax 请求
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="../javascripts/jquery.min.js"></script> <title>Document</title> </head> <body> <form id="loginform"> <input type="text" name="username"> <input type="password" name="password"> <input type="submit" value="登录"> </form> <script> $(function() { $('#loginform').on('submit', function() { const formdata = $(this).serialize() $.ajax({ url: '/users/loginjwt', type: 'post', data: formdata, success(res) { if (res.status === 200) { window.sessionStorage.setItem('token', res.token); location.href = '/user/index.html' } } }) return false }) }) </script> </body> </html>
index.html
<script> if (!sessionStorage.getItem('token')) { location.href = '/user/login.html' } </script> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="../javascripts/jquery.min.js"></script> <title>Document</title> </head> <body> <h1>welcome index</h1> <a href="javascript:;" rel="external nofollow" >退出登录</a> <script> $(function() { $.ajaxSetup({ // 发送请求前触发 beforeSend(xhr) { // 在此设置自定义请求头 xhr.setRequestHeader('authorization', sessionStorage.getItem('token')) } }) $.ajax({ url: '/users', success(res) { console.log(res); } }) $('a').on('click', function() { sessionStorage.clear(); location.href = '/user/login.html' }) }) </script> </body> </html>
4.4 注意点
值得注意的是,对于 本地 token 的验证(是否存在)一定要写在页面最顶端(防止页面加载,再次发送用户列表请求)
对于 ajax请求头,一个一个加真的很烦,此处用 $ajaxSetup方法,修改ajax 的默认配置 。配置过后,写在其下边的 ajax 请求,都会带上请求头。
上一篇:node.js使用免费的阿里云ip查询获取ip所在地【推荐】
栏 目:NodeJS
下一篇:Express.JS使用详解
本文地址:http://www.codeinn.net/misctech/186940.html