一.基于Nodejs+mysql+html实现登录注册功能模块实现思路
1. 环境搭建
- 安装 Node.js: 确保你的开发环境中已经安装了 Node.js。
- 创建项目目录: 创建一个新的项目目录,并在该目录中初始化一个新的 Node.js 项目。
- 安装依赖: 使用 npm 安装必要的依赖包,如 Express(一个流行的 Node.js Web 应用框架)、mysql(数据库)、bcrypt(密码加密库)等。
2. 设计数据库模型
- 选择数据库: 你可以选择任何支持 Node.js 的数据库,比如 mysql。
- 设计用户模型: 用户模型至少应该包括用户名、密码等字段。
3. 设置服务器
- 安装 Express: 使用 npm 安装 Express。
- 创建服务器: 使用 Express 创建一个基本的 HTTP 服务器。
4. 创建登录和注册页面
- HTML 页面: 创建登录和注册的 HTML 表单页面。
- 路由: 使用 Express 设置路由来处理登录和注册的请求。
5. 处理表单提交
- 密码加密: 使用 bcrypt 对用户密码进行哈希处理,以增强安全性。
- 存储用户: 将经过验证的用户数据存储到数据库中。
- 处理登录: 检查用户名和密码是否匹配数据库中的记录。
二.后端具体实现步骤
1.初始化项目
1.1.创建项目
- 新建
node_api
文件夹作为项目根目录,并在项目根目录中打开cmd或PowerShell运行如下的命令,初始化包管理配置文件:npm init -y
- 运行如下的命令,安装 express框架:
npm i express
- vscode打开项目,在项目根目录中新建 app.js 作为整个项目的入口文件,并初始化如下的代码:
// 导入express const express = require('express') // 创建express实例 const app = express() // 启动服务器 app.listen(3000, () => { console.log('server running at http://localhost:3000') });
1.2.配置cors跨域
- 运行如下的命令,安装cros中间件:
npm i cors
- 在
app.js
中导入并配置cors
中间件://注册跨域中间件 const cors = require('cors') app.use(cors())
1.3. 配置解析表单数据的中间件
- 配置解析
application/x-www-form-urlencoded
格式的表单数据的中间件:extended: false
表示使用简化的 URL 编码解析器,它只能解析键值对,并且每个键只能有一个值。这意味着如果表单中有重复的字段名,只有最后一个会被解析并保存。如果extended: true
,那么会使用一个更为灵活的解析器,它可以解析嵌套的对象(例如,a[b][c]=d
这样的键),并且可以处理重复的字段名,将它们作为一个数组来保存。 -
// 注册解析表单数据的中间件 app.use(express.urlencoded({extended:false}))
1.4. 创建路由相关的文件夹
- 在项目根目录下,新建
router
文件夹,用来存放所有的路由模块,路由模块中,只存放客户端的请求与处理函数之间的映射关系 - 在项目根目录中,新建
router_handler
文件夹,用来存放所有的路由处理函数模块,路由处理函数模块中,专门负责存放每个路由对应的处理函数
1.5.初始化路由
- 在 router 文件夹中,新建 user.js 文件,作为用户的路由模块
// 引入express const express = require('express'); // 创建用户模块 const router = express.Router(); // 注册 router.post('/register', (req,res)=>{ res.send('注册成功') }); // 登录 router.post('/login', (req,res)=>{ res.send('登录成功') }); // 导出路由 module.exports = router;
- 在app.js内导入并使用用户路由模块
// 导入用户路由模块 const routeruser = require('./router/use') app.use('/api',routeruser)
1.7. 将路由模块中的处理函数抽离出来
- 在
router_handler
文件夹中,新建 userhandle.js 文件,作为用户的路由处理函数模块/* * 在这里定义和用户相关的路由处理函数,供 /router/user.js 模块进行调用 */ // 用户注册处理函数 exports.register = (req, res) => { res.send('注册成功') } // 用户登录处理函数 exports.login = (req, res) => { res.send('登录成功') }
在
/router/user.js 中/router_handler/
userhandle.js
,并调用对应的处理函数// 引入express const express = require('express'); // 创建用户模块 const router = express.Router(); // 导入用户处理函数 const routerhandle = require('../routerhandle/userhandle'); // 注册 router.post('/register', routerhandle.register); // 登录 router.post('/login', routerhandle.login); // 导出路由 module.exports = router;
2.具体实现登录注册代码
2.1.新建user数据表
2.2. 安装并配置mysql
- 运行如下命令,安装
mysql
模块:npm i mysql
- 在项目根目录中新建
/db/index.js
文件,并创建数据库的连接对象:
2.3. 安装并导入用户密码加密依赖
- 运行如下命令,安装 bcryptjs 依赖:
npm i bcryptjs@2.4.3
2.4. 注册模块处理函数完整代码
// 引入数据库
const mysql = require('../db/mysql')
//导入bcryptjs加密库
const bcrypt = require('bcryptjs')
// 注册
exports.register = (req, res) => {
// 获取客户端提交到服务器的用户信息
const userinfo = req.body;
// 判断用户名密码是否为空
if (!userinfo.username || !userinfo.password) {
return res.send({
code: 1,
msg: '用户名或密码不能为空'
})
}
// 查询用户名是否被占用
const registermysql = "select * from node_use where username = ?"
mysql.query(registermysql, userinfo.username, (err, result) => {
if (err) {
return res.send({
code: 1,
msg: err.message
})
}
if (result.length > 0) {
return res.send({
code: 1,
msg: '用户名已存在'
})
}
// 对密码进行加密
userinfo.password = bcrypt.hashSync(userinfo.password, 10)
// 插入数据
const regmysql = "insert into node_use set ?"
// 执行sql语句
mysql.query(regmysql, { username: userinfo.username, password: userinfo.password }, (err, result) => {
if (err) {
res.send({
code: 1,
mes: err.message
})
}
res.send({
code: 0,
msg: '注册成功'
})
})
})
}
2.5. 安装生成 Token 字符串的包
- 运行如下命令,安装生成 Token 字符串的包
npm i jsonwebtoken
2.6. 登录模块处理函数完整代码
// 引入数据库
const mysql = require('../db/mysql')
//导入bcryptjs加密库
const bcrypt = require('bcryptjs')
// 导入jwt
const jwt = require('jsonwebtoken')
exports.login = (req, res) => {
// 获取客户端提交到服务器的用户信息
const logininfo = req.body;
// 判断用户名密码是否为空
if (!logininfo.username || !logininfo.password) {
return res.send({
code: 1,
msg: '用户名或密码不能为空'
})
}
// 查询用户名是否被占用
const loginmysql = "select * from node_use where username = ?"
// 执行sql语句
mysql.query(loginmysql, logininfo.username, (err, result) => {
if (err) {
return res.send({
code: 1,
msg: err.message
})
}
if (result.length !== 1) {
return res.send({
code: 1,
msg: '用户名不存在'
})
}
// 判断密码是否正确
const compareResult = bcrypt.compareSync(logininfo.password, result[0].password)
if (!compareResult) {
return res.send({
code: 1,
msg: '密码错误'
})
}
// 剔除user中的password属性
const user = { ...result[0], password: '' }
// 生成token
const tokenStr = jwt.sign(user, 'safsaf')
res.send({
code: 0,
msg: '登录成功',
token: tokenStr
})
}
)
}
2.7.配置解析token的中间件
- 运行如下的命令,安装解析 Token的中间件
// 注意这里必须加版本 npm i express-jwt@5.3.3
- 在 app.js 中注册路由之前,配置解析 Token 的中间件
// 导入express-jwt const jwt = require('express-jwt') // 解析token的中间件 app.use(jwt({secret:'safsaf'}).unless({path:['/api/login','/api/register']})) // unless({path:['/api/login','/api/register']})) // 除了登录和注册之外的接口都需要验证身份 // 必须写在导入路由模块之前
- 在 app.js 中的 误级别中间件 里面,捕获并处理 Token 认证失败后的错误
// 捕获身份认证失败的中间件 app.use(function (err, req, res, next) { // 捕获身份认证失败的中间件 if (err.name === "UnauthorizedError") { return res.send({ code:1, msg:'身份认证失败' }); } }); // 必须写在导入路由模块之前
三.前端具体实现步骤
1.新建 html
文件夹作为项目根目录,并在项目根目录下创建login.html与register.html
注意
2.注册具体实现代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>注册</title>
<style>
.login {
width: 50%;
height: 400px;
margin: 150px auto;
background-color: rgb(212, 205, 205);
}
.name {
width: 60%;
height: 80px;
margin: 10px auto;
line-height: 80px;
}
.but {
width: 25%;
height: 50px;
margin: 10px auto;
line-height: 50px;
}
button {
width: 80px;
height: 40px;
margin: 10px auto;
background-color: rgb(102, 177, 255);
border: none;
}
</style>
</head>
<body>
<div class="login">
<h1 style="text-align: center;padding-top: 30px;">欢迎注册</h1>
<div class="name" style="margin-top: 60px;">
<span style="margin-left: 10px;font-weight: 600;font-size: 20px;">用户名:</span>
<input type="text" style="width: 70%;height: 50px;font-size: 18px;" id="username">
</div>
<div class="name">
<span style="margin-left: 10px;font-weight: 600;font-size: 20px;">密 码:</span>
<input type="password" style="width: 70%;height: 50px;font-size: 18px;" id="password">
</div>
<div class="but">
<button style="float: left;" id="log">注册</button>
<button style="float: right;" id="back">取消</button>
</div>
</div>
</div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
// 注册按钮事件
document.querySelector('#log').addEventListener('click', async () => {
// 创建 XMLHttpRequest 对象
var xhr = new XMLHttpRequest();
// 请求方式以及url
xhr.open('POST', 'http://localhost:3000/rest/register', true);
// 将默认json格式转换为application/x-www-form-urlencoded格式
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
const params = new URLSearchParams();
params.append("username", username.value);
params.append("password", password.value);
// 发送请求并携带参数
xhr.send(params);
// 请求监听
xhr.addEventListener('loadend', () => {
const data = JSON.parse(xhr.response)
if (data.code === 0) {
alert(data.msg)
window.location.href = 'login.html'
} else {
alert(data.msg)
}
})
})
// 取消按钮事件
document.querySelector('#back').addEventListener('click', () => {
window.location.href = 'login.html'
})
</script>
</body>
</html>
3.登录具体实现代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录</title>
<style>
.login {
width: 50%;
height: 400px;
margin: 150px auto;
background-color: rgb(212, 205, 205);
}
.name {
width: 60%;
height: 80px;
margin: 10px auto;
line-height: 80px;
}
.but {
width: 25%;
height: 50px;
margin: 10px auto;
line-height: 50px;
}
button {
width: 80px;
height: 40px;
margin: 10px auto;
background-color: rgb(102, 177, 255);
border: none;
}
</style>
</head>
<body>
<div class="login">
<h1 style="text-align: center;padding-top: 30px;">欢迎登录</h1>
<div class="name" style="margin-top: 60px;">
<span style="margin-left: 10px;font-weight: 600;font-size: 20px;">用户名:</span>
<input type="text" style="width: 70%;height: 50px;font-size: 18px;" id="username">
</div>
<div class="name">
<span style="margin-left: 10px;font-weight: 600;font-size: 20px;">密 码:</span>
<input type="password" style="width: 70%;height: 50px;font-size: 18px;" id="password">
</div>
<div class="but">
<button style="float: left;" id="log">登录</button>
<button style="float: right;" id="reg">注册</button>
</div>
</div>
</div>
<script>
// 登录按钮事件
document.querySelector('#log').addEventListener('click', async () => {
// 创建 XMLHttpRequest 对象
var xhr = new XMLHttpRequest();
// 请求方式以及url
xhr.open('POST', 'http://localhost:3000/rest/login', true);
// 设置请求头
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// 将默认json格式转换为application/x-www-form-urlencoded格式
const params = new URLSearchParams();
params.append("username", username.value);
params.append("password", password.value);
// 发送请求并携带参数
xhr.send(params);
// 请求监听
xhr.addEventListener('loadend', () => {
const data = JSON.parse(xhr.response)
if (data.code === 0) {
alert(data.msg)
window.location.href = 'index.html'
} else {
alert(data.msg)
}
})
})
// 注册按钮事件
document.querySelector('#reg').addEventListener('click', () => {
window.location.href = 'register.html'
})
</script>
</body>
</html>
!!!前端发送网络请求部分使用的是原生Ajax,也可使用三方库axios