大多数项目,相比都有注册和登录的功能,本文将记录,如何使用node+mongodb来实现用户的无状态登录。至于什么是无状态,可以查看这篇文章 有状态和无状态登录的概念
接上一篇我们新建的express项目,项目创建请移步上一篇文章,我们已经有了一个基础的项目结构,并且我们已经实现了创建用户的接口。我们使用了bcrypt进行用户密码加密。然后我们登录的时候,进行解密,并验证即可。
使用jwt 实现用户登录并生成token
安装jsonwebtoken依赖
npm i -S jsonwebtoken
在config文件夹下,我们新建一个index.js文件,并配置jwt 加密字符串
// jwttoken加密字符串
const JWT_SECRET = 'token_secret_hahahaha'
module.exports = {
JWT_SECRET
}
然后我们在routes/users.js中
var express = require('express');
var router = express.Router();
const { User } = require('../../model/User')
// 引入 jwt
const jwt = require('jsonwebtoken')
// 引入jwt 加密解密 用的密钥
const { JWT_SECRET } = require('../../../config/index')
// 用户登录
router.post('/login', async (req, res, next) => {
const user = await User.findOne({
username: req.body.username
}).select('password')
// .select('password') 是将密码字段查询出来,由于我们在模型中设置了默认不返回
if (!user) {
return res.status(422).send({
code: 422,
msg: '用户名不存在'
})
}
// bcrypt.compareSync 解密匹配,返回 boolean 值
const isPasswordValid = bcrypt.compareSync(
req.body.password,
user.password
)
if (!isPasswordValid) {
return res.status(422).send({
code:422,
msg: '密码无效'
})
}
/* 生成 token jwt.sign() 接受两个参数,一个是传入的对象,一个是自定义的密钥 */
const token = jwt.sign({ id: String(user._id) }, JWT_SECRET)
res.send({
code: 200,
msg: '登录成功',
data:{ user_id: user._id, token }
})});
于是我们就有了我们用户的token,此时token中加密保存的仅仅是用户的_id信息
然后我们在请求其他接口的时候,需要验证用户信息的话,就在header中加上我们的token信息,再接口中校验token,如果通过,就进行下一步
校验token
我们校验用户token一般会写一个中间件函数,然后再需要验证的接口中,加上这个函数即可
在middleware/checkToken.js
// 引入User模型
const { User } = require('../model/User')
// 引入 jwt
const jwt = require('jsonwebtoken')
// 解析 token 用的密钥
const { JWT_SECRET } = require('../../config/index')
// 验证token
let checkToken = async (req, res, next)=>{
if(!req.headers.authorization) {
res.send({
code: 401,
msg: '尚未登录!'
})
return
}
const token = String(req.headers.authorization.split(' ').pop())
if(!token){
res.send({
code: 401,
msg: 'token验证失败'
})
}
// 解密 token 获取对应的 id
jwt.verify(token, JWT_SECRET, (err,decode)=>{
if(err) {
res.send({
code: 401,
msg: err
})
return
}
let { id } = decode
if(!id) {
res.send({
code: 401,
msg: '当前用户信息不存在'
})
}else {
req.user_id = id
next()
}
// console.log(decode);
})
}
module.exports = checkToken
然后再routes/users.js路由文件中
比如我们要在获取用户信息之前,先验证用户token,判断用户是否登录,
var express = require('express');
var router = express.Router();
const { User } = require('../../model/User')
// 引入校验中间件
const checkToken = require('../../middleware/checkToken')
// 获取用户信息 假如checkToken校验token
router.get('/info',checkToken, async (req, res, next) => {
const user = await User.findOne({
_id: req.user_id
})
res.send({
code: 200,
data: user
})
})
module.exports = router;
测试接口
在.http文件中
// test.http
@url=http://localhost:3000
### 用户登录 拿到token
POST {{url}}/users/login
Content-Type: application/json
{ "username": "user3", "password": "123456"}
### 获取用户信息 使用登录后拿到的token进行验证
GET {{url}}/users/info
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVmZjA3M2I5YjkyMTYwMjM5Y2JlMTQzZiIsImlhdCI6MTYwOTU5NDExOX0.iSXibZxCFKuFI_jJ9xq3-dffe4zPCnVRgNH7ZlrQhvc