思路:用户调用后端登录接口,后端获取到正确的用户名密码后,给response的header设置Authorization属性值,值为通过jwt生成的token值,前端登录成功后,拿到token并存入storage中,在后续请求的时候,在请求头的authorization带上token信息,如果后端在拿到请求提的时候获取不到token,会自动重定向到login页面
项目地址:GitHub - Rose-chen/express-jwt: nodejs框架express使用jwt控制权限
- 创建数据库连接
安装使用mongoose模块
npm i mongoose -S
根目录下创建config文件夹,新建文件db.config.js
创建连接
const mongoose = require('mongoose')
//连接数据库
mongoose.connect('mongodb://127.0.0.1:27017/test-node')
//插入集合和数据,数据库test-node会自动创建
//监听数据库连接状态
mongoose.connection.once('open',()=>{
console.log('数据库连接成功……')
})
mongoose.connection.once('close',()=>{
console.log('数据库断开……')
})
在bin文件夹的www文件中引入
require('./../config/db.config')
- 创建数据模型
根目录下创建models文件夹
新建文件UserModel.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const UserType = new Schema({
username: String,
password: String,
age: Number
})
//数据字段可根据实际需求修改
const UserModel = mongoose.model('users', UserType)
//此处users名称需要跟数据库中对应集合名称一样
module.exports = UserModel
- 后端处理路由
用户登录接口,使用mongoose的find方法匹配用户
router.post('/login', async function(req, res, next) {
// console.log(req)
const { username, password } = req.body;
const resp = await UserModel.find({ username, password})
// 如果查询到数据,resp为对象数组
if(resp.length) {
res.send({
ok: 1
})
} else {
res.send({
ok: 0
})
}
})
用户登录成功后,给req的headers设置Authorization属性
JWT.js代码
const jwt = require('jsonwebtoken')
const secret = 'chen-anydata'
const JWT = {
generator(data, expries) {
return jwt.sign(data, secret, {expiresIn: expries})
},
verify(data) {
return jwt.verify(data, secret)
}
}
module.exports = JWT
/api/login接口新增JWT相关代码
router.post('/login', async function(req, res, next) {
// console.log(req)
const { username, password } = req.body;
const resp = await UserModel.find({ username, password})
// 如果查询到数据,resp为对象数组
if(resp.length) {
const token = JWT.generator({
id: resp[0]._id,
username: resp[0].username,
}, 30)
res.header('Authorization', token)
res.send({
ok: 1
})
} else {
res.send({
ok: 0
})
}
})
前端登录界面/login调用登录
//axios封装
axios.interceptors.request.use(function (config) {
return config;
}, function (error) {
return Promise.reject(error);
});
axios.interceptors.response.use(function (response) {
// console.log(response.headers)
const { authorization } = response.headers
authorization && localStorage.setItem("token", authorization)
return response.data;
}, function (error) {
return Promise.reject(error);
});
//登录请求
axios.post("/api/login", {
username: username.value,
password: password.value,
}
).then(res => {
console.log(res)
if (res.ok === 1) {
alert('登录成功')
location.href= "/"
} else {
alert("用户名密码不匹配")
}
})
前端首页/index
//拿到storage里的token,并在请求的headers中设置Authorization
axios.interceptors.request.use(function (config) {
const token = localStorage.getItem('token');
config.headers.Authorization = token;
return config;
}, function (error) {
return Promise.reject(error);
});
axios.interceptors.response.use(function (response) {
const { authorization } = response.headers
authorization && localStorage.setItem("token", authorization)
return response.data;
}, function (error) {
if(error.response.status===401){
localStorage.removeItem("token")
location.href = "/login"
}
return Promise.reject(error);
});
//获取用户列表
axios.get("/api/user?page=1&limit=10").then(res=>{
if(res.ok===0){
location.href="/login"
}
var tbody = document.querySelector("tbody")
tbody.innerHTML = res.map(item=>`
<tr>
<td>${item._id}</td>
<td>${item.username}</td>
<td>${item.age}</td>
</tr>
`).join("")
})
对应/api/user接口后端代码
router.get('/user', async function(req, res, next) {
const { page, limit } = req.query;
const resp = await UserModel.find({}, ['username', 'age']).sort({age: -1}).skip((page-1)*limit).limit(page*limit)
res.send(resp)
});
app.js中,使用中间件,检验token
//设置中间件,校验token
app.use((req, res, next) => {
//排除不需要校验的路由
if(req.path.includes('/login') || !req.path.includes('/api')) {
next()
return
}
const token = req.headers['authorization']
if(token) {
try {
const payload = JWT.verify(token)
console.log('payload',payload)
if(payload) {
const newtoken = JWT.generator({
id: payload._id,
username: payload.username,
}, 30)
res.header('Authorization', newtoken)
next()
} else {
res.status(401).send({errCode: -1, errMsg: 'token过期'})
}
} catch {
res.status(401).send({errCode: -1, errMsg: 'token过期'})
}
} else {
res.status(401).send({errCode: -1, errMsg: 'token过期'})
}
})