- 上篇對(duì)客戶端的值進(jìn)行了增刪改查,但并沒有進(jìn)行數(shù)據(jù)安全性問題處理與身份驗(yàn)證
1 . 登錄后返回token字段,并對(duì)其他接口進(jìn)行身份驗(yàn)證
1.1導(dǎo)入校驗(yàn)包定義密匙與token有效時(shí)間
- 下載
express-jwt與jsonwebtoken兩個(gè)包
// 一定要在接口之前配置解析 Token 的中間件
const expressJWT = require('express-jwt')
// 導(dǎo)入生成 Token 的包
const jwt = require('jsonwebtoken')
1.2 定義token密匙/不加權(quán)限的接口
// unless 定義的都是不加權(quán)限的接口 會(huì)忽略開頭為api 或者 user的接口 除此兩種都需在請(qǐng)求頭攜帶token
app.use(
expressJWT({ secret: '服務(wù)端私有密匙(自己定義)', algorithms: ['HS256'] }).unless({
path: [/^\/api||user/],
})
)
1.3 定義身份驗(yàn)證錯(cuò)誤級(jí)別的中間件
// 定義錯(cuò)誤級(jí)別的中間件
app.use((err, req, res, next) => {
// 身份認(rèn)證失敗后的錯(cuò)誤
if (err.name === 'UnauthorizedError') return res.cc('身份認(rèn)證失??!')
// 未知的錯(cuò)誤
res.cc(err)
})
1.4 改造登錄接口返回token標(biāo)識(shí)
// 登錄接口
app.post('/api', (req, res) => {
const body = req.body
// 查詢登錄用戶名/密碼數(shù)據(jù)庫是否存在,并匹配
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('服務(wù)端錯(cuò)誤')
if (!result.length) return res.cc('用戶名/密碼錯(cuò)誤')
const user = { ...result[0], password: '', user_pic: '' }
// 對(duì)用戶的信息進(jìn)行加密,生成 Token 字符串
// expiresIn代表token有效時(shí)間
const tokenStr = jwt.sign(user, '服務(wù)端私有密匙(自己定義)', {
expiresIn: '10h',
})
res.send({
status: '200',
data: 'ok',
message: '登錄成功',
token: 'Bearer ' + tokenStr,
})
})
})
2 . 對(duì)用戶數(shù)據(jù)進(jìn)行加密儲(chǔ)存
- 此處用的是
bcrypt不可逆,如需可破解的請(qǐng)用md5進(jìn)行加密
- 下載
bcryptjs包
// 導(dǎo)入 bcryptjs 這個(gè)包
const bcrypt = require('bcryptjs')
// 注冊(cè)接口
app.post('/user', (req, res) => {
const body = req.body
// 查詢數(shù)據(jù)庫 *(所有) 表單為 ev_users 里 所有username ? 為占位符代表?xiàng)l件
const query = 'select * from ev_users where username=?'
// () 第一位為sql
sql.query(query, body.username, (err, result) => {
// err 為執(zhí)行錯(cuò)誤信息 result為數(shù)據(jù)
if (err) return res.cc(err)
// 判斷影響行數(shù) 能查詢到證明數(shù)據(jù)庫表單里存在這個(gè)值
if (result.length > 0) return res.cc('用戶名已存在')
// 調(diào)用 bcrypt.hashSync() 對(duì)密碼進(jìn)行加密 10代表鹽的長(zhǎng)度
body.password = bcrypt.hashSync(body.password, 10)
// 插入數(shù)據(jù)
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('注冊(cè)失敗')
res.send({
status: 200,
data: 'ok',
message: '注冊(cè)成功',
})
}
)
})
})
// 登錄接口進(jìn)行了修改 注釋掉的為以前未進(jìn)行加密時(shí)的處理方式
// 登錄接口
app.post('/api', (req, res) => {
const body = req.body
// 查詢登錄用戶名/密碼數(shù)據(jù)庫是否存在,并匹配
// 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('服務(wù)端錯(cuò)誤')
// if (!result.length) return res.cc('用戶名/密碼錯(cuò)誤')
// 調(diào)用bcrypt.compareSync判斷輸入密碼與數(shù)據(jù)庫加密密碼是否一致
const compareResult = bcrypt.compareSync(body.password, result[0].password)
if (!compareResult) return res.cc('登錄失?。?)
const user = { ...result[0], password: '', user_pic: '' }
// 對(duì)用戶的信息進(jìn)行加密,生成 Token 字符串
const tokenStr = jwt.sign(user, config.jwtSecretKey, {
expiresIn: config.expiresIn,
})
res.send({
status: '200',
data: 'ok',
message: '登錄成功',
token: 'Bearer ' + tokenStr,
})
})
})
3 . 最后貼出改造后的完整代碼,只貼注冊(cè)與登錄
const express = require('express')
const cors = require('cors')
// 導(dǎo)入 mysql 模塊
const mysql = require('mysql')
const axios = require('axios')
const app = express()
// 導(dǎo)入生成 Token 的包
const jwt = require('jsonwebtoken')
// 解析token
const expressJWT = require('express-jwt')
// 導(dǎo)入 bcryptjs 這個(gè)包
const bcrypt = require('bcryptjs')
// 建立與 MySQL 數(shù)據(jù)庫的連接關(guān)系
const sql = mysql.createPool({
host: '127.0.0.1', // 數(shù)據(jù)庫的 IP 地址
user: 'root', // 登錄數(shù)據(jù)庫的賬號(hào)
password: 'root', // 登錄數(shù)據(jù)庫的密碼
database: 'my_users', // 指定要操作哪個(gè)數(shù)據(jù)庫
})
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: '服務(wù)端私有密匙(自己定義)', algorithms: ['HS256'] }).unless({
path: [/^\/api||user/],
})
)
// 錯(cuò)誤級(jí)別中間件
app.use((req, res, next) => {
res.cc = function (err, status = 201) {
res.send({
status,
data: '操作失敗',
message: err,
})
}
next()
})
// 定義錯(cuò)誤級(jí)別的中間件
app.use((err, req, res, next) => {
console.log(err)
// 身份認(rèn)證失敗后的錯(cuò)誤
if (err.name === 'credentials_required') return res.cc('身份認(rèn)證失??!')
// // 未知的錯(cuò)誤
return res.send(err)
})
app.get('/get', (req, res) => {
const body = req.query
res.send({
data: body,
})
})
// 注冊(cè)接口
app.post('/user', (req, res) => {
const body = req.body
// 查詢數(shù)據(jù)庫 *(所有) 表單為 ev_users 里 所有username ? 為占位符代表?xiàng)l件
const query = 'select * from ev_users where username=?'
// () 第一位為sql
sql.query(query, body.username, (err, result) => {
// err 為執(zhí)行錯(cuò)誤信息 result為數(shù)據(jù)
if (err) return res.cc(err)
// 判斷影響行數(shù) 能查詢到證明數(shù)據(jù)庫表單里存在這個(gè)值
if (result.length > 0) return res.cc('用戶名已存在')
// 調(diào)用 bcrypt.hashSync() 對(duì)密碼進(jìn)行加密 10代表鹽的長(zhǎng)度
body.password = bcrypt.hashSync(body.password, 10)
// 插入數(shù)據(jù)
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('注冊(cè)失敗')
res.send({
status: 200,
data: 'ok',
message: '注冊(cè)成功',
})
}
)
})
})
// 登錄接口
app.post('/api', (req, res) => {
const body = req.body
// 查詢登錄用戶名/密碼數(shù)據(jù)庫是否存在,并匹配
// 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('服務(wù)端錯(cuò)誤')
// if (!result.length) return res.cc('用戶名/密碼錯(cuò)誤')
// 判斷密碼是否正確
const compareResult = bcrypt.compareSync(body.password, result[0].password)
if (!compareResult) return res.cc('登錄失??!')
const user = { ...result[0], password: '', user_pic: '' }
// 對(duì)用戶的信息進(jìn)行加密,生成 Token 字符串
const tokenStr = jwt.sign(user, '服務(wù)端私有密匙(自己定義)', {
expiresIn: '10h',
})
res.send({
status: '200',
data: 'ok',
message: '登錄成功',
token: 'Bearer ' + tokenStr,
})
})
})
// 啟動(dòng)服務(wù)器
app.listen(80, () => {
console.log('express server running at http://127.0.0.1')
})