- 上篇对客户端的值进行了增删改查,但并没有进行数据安全性问题处理与身份验证
1 . 登录后返回token字段,并对其他接口进行身份验证
1.1导入校验包定义密匙与token有效时间
- 下载
express-jwt
与jsonwebtoken
两个包
// 一定要在接口之前配置解析 Token 的中间件
const expressJWT = require('express-jwt')
// 导入生成 Token 的包
const jwt = require('jsonwebtoken')
1.2 定义token密匙/不加权限的接口
// unless 定义的都是不加权限的接口 会忽略开头为api 或者 user的接口 除此两种都需在请求头携带token
app.use(
expressJWT({ secret: '服务端私有密匙(自己定义)', algorithms: ['HS256'] }).unless({
path: [/^\/api||user/],
})
)
1.3 定义身份验证错误级别的中间件
// 定义错误级别的中间件
app.use((err, req, res, next) => {
// 身份认证失败后的错误
if (err.name === 'UnauthorizedError') return res.cc('身份认证失败!')
// 未知的错误
res.cc(err)
})
1.4 改造登录接口返回token标识
// 登录接口
app.post('/api', (req, res) => {
const body = req.body
// 查询登录用户名/密码数据库是否存在,并匹配
let sqlapi = 'select * from ev_users where username = ? && password = ?'
sql.query(sqlapi, [body.username, body.password], (err, result) => {
console.log(result)
if (err) return res.cc('服务端错误')
if (!result.length) return res.cc('用户名/密码错误')
const user = { ...result[0], password: '', user_pic: '' }
// 对用户的信息进行加密,生成 Token 字符串
// expiresIn代表token有效时间
const tokenStr = jwt.sign(user, '服务端私有密匙(自己定义)', {
expiresIn: '10h',
})
res.send({
status: '200',
data: 'ok',
message: '登录成功',
token: 'Bearer ' + tokenStr,
})
})
})
2 . 对用户数据进行加密储存
- 此处用的是
bcrypt
不可逆,如需可破解的请用md5
进行加密
- 下载
bcryptjs
包
// 导入 bcryptjs 这个包
const bcrypt = require('bcryptjs')
// 注册接口
app.post('/user', (req, res) => {
const body = req.body
// 查询数据库 *(所有) 表单为 ev_users 里 所有username ? 为占位符代表条件
const query = 'select * from ev_users where username=?'
// () 第一位为sql
sql.query(query, body.username, (err, result) => {
// err 为执行错误信息 result为数据
if (err) return res.cc(err)
// 判断影响行数 能查询到证明数据库表单里存在这个值
if (result.length > 0) return res.cc('用户名已存在')
// 调用 bcrypt.hashSync() 对密码进行加密 10代表盐的长度
body.password = bcrypt.hashSync(body.password, 10)
// 插入数据
const sqlint = 'insert into ev_users set ?'
sql.query(
sqlint,
{ username: body.username, password: body.password },
(err, result) => {
if (err) return res.cc(err)
if(!result.affectedRows) return res.cc('注册失败')
res.send({
status: 200,
data: 'ok',
message: '注册成功',
})
}
)
})
})
// 登录接口进行了修改 注释掉的为以前未进行加密时的处理方式
// 登录接口
app.post('/api', (req, res) => {
const body = req.body
// 查询登录用户名/密码数据库是否存在,并匹配
// let sqlapi = 'select * from ev_users where username = ? && password = ?'
let sqlapi = 'select * from ev_users where username = ?'
sql.query(sqlapi, [body.username, body.password], (err, result) => {
if (err) return res.cc('服务端错误')
// if (!result.length) return res.cc('用户名/密码错误')
// 调用bcrypt.compareSync判断输入密码与数据库加密密码是否一致
const compareResult = bcrypt.compareSync(body.password, result[0].password)
if (!compareResult) return res.cc('登录失败!')
const user = { ...result[0], password: '', user_pic: '' }
// 对用户的信息进行加密,生成 Token 字符串
const tokenStr = jwt.sign(user, config.jwtSecretKey, {
expiresIn: config.expiresIn,
})
res.send({
status: '200',
data: 'ok',
message: '登录成功',
token: 'Bearer ' + tokenStr,
})
})
})
3 . 最后贴出改造后的完整代码,只贴注册与登录
const express = require('express')
const cors = require('cors')
// 导入 mysql 模块
const mysql = require('mysql')
const axios = require('axios')
const app = express()
// 导入生成 Token 的包
const jwt = require('jsonwebtoken')
// 解析token
const expressJWT = require('express-jwt')
// 导入 bcryptjs 这个包
const bcrypt = require('bcryptjs')
// 建立与 MySQL 数据库的连接关系
const sql = mysql.createPool({
host: '127.0.0.1', // 数据库的 IP 地址
user: 'root', // 登录数据库的账号
password: 'root', // 登录数据库的密码
database: 'my_users', // 指定要操作哪个数据库
})
axios.baserUrl = 'http://ihrm-java.itheima.net/'
// 处理跨域中间件
app.use(cors())
// 处理JSON表单格式中间件
app.use(express.json())
// 处理application/x-www-form-urlencoded表单格式的中间件
app.use(express.urlencoded({ extended: false }))
app.use(
expressJWT({ secret: '服务端私有密匙(自己定义)', algorithms: ['HS256'] }).unless({
path: [/^\/api||user/],
})
)
// 错误级别中间件
app.use((req, res, next) => {
res.cc = function (err, status = 201) {
res.send({
status,
data: '操作失败',
message: err,
})
}
next()
})
// 定义错误级别的中间件
app.use((err, req, res, next) => {
console.log(err)
// 身份认证失败后的错误
if (err.name === 'credentials_required') return res.cc('身份认证失败!')
// // 未知的错误
return res.send(err)
})
app.get('/get', (req, res) => {
const body = req.query
res.send({
data: body,
})
})
// 注册接口
app.post('/user', (req, res) => {
const body = req.body
// 查询数据库 *(所有) 表单为 ev_users 里 所有username ? 为占位符代表条件
const query = 'select * from ev_users where username=?'
// () 第一位为sql
sql.query(query, body.username, (err, result) => {
// err 为执行错误信息 result为数据
if (err) return res.cc(err)
// 判断影响行数 能查询到证明数据库表单里存在这个值
if (result.length > 0) return res.cc('用户名已存在')
// 调用 bcrypt.hashSync() 对密码进行加密 10代表盐的长度
body.password = bcrypt.hashSync(body.password, 10)
// 插入数据
const sqlint = 'insert into ev_users set ?'
sql.query(
sqlint,
{ username: body.username, password: body.password },
(err, result) => {
if (err) return res.cc(err)
if (!result.affectedRows) return res.cc('注册失败')
res.send({
status: 200,
data: 'ok',
message: '注册成功',
})
}
)
})
})
// 登录接口
app.post('/api', (req, res) => {
const body = req.body
// 查询登录用户名/密码数据库是否存在,并匹配
// let sqlapi = 'select * from ev_users where username = ? && password = ?'
let sqlapi = 'select * from ev_users where username = ?'
sql.query(sqlapi, [body.username, body.password], (err, result) => {
if (err) return res.cc('服务端错误')
// if (!result.length) return res.cc('用户名/密码错误')
// 判断密码是否正确
const compareResult = bcrypt.compareSync(body.password, result[0].password)
if (!compareResult) return res.cc('登录失败!')
const user = { ...result[0], password: '', user_pic: '' }
// 对用户的信息进行加密,生成 Token 字符串
const tokenStr = jwt.sign(user, '服务端私有密匙(自己定义)', {
expiresIn: '10h',
})
res.send({
status: '200',
data: 'ok',
message: '登录成功',
token: 'Bearer ' + tokenStr,
})
})
})
// 启动服务器
app.listen(80, () => {
console.log('express server running at http://127.0.0.1')
})